From 995a0524eda56eb3c9cc9568de1cbe5d3f29b23b Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Tue, 28 Apr 2026 10:11:47 +0000 Subject: [PATCH 001/338] Add reactions support in the pinned messages list --- .../pinned/list/PinnedMessagesListEvent.kt | 2 + .../list/PinnedMessagesListPresenter.kt | 32 +++++++++++++--- .../pinned/list/PinnedMessagesListState.kt | 4 ++ .../list/PinnedMessagesListStateProvider.kt | 8 ++++ .../pinned/list/PinnedMessagesListView.kt | 37 ++++++++++++++++--- 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListEvent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListEvent.kt index 73ba08b29d..9be11d244a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListEvent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListEvent.kt @@ -11,8 +11,10 @@ package io.element.android.features.messages.impl.pinned.list import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.matrix.api.core.ThreadId +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId sealed interface PinnedMessagesListEvent { data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : PinnedMessagesListEvent data class OpenThread(val threadRootId: ThreadId) : PinnedMessagesListEvent + data class ToggleReaction(val emoji: String, val eventOrTransactionId: EventOrTransactionId) : PinnedMessagesListEvent } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt index 6cd037484d..fd19656fdc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt @@ -29,6 +29,8 @@ import io.element.android.features.messages.impl.actionlist.model.TimelineItemAc import io.element.android.features.messages.impl.link.LinkState import io.element.android.features.messages.impl.pinned.DefaultPinnedEventsTimelineProvider import io.element.android.features.messages.impl.timeline.TimelineRoomInfo +import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState +import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig import io.element.android.features.messages.impl.timeline.model.TimelineItem @@ -47,6 +49,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState import io.element.android.libraries.matrix.api.room.roomMembers +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction @@ -71,6 +74,8 @@ class PinnedMessagesListPresenter( private val linkPresenter: Presenter, private val snackbarDispatcher: SnackbarDispatcher, @Assisted private val actionListPresenter: Presenter, + private val customReactionPresenter: Presenter, + private val reactionSummaryPresenter: Presenter, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, private val analyticsService: AnalyticsService, @@ -88,7 +93,7 @@ class PinnedMessagesListPresenter( private val timelineItemsFactory: TimelineItemsFactory = timelineItemsFactoryCreator.create( config = TimelineItemsFactoryConfig( computeReadReceipts = false, - computeReactions = false, + computeReactions = true, ) ) @@ -96,6 +101,9 @@ class PinnedMessagesListPresenter( override fun present(): PinnedMessagesListState { htmlConverterProvider.Update() val roomInfo by room.roomInfoFlow.collectAsState() + val userEventPermissions by room.permissionsAsState(UserEventPermissions.DEFAULT) { perms -> + perms.userEventPermissions() + } val timelineRoomInfo by remember { derivedStateOf { TimelineRoomInfo( @@ -103,7 +111,7 @@ class PinnedMessagesListPresenter( name = roomInfo.name, // We don't need to compute those values userHasPermissionToSendMessage = false, - userHasPermissionToSendReaction = false, + userHasPermissionToSendReaction = userEventPermissions.canSendReaction, // We do not care about the call state here. roomCallState = aStandByCallState(), // don't compute this value or the pin icon will be shown @@ -119,9 +127,8 @@ class PinnedMessagesListPresenter( } val timelineProtectionState = timelineProtectionPresenter.present() val linkState = linkPresenter.present() - val userEventPermissions by room.permissionsAsState(UserEventPermissions.DEFAULT) { perms -> - perms.userEventPermissions() - } + val customReactionState = customReactionPresenter.present() + val reactionSummaryState = reactionSummaryPresenter.present() val displayThreadSummaries by featureFlagService.isFeatureEnabledFlow(FeatureFlags.Threads).collectAsState(false) @@ -138,6 +145,7 @@ class PinnedMessagesListPresenter( when (event) { is PinnedMessagesListEvent.HandleAction -> sessionCoroutineScope.handleTimelineAction(event.action, event.event) is PinnedMessagesListEvent.OpenThread -> navigator.navigateToThread(event.threadRootId) + is PinnedMessagesListEvent.ToggleReaction -> sessionCoroutineScope.toggleReaction(event.emoji, event.eventOrTransactionId) } } @@ -148,10 +156,20 @@ class PinnedMessagesListPresenter( displayThreadSummaries = displayThreadSummaries, userEventPermissions = userEventPermissions, timelineItems = pinnedMessageItems, + customReactionState = customReactionState, + reactionSummaryState = reactionSummaryState, eventSink = ::handleEvent, ) } + private fun CoroutineScope.toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId) { + launch { + timelineProvider.invokeOnTimeline { + toggleReaction(emoji, eventOrTransactionId) + } + } + } + private fun CoroutineScope.handleTimelineAction( action: TimelineItemAction, targetEvent: TimelineItem.Event, @@ -233,6 +251,8 @@ class PinnedMessagesListPresenter( linkState: LinkState, userEventPermissions: UserEventPermissions, timelineItems: AsyncData>, + customReactionState: CustomReactionState, + reactionSummaryState: ReactionSummaryState, eventSink: (PinnedMessagesListEvent) -> Unit ): PinnedMessagesListState { return when (timelineItems) { @@ -251,6 +271,8 @@ class PinnedMessagesListPresenter( userEventPermissions = userEventPermissions, timelineItems = timelineItems.data, actionListState = actionListState, + customReactionState = customReactionState, + reactionSummaryState = reactionSummaryState, eventSink = eventSink ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListState.kt index bf37029eff..6c55a0d90a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListState.kt @@ -16,6 +16,8 @@ import io.element.android.features.messages.impl.UserEventPermissions import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.link.LinkState import io.element.android.features.messages.impl.timeline.TimelineRoomInfo +import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState +import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.libraries.ui.strings.CommonPlurals @@ -34,6 +36,8 @@ sealed interface PinnedMessagesListState { val timelineItems: ImmutableList, val actionListState: ActionListState, val linkState: LinkState, + val customReactionState: CustomReactionState, + val reactionSummaryState: ReactionSummaryState, val displayThreadSummaries: Boolean, val eventSink: (PinnedMessagesListEvent) -> Unit, ) : PinnedMessagesListState { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStateProvider.kt index bfa9820a44..23e1feac93 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStateProvider.kt @@ -19,7 +19,11 @@ import io.element.android.features.messages.impl.timeline.aTimelineItemDaySepara import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.aTimelineItemReactions import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo +import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState +import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.aCustomReactionState +import io.element.android.features.messages.impl.aReactionSummaryState import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemAudioContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemFileContent @@ -92,6 +96,8 @@ fun aLoadedPinnedMessagesListState( linkState: LinkState = aLinkState(), timelineItems: List = emptyList(), actionListState: ActionListState = anActionListState(), + customReactionState: CustomReactionState = aCustomReactionState(), + reactionSummaryState: ReactionSummaryState = aReactionSummaryState(), aUserEventPermissions: UserEventPermissions = UserEventPermissions.DEFAULT, displayThreadSummaries: Boolean = false, eventSink: (PinnedMessagesListEvent) -> Unit = {} @@ -101,6 +107,8 @@ fun aLoadedPinnedMessagesListState( linkState = linkState, timelineItems = timelineItems.toImmutableList(), actionListState = actionListState, + customReactionState = customReactionState, + reactionSummaryState = reactionSummaryState, userEventPermissions = aUserEventPermissions, displayThreadSummaries = displayThreadSummaries, eventSink = eventSink, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt index 7190aa174f..c900aa9f66 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt @@ -32,8 +32,12 @@ import io.element.android.features.messages.impl.link.LinkEvent import io.element.android.features.messages.impl.link.LinkView import io.element.android.features.messages.impl.timeline.TimelineEvent import io.element.android.features.messages.impl.timeline.components.TimelineItemRow +import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionBottomSheet +import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvent import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData +import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvent +import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionEvent @@ -195,11 +199,27 @@ private fun PinnedMessagesListLoaded( ) } + fun onReactionClick(emoji: String, event: TimelineItem.Event) { + event.eventOrTransactionId.let { eventOrTransactionId -> + state.eventSink(PinnedMessagesListEvent.ToggleReaction(emoji, eventOrTransactionId)) + } + } + + fun onReactionLongClick(emoji: String, event: TimelineItem.Event) { + event.eventId?.let { eventId -> + state.reactionSummaryState.eventSink(ReactionSummaryEvent.ShowReactionSummary(eventId, event.reactionsState.reactions, emoji)) + } + } + + fun onMoreReactionsClick(event: TimelineItem.Event) { + state.customReactionState.eventSink(CustomReactionEvent.ShowCustomReactionSheet(event)) + } + ActionListView( state = state.actionListState, onSelectAction = ::onActionSelected, - onCustomReactionClick = {}, - onEmojiReactionClick = { _, _ -> }, + onCustomReactionClick = ::onMoreReactionsClick, + onEmojiReactionClick = ::onReactionClick, onVerifiedUserSendFailureClick = {} ) LazyColumn( @@ -230,9 +250,9 @@ private fun PinnedMessagesListLoaded( onLongClick = ::onMessageLongClick, displayThreadSummaries = displayThreadSummaries, inReplyToClick = {}, - onReactionClick = { _, _ -> }, - onReactionLongClick = { _, _ -> }, - onMoreReactionsClick = {}, + onReactionClick = ::onReactionClick, + onReactionLongClick = ::onReactionLongClick, + onMoreReactionsClick = ::onMoreReactionsClick, onReadReceiptClick = {}, onSwipeToReply = {}, eventSink = { timelineItemEvent -> @@ -262,6 +282,13 @@ private fun PinnedMessagesListLoaded( state.linkState, onLinkValid = onLinkClick, ) + CustomReactionBottomSheet( + state = state.customReactionState, + onSelectEmoji = { eventOrTransactionId, emoji -> + state.eventSink(PinnedMessagesListEvent.ToggleReaction(emoji.unicode, eventOrTransactionId)) + } + ) + ReactionSummaryView(state = state.reactionSummaryState) } @Composable From b1a3c2acb84999287420065ad827c2ec2368458c Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:34:59 +0000 Subject: [PATCH 002/338] Review + lint --- .../impl/pinned/list/PinnedMessagesListStateProvider.kt | 4 ++-- .../messages/impl/pinned/list/PinnedMessagesListView.kt | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStateProvider.kt index 23e1feac93..19f19da0de 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStateProvider.kt @@ -10,6 +10,8 @@ package io.element.android.features.messages.impl.pinned.list import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.messages.impl.UserEventPermissions +import io.element.android.features.messages.impl.aCustomReactionState +import io.element.android.features.messages.impl.aReactionSummaryState import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.actionlist.anActionListState import io.element.android.features.messages.impl.link.LinkState @@ -22,8 +24,6 @@ import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.features.messages.impl.aCustomReactionState -import io.element.android.features.messages.impl.aReactionSummaryState import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemAudioContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemFileContent diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt index 4a864eb3ae..49dafe5b2f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt @@ -205,9 +205,7 @@ private fun PinnedMessagesListLoaded( } fun onReactionClick(emoji: String, event: TimelineItem.Event) { - event.eventOrTransactionId.let { eventOrTransactionId -> - state.eventSink(PinnedMessagesListEvent.ToggleReaction(emoji, eventOrTransactionId)) - } + state.eventSink(PinnedMessagesListEvent.ToggleReaction(emoji, event.eventOrTransactionId)) } fun onReactionLongClick(emoji: String, event: TimelineItem.Event) { From c86307dead2f7f93971e23edb2346aefe5d5b752 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 13 Jul 2026 19:54:40 +0100 Subject: [PATCH 003/338] Render push notifications for knocks The homeserver's new MSCxxxx knock push rule notifies members who can act on a knock; the push resolver previously filtered every m.room.member state event out, so the push produced no notification. Split RoomMemberContent out of the filtered-out catch-all and render membership=knock as "Requested to join" from the knocking user (other membership changes stay filtered out). Co-Authored-By: Claude Fable 5 --- .../DefaultNotifiableEventResolver.kt | 25 ++++++++++++++++++- .../impl/src/main/res/values/temporary.xml | 11 ++++++++ .../DefaultNotifiableEventResolverTest.kt | 22 ++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 libraries/push/impl/src/main/res/values/temporary.xml 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 2f3fa9b0b7..bf9390533a 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 @@ -36,6 +36,7 @@ import io.element.android.libraries.matrix.api.media.isPreviewEnabled import io.element.android.libraries.matrix.api.notification.NotificationContent import io.element.android.libraries.matrix.api.notification.NotificationData import io.element.android.libraries.matrix.api.permalink.PermalinkParser +import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType @@ -299,7 +300,29 @@ class DefaultNotifiableEventResolver( Timber.tag(loggerTag.value).d("Ignoring notification for sticker") throw NotificationResolverException.EventFilteredOut } - is NotificationContent.StateEvent.RoomMemberContent, + is NotificationContent.StateEvent.RoomMemberContent -> { + // MSCxxxx: the homeserver pushes knocks to users who can act on them. + if (content.membershipState == RoomMembershipState.KNOCK) { + val notifiableMessageEvent = buildNotifiableMessageEvent( + sessionId = userId, + senderId = content.userId, + roomId = roomId, + eventId = eventId, + noisy = isNoisy, + timestamp = this.timestamp, + senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.userId), + body = stringProvider.getString(R.string.notification_knock_request_body), + roomName = roomDisplayName, + roomIsDm = isDm, + roomAvatarPath = roomAvatarUrl, + senderAvatarPath = senderAvatarUrl, + ) + ResolvedPushEvent.Event(notifiableMessageEvent) + } else { + Timber.tag(loggerTag.value).d("Ignoring notification for membership ${content.membershipState}") + throw NotificationResolverException.EventFilteredOut + } + } NotificationContent.StateEvent.PolicyRuleRoom, NotificationContent.StateEvent.PolicyRuleServer, NotificationContent.StateEvent.PolicyRuleUser, diff --git a/libraries/push/impl/src/main/res/values/temporary.xml b/libraries/push/impl/src/main/res/values/temporary.xml new file mode 100644 index 0000000000..7a569d555e --- /dev/null +++ b/libraries/push/impl/src/main/res/values/temporary.xml @@ -0,0 +1,11 @@ + + + + + "Requested to join" + 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 e196ddb0ec..d188cd0fee 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 @@ -627,6 +627,28 @@ class DefaultNotifiableEventResolverTest : RobolectricTest() { assertThat(result.getEvent(request)).isEqualTo(Result.success(expectedResult)) } + @Test + fun `resolve RoomMemberContent knock`() = runTest { + val sut = createDefaultNotifiableEventResolver( + notificationResult = Result.success( + mapOf( + AN_EVENT_ID to Result.success(aNotificationData( + content = NotificationContent.StateEvent.RoomMemberContent( + userId = A_USER_ID_2, + membershipState = RoomMembershipState.KNOCK + ) + )) + ) + ) + ) + val request = aPushRequest(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID, "firebase") + val result = sut.resolveEvents(A_SESSION_ID, listOf(request)) + val expectedResult = ResolvedPushEvent.Event( + aNotifiableMessageEvent(body = "Requested to join") + ) + assertThat(result.getEvent(request)).isEqualTo(Result.success(expectedResult)) + } + @Test fun `resolve RoomMemberContent other`() = runTest { val sut = createDefaultNotifiableEventResolver( From 34293eb8240a05a6264dc1a5ec87b0dccd67736f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 14 Jul 2026 13:24:33 +0100 Subject: [PATCH 004/338] Refer to the knock push rule by its published MSC number (MSC4506) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Uk8aPxHn3BHCe52L226jdG --- .../push/impl/notifications/DefaultNotifiableEventResolver.kt | 2 +- libraries/push/impl/src/main/res/values/temporary.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 bf9390533a..47f97404a3 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 @@ -301,7 +301,7 @@ class DefaultNotifiableEventResolver( throw NotificationResolverException.EventFilteredOut } is NotificationContent.StateEvent.RoomMemberContent -> { - // MSCxxxx: the homeserver pushes knocks to users who can act on them. + // MSC4506: the homeserver pushes knocks to users who can act on them. if (content.membershipState == RoomMembershipState.KNOCK) { val notifiableMessageEvent = buildNotifiableMessageEvent( sessionId = userId, diff --git a/libraries/push/impl/src/main/res/values/temporary.xml b/libraries/push/impl/src/main/res/values/temporary.xml index 7a569d555e..494f2ee441 100644 --- a/libraries/push/impl/src/main/res/values/temporary.xml +++ b/libraries/push/impl/src/main/res/values/temporary.xml @@ -6,6 +6,6 @@ --> - + "Requested to join" From 21471e07fb5e3faeccc0b5683f58a7adb25e2a0d Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Mon, 3 Aug 2026 09:07:48 -0700 Subject: [PATCH 005/338] Add persistent account provider history to app preferences --- .../api/store/AppPreferencesStore.kt | 14 ++++ .../impl/store/DefaultAppPreferencesStore.kt | 27 +++++++ .../store/DefaultAppPreferencesStoreTest.kt | 70 +++++++++++++++++++ .../test/InMemoryAppPreferencesStore.kt | 15 ++++ 4 files changed, 126 insertions(+) diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt index b1877b3ae5..d4ff5b3592 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt @@ -38,6 +38,20 @@ interface AppPreferencesStore { suspend fun setTracingLogLevel(logLevel: LogLevel) fun getTracingLogLevelFlow(): Flow + /** + * The list of account providers (homeserver URLs) the user has previously authenticated + * against, most recent first. Used to default the account provider and to power autocomplete + * suggestions during sign-in. Stored locally only; never synced across devices. + */ + fun getHomeserverHistoryFlow(): Flow> + + /** + * Add [url] to the front of the account provider history (see [getHomeserverHistoryFlow]). + * The value is normalised (trimmed + lowercased); existing case-insensitive duplicates are + * moved to the front rather than duplicated, and the list is capped in size. No-op for blanks. + */ + suspend fun addHomeserverToHistory(url: String) + suspend fun setTracingLogPacks(targets: Set) fun getTracingLogPacksFlow(): Flow> diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt index af35ac3c44..f8db0c11ee 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.preferences.impl.store +import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.intPreferencesKey @@ -36,6 +37,7 @@ private val timelineMediaPreviewValueKey = stringPreferencesKey("timelineMediaPr private val liveLocationMinimumDistanceUpdateKey = intPreferencesKey("liveLocationMinimumDistanceUpdate") private val logLevelKey = stringPreferencesKey("logLevel") private val traceLogPacksKey = stringPreferencesKey("traceLogPacks") +private val homeserverHistoryKey = stringPreferencesKey("homeserverHistory") private val messageSoundUriKey = stringPreferencesKey("notificationMessageSoundUri") private val messageSoundChannelVersionKey = intPreferencesKey("notificationMessageSoundChannelVersion") private val messageSoundDisplayNameKey = stringPreferencesKey("notificationMessageSoundDisplayName") @@ -43,6 +45,10 @@ private val callRingtoneUriKey = stringPreferencesKey("notificationCallRingtoneU private val callRingtoneChannelVersionKey = intPreferencesKey("notificationCallRingtoneChannelVersion") private val callRingtoneDisplayNameKey = stringPreferencesKey("notificationCallRingtoneDisplayName") +// URLs never contain a newline, so it is a safe delimiter to persist an ordered list in a single String. +private const val HOMESERVER_HISTORY_DELIMITER = "\n" +private const val MAX_HOMESERVER_HISTORY_SIZE = 10 + @ContributesBinding(AppScope::class) class DefaultAppPreferencesStore( private val buildMeta: BuildMeta, @@ -168,6 +174,20 @@ class DefaultAppPreferencesStore( } } + override fun getHomeserverHistoryFlow(): Flow> { + return store.data.map { prefs -> prefs.readHomeserverHistory() } + } + + override suspend fun addHomeserverToHistory(url: String) { + val normalized = url.trim().lowercase() + if (normalized.isEmpty()) return + store.edit { prefs -> + val updated = (listOf(normalized) + prefs.readHomeserverHistory().filter { it != normalized }) + .take(MAX_HOMESERVER_HISTORY_SIZE) + prefs[homeserverHistoryKey] = updated.joinToString(HOMESERVER_HISTORY_DELIMITER) + } + } + override fun getMessageSoundFlow(): Flow { return store.data.map { prefs -> NotificationSound.fromStored(prefs[messageSoundUriKey]) } } @@ -242,6 +262,13 @@ class DefaultAppPreferencesStore( } } +private fun Preferences.readHomeserverHistory(): List { + return this[homeserverHistoryKey] + ?.split(HOMESERVER_HISTORY_DELIMITER) + ?.filter { it.isNotEmpty() } + ?: emptyList() +} + private fun BuildMeta.defaultLogLevel(): LogLevel { return when (buildType) { BuildType.DEBUG -> LogLevel.TRACE diff --git a/libraries/preferences/impl/src/test/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStoreTest.kt b/libraries/preferences/impl/src/test/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStoreTest.kt index c52d1648ac..fbb9005ea5 100644 --- a/libraries/preferences/impl/src/test/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStoreTest.kt +++ b/libraries/preferences/impl/src/test/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStoreTest.kt @@ -54,4 +54,74 @@ class DefaultAppPreferencesStoreTest { assertThat(store.getLiveLocationMinimumDistanceInMetersUpdateFlow().first()).isEqualTo(25) } + + @Test + fun `homeserver history is empty by default`() = runTest { + val store = createStore() + assertThat(store.getHomeserverHistoryFlow().first()).isEmpty() + } + + @Test + fun `adding homeservers keeps the most recent first`() = runTest { + val store = createStore() + + store.addHomeserverToHistory("https://matrix.org") + store.addHomeserverToHistory("https://example.com") + + assertThat(store.getHomeserverHistoryFlow().first()) + .containsExactly("https://example.com", "https://matrix.org") + .inOrder() + } + + @Test + fun `adding an existing homeserver moves it to the front without duplicating`() = runTest { + val store = createStore() + + store.addHomeserverToHistory("https://matrix.org") + store.addHomeserverToHistory("https://example.com") + store.addHomeserverToHistory("https://matrix.org") + + assertThat(store.getHomeserverHistoryFlow().first()) + .containsExactly("https://matrix.org", "https://example.com") + .inOrder() + } + + @Test + fun `homeservers are normalised and deduplicated case-insensitively`() = runTest { + val store = createStore() + + store.addHomeserverToHistory(" HTTPS://Matrix.ORG ") + store.addHomeserverToHistory("https://matrix.org") + + assertThat(store.getHomeserverHistoryFlow().first()) + .containsExactly("https://matrix.org") + } + + @Test + fun `blank homeservers are ignored`() = runTest { + val store = createStore() + + store.addHomeserverToHistory(" ") + + assertThat(store.getHomeserverHistoryFlow().first()).isEmpty() + } + + @Test + fun `homeserver history is capped to the most recent entries`() = runTest { + val store = createStore() + + repeat(15) { index -> + store.addHomeserverToHistory("https://server$index.org") + } + + val history = store.getHomeserverHistoryFlow().first() + assertThat(history).hasSize(10) + assertThat(history.first()).isEqualTo("https://server14.org") + assertThat(history.last()).isEqualTo("https://server5.org") + } + + private fun createStore() = DefaultAppPreferencesStore( + buildMeta = buildMeta, + preferenceDataStoreFactory = FakePreferenceDataStoreFactory(), + ) } diff --git a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt index 6499e70223..e8d0b0bb8e 100644 --- a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt +++ b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt @@ -16,6 +16,7 @@ import io.element.android.libraries.preferences.api.store.NotificationSound import io.element.android.libraries.preferences.api.store.NotificationSoundChannelConfig import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.updateAndGet class InMemoryAppPreferencesStore( @@ -27,6 +28,7 @@ class InMemoryAppPreferencesStore( liveLocationMinimumDistanceUpdate: Int = 10, logLevel: LogLevel = LogLevel.INFO, traceLogPacks: Set = emptySet(), + homeserverHistory: List = emptyList(), messageSound: NotificationSound = NotificationSound.SystemDefault, messageSoundChannelVersion: Int = 0, messageSoundDisplayName: String? = null, @@ -40,6 +42,7 @@ class InMemoryAppPreferencesStore( private val liveLocationMinimumDistanceUpdate = MutableStateFlow(liveLocationMinimumDistanceUpdate) private val logLevel = MutableStateFlow(logLevel) private val tracingLogPacks = MutableStateFlow(traceLogPacks) + private val homeserverHistory = MutableStateFlow(homeserverHistory) private val hideInviteAvatars = MutableStateFlow(hideInviteAvatars) private val timelineMediaPreviewValue = MutableStateFlow(timelineMediaPreviewValue) private val messageSound = MutableStateFlow(messageSound) @@ -117,6 +120,18 @@ class InMemoryAppPreferencesStore( return tracingLogPacks } + override fun getHomeserverHistoryFlow(): Flow> { + return homeserverHistory + } + + override suspend fun addHomeserverToHistory(url: String) { + val normalized = url.trim().lowercase() + if (normalized.isEmpty()) return + homeserverHistory.update { current -> + (listOf(normalized) + current.filter { it != normalized }).take(10) + } + } + override fun getMessageSoundFlow(): Flow { return messageSound } From 4714127cc053e06791c86775734f70ab847ff0ac Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Mon, 3 Aug 2026 10:54:45 -0700 Subject: [PATCH 006/338] Default the account provider to the most recently used one --- features/login/impl/build.gradle.kts | 2 + .../AccountProviderDataSource.kt | 39 ++++++++++-- .../login/impl/DefaultLoginEntryPointTest.kt | 5 +- .../AccountProviderDataSourceFixture.kt | 25 ++++++++ .../AccountProviderDataSourceTest.kt | 62 ++++++++++++++++--- .../changeserver/ChangeServerPresenterTest.kt | 3 +- .../LoginWithClassicPresenterTest.kt | 4 +- .../ConfirmAccountProviderPresenterTest.kt | 4 +- .../createaccount/DefaultMessageParserTest.kt | 5 +- .../LoginPasswordPresenterTest.kt | 4 +- .../onboarding/OnBoardingPresenterTest.kt | 5 +- 11 files changed, 130 insertions(+), 28 deletions(-) create mode 100644 features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceFixture.kt diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index 86a8e0e7bc..6cf8945e05 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -69,6 +69,7 @@ dependencies { implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.qrcode) implementation(projects.libraries.oauth.api) + implementation(projects.libraries.preferences.api) implementation(projects.libraries.uiUtils) implementation(projects.libraries.wellknown.api) implementation(libs.androidx.browser) @@ -80,6 +81,7 @@ dependencies { testImplementation(projects.features.login.test) testImplementation(projects.features.enterprise.test) testImplementation(projects.features.preferences.test) + testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.oauth.test) testImplementation(projects.libraries.permissions.test) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt index 931959d4ea..28e4294fa4 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt @@ -13,27 +13,58 @@ import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.features.enterprise.api.canConnectToAnyHomeserver +import io.element.android.libraries.di.annotations.AppCoroutineScope +import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch @SingleIn(AppScope::class) @Inject class AccountProviderDataSource( - enterpriseService: EnterpriseService, + private val enterpriseService: EnterpriseService, + private val appPreferencesStore: AppPreferencesStore, + @AppCoroutineScope private val coroutineScope: CoroutineScope, ) { - private val defaultAccountProvider = createAccountProvider( + // The provider used when the user has not selected one: an enterprise/MDM-configured provider, + // else matrix.org. The most recently used provider (from history) can override it, see init. + private val configuredAccountProvider = createAccountProvider( url = enterpriseService.defaultHomeserverList() .firstOrNull { it != EnterpriseService.ANY_ACCOUNT_PROVIDER } ?: AuthenticationConfig.MATRIX_ORG_URL ) - private val accountProvider: MutableStateFlow = MutableStateFlow(defaultAccountProvider) + private val accountProvider: MutableStateFlow = MutableStateFlow(configuredAccountProvider) val flow: StateFlow = accountProvider.asStateFlow() + init { + // Seed the default from the last used provider, unless the user has already selected one. + coroutineScope.launch { + val default = defaultAccountProvider() + accountProvider.update { current -> if (current == configuredAccountProvider) default else current } + } + } + suspend fun reset() { - accountProvider.emit(defaultAccountProvider) + accountProvider.emit(defaultAccountProvider()) + } + + /** + * The provider to default to: the most recently used one from history when the user is free to + * connect to any provider, otherwise the enterprise/MDM-configured provider. + */ + private suspend fun defaultAccountProvider(): AccountProvider { + if (!enterpriseService.canConnectToAnyHomeserver()) { + return configuredAccountProvider + } + val lastUsedProvider = appPreferencesStore.getHomeserverHistoryFlow().first().firstOrNull() + return lastUsedProvider?.let { createAccountProvider(it) } ?: configuredAccountProvider } suspend fun setUrl(url: String) { diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt index a05194d008..4e040a7556 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt @@ -12,9 +12,8 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat -import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.api.LoginEntryPoint -import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.features.login.impl.classic.FakeElementClassicConnection import io.element.android.features.preferences.test.FakePreferencesEntryPoint import io.element.android.libraries.oauth.test.customtab.FakeOAuthActionFlow @@ -38,7 +37,7 @@ class DefaultLoginEntryPointTest { LoginFlowNode( buildContext = buildContext, plugins = plugins, - accountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), + accountProviderDataSource = anAccountProviderDataSource(), oAuthActionFlow = FakeOAuthActionFlow(), appCoroutineScope = backgroundScope, elementClassicConnection = FakeElementClassicConnection(), diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceFixture.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceFixture.kt new file mode 100644 index 0000000000..e8f9dbefb9 --- /dev/null +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceFixture.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.login.impl.accountprovider + +import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.features.enterprise.test.FakeEnterpriseService +import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +fun anAccountProviderDataSource( + enterpriseService: EnterpriseService = FakeEnterpriseService(), + appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(), + coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Unconfined), +) = AccountProviderDataSource( + enterpriseService = enterpriseService, + appPreferencesStore = appPreferencesStore, + coroutineScope = coroutineScope, +) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceTest.kt index f86df13335..30e25c8c3e 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceTest.kt @@ -13,6 +13,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.tests.testutils.WarmUpRule import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -24,7 +25,7 @@ class AccountProviderDataSourceTest { @Test fun `present - initial state`() = runTest { - val sut = AccountProviderDataSource(FakeEnterpriseService()) + val sut = anAccountProviderDataSource() sut.flow.test { val initialState = awaitItem() assertThat(initialState).isEqualTo( @@ -41,10 +42,10 @@ class AccountProviderDataSourceTest { @Test fun `present - initial state - matrix org`() = runTest { - val sut = AccountProviderDataSource( - FakeEnterpriseService( + val sut = anAccountProviderDataSource( + enterpriseService = FakeEnterpriseService( defaultHomeserverListResult = { listOf(AuthenticationConfig.MATRIX_ORG_URL) } - ) + ), ) sut.flow.test { val initialState = awaitItem() @@ -62,10 +63,10 @@ class AccountProviderDataSourceTest { @Test fun `present - ensure that default homeserver is not star char`() = runTest { - val sut = AccountProviderDataSource( - FakeEnterpriseService( + val sut = anAccountProviderDataSource( + enterpriseService = FakeEnterpriseService( defaultHomeserverListResult = { listOf(EnterpriseService.ANY_ACCOUNT_PROVIDER, AuthenticationConfig.MATRIX_ORG_URL) } - ) + ), ) sut.flow.test { val initialState = awaitItem() @@ -83,7 +84,7 @@ class AccountProviderDataSourceTest { @Test fun `present - user change and reset`() = runTest { - val sut = AccountProviderDataSource(FakeEnterpriseService()) + val sut = anAccountProviderDataSource() sut.flow.test { val initialState = awaitItem() assertThat(initialState.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) @@ -106,7 +107,7 @@ class AccountProviderDataSourceTest { @Test fun `present - set url and reset`() = runTest { - val sut = AccountProviderDataSource(FakeEnterpriseService()) + val sut = anAccountProviderDataSource() sut.flow.test { val initialState = awaitItem() assertThat(initialState.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) @@ -126,4 +127,47 @@ class AccountProviderDataSourceTest { assertThat(resetState.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) } } + + @Test + fun `present - defaults to the most recently used provider from history`() = runTest { + val sut = anAccountProviderDataSource( + appPreferencesStore = InMemoryAppPreferencesStore( + homeserverHistory = listOf("https://example.com", "https://matrix.org"), + ), + ) + sut.flow.test { + assertThat(awaitItem().url).isEqualTo("https://example.com") + } + } + + @Test + fun `present - history is ignored when the account provider is enforced`() = runTest { + val sut = anAccountProviderDataSource( + enterpriseService = FakeEnterpriseService( + defaultHomeserverListResult = { listOf("https://enforced.org") } + ), + appPreferencesStore = InMemoryAppPreferencesStore( + homeserverHistory = listOf("https://example.com"), + ), + ) + sut.flow.test { + assertThat(awaitItem().url).isEqualTo("https://enforced.org") + } + } + + @Test + fun `present - reset returns to the most recently used provider from history`() = runTest { + val sut = anAccountProviderDataSource( + appPreferencesStore = InMemoryAppPreferencesStore( + homeserverHistory = listOf("https://example.com"), + ), + ) + sut.flow.test { + assertThat(awaitItem().url).isEqualTo("https://example.com") + sut.setUrl("https://other.com") + assertThat(awaitItem().url).isEqualTo("https://other.com") + sut.reset() + assertThat(awaitItem().url).isEqualTo("https://example.com") + } + } } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index 5d9040adac..868f56f8a7 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -14,6 +14,7 @@ import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.features.login.impl.error.ChangeServerError import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate import io.element.android.features.wellknown.test.FakeWellknownRetriever @@ -259,7 +260,7 @@ class ChangeServerPresenterTest { private fun createPresenter( authenticationService: FakeMatrixAuthenticationService = FakeMatrixAuthenticationService(), - accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), + accountProviderDataSource: AccountProviderDataSource = anAccountProviderDataSource(), enterpriseService: EnterpriseService = FakeEnterpriseService(), wellknownRetriever: WellknownRetriever = FakeWellknownRetriever(), localNetworkPermissionAdvisor: FakeLocalNetworkPermissionAdvisor = FakeLocalNetworkPermissionAdvisor(), diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicPresenterTest.kt index 8d56386fa4..d4a1a74d99 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicPresenterTest.kt @@ -8,8 +8,8 @@ package io.element.android.features.login.impl.screens.classic.loginwithclassic import com.google.common.truth.Truth.assertThat -import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.features.login.impl.classic.ElementClassicConnection import io.element.android.features.login.impl.classic.ElementClassicConnectionState import io.element.android.features.login.impl.classic.FakeElementClassicConnection @@ -249,7 +249,7 @@ private fun createPresenter( navigator: LoginWithClassicNavigator = FakeLoginWithClassicNavigator(), loginModePresenter: LoginModePresenter = createLoginModePresenter(), elementClassicConnection: ElementClassicConnection = FakeElementClassicConnection(), - accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), + accountProviderDataSource: AccountProviderDataSource = anAccountProviderDataSource(), isEnterpriseBuild: Boolean = false, ) = LoginWithClassicPresenter( userId = userId, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index c20896870b..ce8e60c73f 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -10,8 +10,8 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig -import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.features.login.impl.login.LoginMode import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported import io.element.android.features.login.impl.screens.onboarding.createLoginModePresenter @@ -378,7 +378,7 @@ class ConfirmAccountProviderPresenterTest { private fun createConfirmAccountProviderPresenter( params: ConfirmAccountProviderPresenter.Params = ConfirmAccountProviderPresenter.Params(isAccountCreation = false), - accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), + accountProviderDataSource: AccountProviderDataSource = anAccountProviderDataSource(), matrixAuthenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), defaultOAuthActionFlow: OAuthActionFlow = FakeOAuthActionFlow(), webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever(), diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt index 680fda2838..e3fea27557 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt @@ -10,8 +10,7 @@ package io.element.android.features.login.impl.screens.createaccount import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig -import io.element.android.features.enterprise.test.FakeEnterpriseService -import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.matrix.api.auth.external.ExternalSession import kotlinx.serialization.SerializationException @@ -70,7 +69,7 @@ class DefaultMessageParserTest { private fun createDefaultMessageParser(): DefaultMessageParser { return DefaultMessageParser( - accountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), + accountProviderDataSource = anAccountProviderDataSource(), json = DefaultJsonProvider(), ) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt index 31a835cb8c..4133403c51 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt @@ -10,8 +10,8 @@ package io.element.android.features.login.impl.screens.loginpassword import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig -import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.AN_EXCEPTION @@ -157,7 +157,7 @@ class LoginPasswordPresenterTest { private fun createLoginPasswordPresenter( initialLogin: String = "", authenticationService: FakeMatrixAuthenticationService = FakeMatrixAuthenticationService(), - accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), + accountProviderDataSource: AccountProviderDataSource = anAccountProviderDataSource(), ): LoginPasswordPresenter = LoginPasswordPresenter( initialLogin = initialLogin, authenticationService = authenticationService, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt index 79268aa1e4..b14e288d7e 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt @@ -15,6 +15,7 @@ import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate import io.element.android.features.login.impl.login.LoginModePresenter import io.element.android.features.login.impl.web.FakeWebClientUrlForAuthenticationRetriever @@ -247,7 +248,7 @@ class OnBoardingPresenterTest { Result.failure(AN_EXCEPTION) }, ) - val accountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()) + val accountProviderDataSource = anAccountProviderDataSource() val presenter = createPresenter( params = OnBoardingNode.Params( accountProvider = A_HOMESERVER_URL, @@ -297,7 +298,7 @@ private fun createPresenter( loginModePresenter: LoginModePresenter = createLoginModePresenter(), onBoardingLogoResIdProvider: OnBoardingLogoResIdProvider = OnBoardingLogoResIdProvider { null }, sessionStore: SessionStore = InMemorySessionStore(), - accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), + accountProviderDataSource: AccountProviderDataSource = anAccountProviderDataSource(), ) = OnBoardingPresenter( params = params, buildMeta = buildMeta, From 3c09fb37b92a254a81286fab01a81dac21a9c7ab Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Mon, 3 Aug 2026 11:18:05 -0700 Subject: [PATCH 007/338] Save the account provider to history on successful sign-in --- .../SaveAccountProviderToHistory.kt | 28 +++++++++++++++ .../login/impl/login/LoginModePresenter.kt | 3 ++ .../createaccount/CreateAccountPresenter.kt | 3 ++ .../loginpassword/LoginPasswordPresenter.kt | 3 ++ .../SaveAccountProviderToHistoryTest.kt | 34 +++++++++++++++++++ .../CreateAccountPresenterTest.kt | 30 ++++++++++++++++ .../LoginPasswordPresenterTest.kt | 32 +++++++++++++++++ .../onboarding/OnBoardingPresenterTest.kt | 5 +++ 8 files changed, 138 insertions(+) create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt create mode 100644 features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt new file mode 100644 index 0000000000..a88be1ae15 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.login.impl.accountprovider + +import dev.zacsweers.metro.Inject +import io.element.android.libraries.preferences.api.store.AppPreferencesStore + +/** + * Persist the account provider the user just authenticated against, so that it can be offered as + * the default (see [AccountProviderDataSource]) and as an autocomplete suggestion on the next + * sign-in. To be called from the manual sign-in / account creation flows only, on success. + * + * See [AppPreferencesStore.addHomeserverToHistory]. + */ +@Inject +class SaveAccountProviderToHistory( + private val accountProviderDataSource: AccountProviderDataSource, + private val appPreferencesStore: AppPreferencesStore, +) { + suspend operator fun invoke() { + appPreferencesStore.addHomeserverToHistory(accountProviderDataSource.flow.value.url) + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModePresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModePresenter.kt index 34188095b0..1bb85fb6c9 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModePresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModePresenter.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import dev.zacsweers.metro.Inject +import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory import io.element.android.features.login.impl.error.ChangeServerError import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate import io.element.android.features.login.impl.screens.chooseaccountprovider.ChooseAccountProviderPresenter @@ -41,6 +42,7 @@ class LoginModePresenter( private val authenticationService: MatrixAuthenticationService, private val webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever, private val localNetworkPermissionGate: LocalNetworkPermissionGate, + private val saveAccountProviderToHistory: SaveAccountProviderToHistory, ) : Presenter { @Composable override fun present(): LoginModeState { @@ -120,6 +122,7 @@ class LoginModePresenter( .onSuccess { loginMode.value = AsyncData.Uninitialized } .onFailure { loginMode.value = AsyncData.Failure(it) } is OAuthAction.Success -> authenticationService.loginWithOAuth(action.url) + .onSuccess { saveAccountProviderToHistory() } .onFailure { loginMode.value = AsyncData.Failure(it) } } oAuthActionFlow.reset() diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt index bf7af111f4..d750586e94 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject +import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.extensions.flatMap @@ -33,6 +34,7 @@ class CreateAccountPresenter( private val authenticationService: MatrixAuthenticationService, private val messageParser: MessageParser, private val buildMeta: BuildMeta, + private val saveAccountProviderToHistory: SaveAccountProviderToHistory, ) : Presenter { @AssistedFactory interface Factory { @@ -74,6 +76,7 @@ class CreateAccountPresenter( }.flatMap { externalSession -> authenticationService.importCreatedSession(externalSession) }.onSuccess { sessionId -> + saveAccountProviderToHistory() loggedInState.value = AsyncAction.Success(sessionId) }.onFailure { failure -> loggedInState.value = AsyncAction.Failure(failure) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt index f26f342a42..6e28bd8781 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt @@ -20,6 +20,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService @@ -33,6 +34,7 @@ class LoginPasswordPresenter( private val initialLogin: String, private val authenticationService: MatrixAuthenticationService, private val accountProviderDataSource: AccountProviderDataSource, + private val saveAccountProviderToHistory: SaveAccountProviderToHistory, ) : Presenter { @AssistedFactory interface Factory { @@ -83,6 +85,7 @@ class LoginPasswordPresenter( loggedInState.value = AsyncData.Loading() authenticationService.login(formState.login.trim(), formState.password) .onSuccess { sessionId -> + saveAccountProviderToHistory() loggedInState.value = AsyncData.Success(sessionId) } .onFailure { failure -> diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt new file mode 100644 index 0000000000..957235395f --- /dev/null +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.login.impl.accountprovider + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore +import io.element.android.tests.testutils.WarmUpRule +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class SaveAccountProviderToHistoryTest { + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `invoke saves the current account provider url to history`() = runTest { + val appPreferencesStore = InMemoryAppPreferencesStore() + val accountProviderDataSource = anAccountProviderDataSource() + accountProviderDataSource.setUrl("https://example.com") + val sut = SaveAccountProviderToHistory(accountProviderDataSource, appPreferencesStore) + + sut() + + assertThat(appPreferencesStore.getHomeserverHistoryFlow().first()) + .containsExactly("https://example.com") + } +} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt index c70a5c9a9a..d2c9cf32b2 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt @@ -9,6 +9,10 @@ package io.element.android.features.login.impl.screens.createaccount import com.google.common.truth.Truth.assertThat +import io.element.android.appconfig.AuthenticationConfig +import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory +import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService @@ -19,10 +23,13 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService +import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test +import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -94,6 +101,26 @@ class CreateAccountPresenterTest { lambda.assertions().isCalledOnce().with(value("aMessage")) } + @Test + fun `present - successful account creation saves the account provider to history`() = runTest { + val appPreferencesStore = InMemoryAppPreferencesStore() + val presenter = createPresenter( + authenticationService = FakeMatrixAuthenticationService( + importCreatedSessionLambda = { Result.success(A_SESSION_ID) } + ), + messageParser = FakeMessageParser { anExternalSession() }, + appPreferencesStore = appPreferencesStore, + ) + presenter.test { + val initialState = awaitItem() + initialState.eventSink(CreateAccountEvents.OnMessageReceived("aMessage")) + assertThat(awaitItem().createAction.isLoading()).isTrue() + assertThat(awaitItem().createAction.dataOrNull()).isEqualTo(A_SESSION_ID) + assertThat(appPreferencesStore.getHomeserverHistoryFlow().first()) + .containsExactly(AuthenticationConfig.MATRIX_ORG_URL) + } + } + @Test fun `present - receiving a message able to be parsed but error in importing change the state to error`() = runTest { val presenter = createPresenter( @@ -115,10 +142,13 @@ class CreateAccountPresenterTest { authenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), messageParser: MessageParser = FakeMessageParser(), buildMeta: BuildMeta = aBuildMeta(), + appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(), + accountProviderDataSource: AccountProviderDataSource = anAccountProviderDataSource(), ) = CreateAccountPresenter( url = url, authenticationService = authenticationService, messageParser = messageParser, buildMeta = buildMeta, + saveAccountProviderToHistory = SaveAccountProviderToHistory(accountProviderDataSource, appPreferencesStore), ) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt index 4133403c51..4dd2859dee 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt @@ -11,6 +11,7 @@ package io.element.android.features.login.impl.screens.loginpassword import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.SessionId @@ -21,8 +22,11 @@ import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.A_USER_NAME_2 import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails +import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test +import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -101,6 +105,32 @@ class LoginPasswordPresenterTest { } } + @Test + fun `present - successful login saves the account provider to history`() = runTest { + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(aMatrixHomeServerDetails()) + }, + ) + val appPreferencesStore = InMemoryAppPreferencesStore() + createLoginPasswordPresenter( + authenticationService = authenticationService, + appPreferencesStore = appPreferencesStore, + ).test { + val initialState = awaitItem() + initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME)) + initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD)) + skipItems(1) + val loginAndPasswordState = awaitItem() + loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit) + skipItems(1) + val loggedInState = awaitItem() + assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Success(A_SESSION_ID)) + assertThat(appPreferencesStore.getHomeserverHistoryFlow().first()) + .containsExactly(AuthenticationConfig.MATRIX_ORG_URL) + } + } + @Test fun `present - submit with error`() = runTest { val authenticationService = FakeMatrixAuthenticationService( @@ -158,9 +188,11 @@ class LoginPasswordPresenterTest { initialLogin: String = "", authenticationService: FakeMatrixAuthenticationService = FakeMatrixAuthenticationService(), accountProviderDataSource: AccountProviderDataSource = anAccountProviderDataSource(), + appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(), ): LoginPasswordPresenter = LoginPasswordPresenter( initialLogin = initialLogin, authenticationService = authenticationService, accountProviderDataSource = accountProviderDataSource, + saveAccountProviderToHistory = SaveAccountProviderToHistory(accountProviderDataSource, appPreferencesStore), ) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt index b14e288d7e..49ea0e3caa 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt @@ -15,6 +15,7 @@ import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate import io.element.android.features.login.impl.login.LoginModePresenter @@ -36,6 +37,7 @@ import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.oauth.api.OAuthActionFlow import io.element.android.libraries.oauth.test.customtab.FakeOAuthActionFlow import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionAdvisor +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.aSessionData @@ -322,6 +324,8 @@ fun createLoginModePresenter( io.element.android.libraries.permissions.test.FakeLocalNetworkPermissionAdvisor(), permissionsPresenterFactory: io.element.android.libraries.permissions.api.PermissionsPresenter.Factory = io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory(), + saveAccountProviderToHistory: SaveAccountProviderToHistory = + SaveAccountProviderToHistory(anAccountProviderDataSource(), InMemoryAppPreferencesStore()), ): LoginModePresenter = LoginModePresenter( oAuthActionFlow = oAuthActionFlow, authenticationService = authenticationService, @@ -330,4 +334,5 @@ fun createLoginModePresenter( advisor = localNetworkPermissionAdvisor, permissionsPresenterFactory = permissionsPresenterFactory, ), + saveAccountProviderToHistory = saveAccountProviderToHistory, ) From 38102747adb3544e29d54e4f9bbfb108e5e5eebc Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 30 Jun 2026 18:46:56 +0200 Subject: [PATCH 008/338] Wire user status through the Rust SDK and refresh profile after change # Conflicts: # libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt --- .../matrix/api/user/DisplayedStatus.kt | 6 ++- .../libraries/matrix/impl/RustMatrixClient.kt | 43 +++++++------------ .../matrix/impl/room/JoinedRustRoom.kt | 1 + .../{mapper => user}/UserProfileMapper.kt | 5 ++- .../UserSearchResultMapper.kt | 3 +- .../libraries/matrix/impl/user/UserStatus.kt | 29 +++++++++++++ .../SessionVerificationRequestDetails.kt | 2 +- .../impl/mapper/UserProfileMapperTest.kt | 1 + .../usersearch/UserSearchResultMapperTest.kt | 1 + 9 files changed, 59 insertions(+), 32 deletions(-) rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/{mapper => user}/UserProfileMapper.kt (71%) rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/{usersearch => user}/UserSearchResultMapper.kt (86%) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/user/UserStatus.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/DisplayedStatus.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/DisplayedStatus.kt index 581658ff28..64cf68cda7 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/DisplayedStatus.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/DisplayedStatus.kt @@ -13,6 +13,10 @@ import kotlinx.parcelize.Parcelize @Immutable sealed interface DisplayedStatus : Parcelable { + + /** Useful for adding extension methods. */ + companion object + /** Status set manually by the user via m.status. */ @Parcelize data class UserSet(val status: UserStatus) : DisplayedStatus @@ -21,6 +25,6 @@ sealed interface DisplayedStatus : Parcelable { @Parcelize data class InCall( /** Unix timestamp in seconds when the call was joined (from m.call.call_joined_ts). */ - val callJoinedTs: Long, + val callJoinedTs: Long?, ) : DisplayedStatus } 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 88b1f05909..d946ffd18b 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 @@ -49,7 +49,6 @@ import io.element.android.libraries.matrix.api.scanner.ContentScanner import io.element.android.libraries.matrix.api.spaces.SpaceService import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion import io.element.android.libraries.matrix.api.sync.SyncState -import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.UserStatus @@ -58,7 +57,8 @@ import io.element.android.libraries.matrix.impl.exception.mapClientException import io.element.android.libraries.matrix.impl.linknewdevice.RustLinkDesktopHandler import io.element.android.libraries.matrix.impl.linknewdevice.RustLinkMobileHandler import io.element.android.libraries.matrix.impl.linknewdevice.RustQrCodeDataParser -import io.element.android.libraries.matrix.impl.mapper.map +import io.element.android.libraries.matrix.impl.user.into +import io.element.android.libraries.matrix.impl.user.map import io.element.android.libraries.matrix.impl.media.RustMediaLoader import io.element.android.libraries.matrix.impl.media.RustMediaPreviewService import io.element.android.libraries.matrix.impl.notification.RustNotificationService @@ -85,7 +85,7 @@ import io.element.android.libraries.matrix.impl.search.RustMessageSearchService import io.element.android.libraries.matrix.impl.spaces.RustSpaceService import io.element.android.libraries.matrix.impl.sync.RustSyncService import io.element.android.libraries.matrix.impl.sync.map -import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper +import io.element.android.libraries.matrix.impl.user.UserSearchResultMapper import io.element.android.libraries.matrix.impl.util.cancelAndDestroy import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService @@ -271,8 +271,6 @@ class RustMatrixClient( private var clientDelegateTaskHandle: TaskHandle? = innerClient.setDelegate(sessionDelegate) - @Volatile private var localUserStatus: UserStatus? = null - private val _userProfile: MutableStateFlow = MutableStateFlow( MatrixUser( userId = sessionId, @@ -458,13 +456,8 @@ class RustMatrixClient( override suspend fun getUserProfile(): Result = getProfile(sessionId) .onSuccess { matrixUser -> - _userProfile.emit( - matrixUser.copy( - rawStatus = localUserStatus, - displayedStatus = localUserStatus?.let { DisplayedStatus.UserSet(it) }, - ) - ) // Also update our session storage + _userProfile.emit(matrixUser) sessionStore.updateUserProfile( sessionId = sessionId.value, displayName = matrixUser.displayName, @@ -495,25 +488,21 @@ class RustMatrixClient( } override suspend fun setUserStatus(status: UserStatus): Result = withContext(sessionDispatcher) { - localUserStatus = status - _userProfile.emit( - _userProfile.value.copy( - rawStatus = status, - displayedStatus = DisplayedStatus.UserSet(status), - ) - ) - Result.success(Unit) + runCatchingExceptions { + innerClient.setUserStatus(status.into()) + }.onSuccess { + // try to refresh data once it's updated + getUserProfile() + } } override suspend fun clearUserStatus(): Result = withContext(sessionDispatcher) { - localUserStatus = null - _userProfile.emit( - _userProfile.value.copy( - rawStatus = null, - displayedStatus = null, - ) - ) - Result.success(Unit) + runCatchingExceptions { + innerClient.clearUserStatus() + }.onSuccess { + // try to refresh data once it's updated + getUserProfile() + } } override suspend fun joinRoom(roomId: RoomId): Result = withContext(sessionDispatcher) { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt index 68fcf6287c..db4cd5f0db 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt @@ -42,6 +42,7 @@ import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings import io.element.android.libraries.matrix.impl.core.RustSendHandle import io.element.android.libraries.matrix.impl.mapper.map +import io.element.android.libraries.matrix.impl.user.map import io.element.android.libraries.matrix.impl.room.history.map import io.element.android.libraries.matrix.impl.room.join.map import io.element.android.libraries.matrix.impl.room.knock.RustKnockRequest diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/user/UserProfileMapper.kt similarity index 71% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapper.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/user/UserProfileMapper.kt index 14c813548a..c9c64d83b6 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/user/UserProfileMapper.kt @@ -6,9 +6,10 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.impl.mapper +package io.element.android.libraries.matrix.impl.user import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.api.user.MatrixUser import org.matrix.rustcomponents.sdk.UserProfile @@ -16,4 +17,6 @@ fun UserProfile.map() = MatrixUser( userId = UserId(userId), displayName = displayName, avatarUrl = avatarUrl, + rawStatus = status?.into(), + displayedStatus = DisplayedStatus.from(status, call) ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/user/UserSearchResultMapper.kt similarity index 86% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/user/UserSearchResultMapper.kt index 5a6481b4f8..f9b1503c8c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/user/UserSearchResultMapper.kt @@ -6,10 +6,9 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.impl.usersearch +package io.element.android.libraries.matrix.impl.user import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults -import io.element.android.libraries.matrix.impl.mapper.map import kotlinx.collections.immutable.toImmutableList import org.matrix.rustcomponents.sdk.SearchUsersResults diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/user/UserStatus.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/user/UserStatus.kt new file mode 100644 index 0000000000..1b8a738bf2 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/user/UserStatus.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.user + +import io.element.android.libraries.matrix.api.user.DisplayedStatus +import io.element.android.libraries.matrix.api.user.UserStatus +import org.matrix.rustcomponents.sdk.UserCall +import org.matrix.rustcomponents.sdk.UserStatus as RustUserStatus + +fun UserStatus.into(): RustUserStatus { + return RustUserStatus(emoji = emoji, text = text) +} + +fun RustUserStatus.into(): UserStatus { + return UserStatus(emoji = emoji, text = text) +} + +fun DisplayedStatus.Companion.from(userStatus: RustUserStatus?, userCall: UserCall?): DisplayedStatus? { + return when { + userStatus != null -> DisplayedStatus.UserSet(userStatus.into()) + userCall != null -> DisplayedStatus.InCall(userCall.callJoinedTs?.toLong()) + else -> null + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/SessionVerificationRequestDetails.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/SessionVerificationRequestDetails.kt index d88f9fd6ce..c3b6d33b82 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/SessionVerificationRequestDetails.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/SessionVerificationRequestDetails.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.matrix.api.core.FlowId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails import io.element.android.libraries.matrix.api.verification.VerificationRequest -import io.element.android.libraries.matrix.impl.mapper.map +import io.element.android.libraries.matrix.impl.user.map import org.matrix.rustcomponents.sdk.SessionVerificationRequestDetails as RustSessionVerificationRequestDetails fun RustSessionVerificationRequestDetails.map() = SessionVerificationRequestDetails( diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapperTest.kt index 61e5886356..4ace520212 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapperTest.kt @@ -11,6 +11,7 @@ package io.element.android.libraries.matrix.impl.mapper import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.impl.fixtures.factories.aRustUserProfile +import io.element.android.libraries.matrix.impl.user.map import io.element.android.libraries.matrix.test.A_USER_ID import org.junit.Test diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapperTest.kt index 3926b93d12..9134fdb6c7 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapperTest.kt @@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSearchUsersResults import io.element.android.libraries.matrix.impl.fixtures.factories.aRustUserProfile +import io.element.android.libraries.matrix.impl.user.UserSearchResultMapper import io.element.android.libraries.matrix.test.A_USER_ID import kotlinx.collections.immutable.toImmutableList import org.junit.Test From ac6cacb415d9e03e5f1328fcf41101490df498b2 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 23 Jul 2026 17:18:02 +0200 Subject: [PATCH 009/338] Add MatrixClient.isUserStatusSupported --- .../io/element/android/libraries/matrix/api/MatrixClient.kt | 3 +++ .../android/libraries/matrix/impl/RustMatrixClient.kt | 6 ++++++ .../android/libraries/matrix/test/FakeMatrixClient.kt | 3 +++ 3 files changed, 12 insertions(+) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 0dd8821a4f..4ceacebbd7 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -99,6 +99,9 @@ interface MatrixClient { /** Clears both m.status and m.call profile fields (maps to DELETE on the profile endpoint per MSC4426). */ suspend fun clearUserStatus(): Result + + /** Whether the homeserver advertises support for user status (MSC4426). */ + suspend fun isUserStatusSupported(): Result suspend fun joinRoom(roomId: RoomId): Result suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result 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 d946ffd18b..a80fa72241 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 @@ -505,6 +505,12 @@ class RustMatrixClient( } } + override suspend fun isUserStatusSupported(): Result = withContext(sessionDispatcher) { + runCatchingExceptions { + innerClient.isUserStatusSupported() + } + } + override suspend fun joinRoom(roomId: RoomId): Result = withContext(sessionDispatcher) { runCatchingExceptions { innerClient.joinRoomById(roomId.value).destroy() diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index cdf7cc85cf..56569cb1ae 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -144,6 +144,7 @@ class FakeMatrixClient( private set var setUserStatusResult: Result = Result.success(Unit) var clearUserStatusResult: Result = Result.success(Unit) + var isUserStatusSupportedResult: Result = Result.success(true) private val _userProfile: MutableStateFlow = MutableStateFlow(MatrixUser(sessionId, userDisplayName, userAvatarUrl)) override val userProfile: StateFlow = _userProfile @@ -288,6 +289,8 @@ class FakeMatrixClient( return clearUserStatusResult } + override suspend fun isUserStatusSupported(): Result = isUserStatusSupportedResult + override suspend fun joinRoom(roomId: RoomId): Result = joinRoomLambda(roomId) override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result { From 489ef4666a654b1f75ac32d2e35f44933a3f2a57 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 24 Jul 2026 15:13:53 +0200 Subject: [PATCH 010/338] Map displayed status for room members and timeline events --- .../android/libraries/matrix/api/room/RoomMember.kt | 1 + .../android/libraries/matrix/impl/room/RoomInfoMapper.kt | 7 ++++++- .../libraries/matrix/impl/room/member/RoomMemberMapper.kt | 5 +++-- .../impl/timeline/item/event/EventTimelineItemMapper.kt | 5 +++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt index 1874986d70..9155d47a08 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt @@ -104,4 +104,5 @@ fun RoomMember.toMatrixUser() = MatrixUser( userId = userId, displayName = displayName.takeUnless { membership == RoomMembershipState.BAN }, avatarUrl = avatarUrl.takeUnless { membership == RoomMembershipState.BAN }, + displayedStatus = displayedStatus, ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt index a972b3c130..33d7017ff9 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt @@ -16,12 +16,15 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels +import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.impl.room.history.map import io.element.android.libraries.matrix.impl.room.join.map import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper import io.element.android.libraries.matrix.impl.room.powerlevels.RoomPowerLevelsValuesMapper import io.element.android.libraries.matrix.impl.room.tombstone.map +import io.element.android.libraries.matrix.impl.user.from +import io.element.android.libraries.matrix.impl.user.into import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableMap import org.matrix.rustcomponents.sdk.Membership @@ -102,7 +105,9 @@ fun RustRoomNotificationMode.map(): RoomNotificationMode = when (this) { fun RoomHero.map(): MatrixUser = MatrixUser( userId = UserId(userId), displayName = displayName, - avatarUrl = avatarUrl + avatarUrl = avatarUrl, + displayedStatus = DisplayedStatus.from(status, call), + rawStatus = status?.into(), ) fun mapPowerLevels(roomPowerLevels: RustRoomPowerLevels): RoomPowerLevels { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt index b2a530f806..bcff7fc153 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt @@ -11,7 +11,9 @@ package io.element.android.libraries.matrix.impl.room.member import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState +import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.impl.room.powerlevels.into +import io.element.android.libraries.matrix.impl.user.from import uniffi.matrix_sdk.RoomMemberRole import org.matrix.rustcomponents.sdk.MembershipState as RustMembershipState import org.matrix.rustcomponents.sdk.RoomMember as RustRoomMember @@ -30,8 +32,7 @@ object RoomMemberMapper { role = mapRole(roomMember.suggestedRoleForPowerLevel, powerLevel), membershipChangeReason = roomMember.membershipChangeReason, isServiceMember = roomMember.isServiceMember, - // TODO map this value when available - displayedStatus = null, + displayedStatus = DisplayedStatus.from(roomMember.status, roomMember.call), ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt index 801d5fd146..3a568761ce 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt @@ -21,7 +21,9 @@ import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetail import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender import io.element.android.libraries.matrix.api.timeline.item.event.Receipt import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin +import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.impl.core.RustSendHandle +import io.element.android.libraries.matrix.impl.user.from import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -75,8 +77,7 @@ fun RustProfileDetails.map(): ProfileDetails { displayName = displayName, displayNameAmbiguous = displayNameAmbiguous, avatarUrl = avatarUrl, - // TODO map this value when available - displayedStatus = null, + displayedStatus = DisplayedStatus.from(status, call), ) } } From 494dc026fa5b596d72c55623cde5b01a9c41cd56 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 24 Jul 2026 17:09:57 +0200 Subject: [PATCH 011/338] Create fun ProfileDetails.toMatrixUser method --- .../components/TimelineItemEventRow.kt | 7 ++----- .../api/timeline/item/event/ProfileDetails.kt | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index fed2ab6577..f52a70064b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -125,6 +125,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.getAvatarUrl import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName import io.element.android.libraries.matrix.api.timeline.item.event.getDisplayName import io.element.android.libraries.matrix.api.timeline.item.event.mediaSources +import io.element.android.libraries.matrix.api.timeline.item.event.toMatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.media.contentvalidation.collectOverallState import io.element.android.libraries.matrix.ui.media.contentvalidation.rememberEventContentValidationState @@ -207,11 +208,7 @@ fun TimelineItemEventRow( } fun onUserDataClick() { - val sender = MatrixUser( - userId = event.senderId, - displayName = event.senderProfile.getDisplayName(), - avatarUrl = event.senderProfile.getAvatarUrl(), - ) + val sender = event.senderProfile.toMatrixUser(event.senderId) onUserDataClick(sender) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileDetails.kt index 73b0478d63..1a56e888fa 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileDetails.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileDetails.kt @@ -11,6 +11,7 @@ package io.element.android.libraries.matrix.api.timeline.item.event import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.DisplayedStatus +import io.element.android.libraries.matrix.api.user.MatrixUser @Immutable sealed interface ProfileDetails { @@ -60,3 +61,20 @@ fun ProfileDetails.getAvatarUrl(): String? { else -> null } } + +fun ProfileDetails.toMatrixUser(userId: UserId): MatrixUser { + return when (this) { + is ProfileDetails.Ready -> MatrixUser( + userId = userId, + displayName = displayName, + avatarUrl = avatarUrl, + displayedStatus = displayedStatus, + ) + else -> MatrixUser( + userId = userId, + displayName = null, + avatarUrl = null, + displayedStatus = null, + ) + } +} From 863cece4fb0824fdb4da58ff35cc9c89ed58b4e4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 24 Jul 2026 18:20:05 +0200 Subject: [PATCH 012/338] Subscribe to own profile updates and gate on server support --- .../impl/root/PreferencesRootPresenter.kt | 10 ++++- .../libraries/matrix/impl/RustMatrixClient.kt | 42 ++++++++++++++----- .../matrix/impl/RustMatrixClientFactory.kt | 1 + .../impl/fixtures/fakes/FakeFfiClient.kt | 10 +++++ 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt index 3daeb5aec1..581c26218b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt @@ -74,8 +74,14 @@ class PreferencesRootPresenter( val showLinkNewDevice by remember { featureFlagService.isFeatureEnabledFlow(FeatureFlags.QrCodeLogin) }.collectAsState(initial = false) - val isUserStatusEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.UserStatus).collectAsState(initial = false) - val userStatusState = if (isUserStatusEnabled) userStatusPresenter.present() else null + + val isUserStatusSupported by produceState(false) { + featureFlagService.isFeatureEnabledFlow(FeatureFlags.UserStatus) + .collect { isUserStatusEnabled -> + value = isUserStatusEnabled && matrixClient.isUserStatusSupported().getOrDefault(false) + } + } + val userStatusState = if (isUserStatusSupported) userStatusPresenter.present() else null val otherSessions by remember { sessionStore.sessionsFlow().map { list -> 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 a80fa72241..faf1503b58 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 @@ -57,8 +57,6 @@ import io.element.android.libraries.matrix.impl.exception.mapClientException import io.element.android.libraries.matrix.impl.linknewdevice.RustLinkDesktopHandler import io.element.android.libraries.matrix.impl.linknewdevice.RustLinkMobileHandler import io.element.android.libraries.matrix.impl.linknewdevice.RustQrCodeDataParser -import io.element.android.libraries.matrix.impl.user.into -import io.element.android.libraries.matrix.impl.user.map import io.element.android.libraries.matrix.impl.media.RustMediaLoader import io.element.android.libraries.matrix.impl.media.RustMediaPreviewService import io.element.android.libraries.matrix.impl.notification.RustNotificationService @@ -86,6 +84,8 @@ import io.element.android.libraries.matrix.impl.spaces.RustSpaceService import io.element.android.libraries.matrix.impl.sync.RustSyncService import io.element.android.libraries.matrix.impl.sync.map import io.element.android.libraries.matrix.impl.user.UserSearchResultMapper +import io.element.android.libraries.matrix.impl.user.into +import io.element.android.libraries.matrix.impl.user.map import io.element.android.libraries.matrix.impl.util.cancelAndDestroy import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService @@ -126,9 +126,11 @@ import org.matrix.rustcomponents.sdk.IgnoredUsersListener import org.matrix.rustcomponents.sdk.Membership import org.matrix.rustcomponents.sdk.NotificationProcessSetup import org.matrix.rustcomponents.sdk.PowerLevels +import org.matrix.rustcomponents.sdk.ProfileListener import org.matrix.rustcomponents.sdk.RoomInfoListener import org.matrix.rustcomponents.sdk.SendQueueRoomErrorListener import org.matrix.rustcomponents.sdk.TaskHandle +import org.matrix.rustcomponents.sdk.UserProfile import org.matrix.rustcomponents.sdk.use import timber.log.Timber import java.io.File @@ -281,6 +283,14 @@ class RustMatrixClient( override val userProfile: StateFlow = _userProfile + private var ownProfileTaskHandle: TaskHandle? = null + + private val ownProfileListener = object : ProfileListener { + override fun onUpdate(profile: UserProfile) { + _userProfile.tryEmit(profile.map()) + } + } + override val ignoredUsersFlow = mxCallbackFlow> { // Fetch the initial value manually, the SDK won't return it automatically channel.trySend(innerClient.ignoredUsers().map(::UserId).toImmutableList()) @@ -301,8 +311,21 @@ class RustMatrixClient( sessionCoroutineScope.launch { // Start notification settings notificationSettingsService.start() + // Setup user profile logic + setupUserProfile() + } - // Update the user profile in the session store if needed + // Schedule regular database vacuuming to ensure DB performance remains optimal + scheduleDatabaseVacuum() + } + + private suspend fun setupUserProfile() { + val supported = isUserStatusSupported().getOrDefault(false) + if (supported) { + // No need to seed the data here, it's already stored by the sdk. + ownProfileTaskHandle = innerClient.subscribeToOwnProfile(ownProfileListener) + } else { + // Seed from the session store so cold-start UI has something to render before the network fetch. sessionStore.getSession(sessionId.value)?.let { sessionData -> _userProfile.emit( MatrixUser( @@ -312,12 +335,8 @@ class RustMatrixClient( ) ) } - // Force a refresh of the profile getUserProfile() } - - // Schedule regular database vacuuming to ensure DB performance remains optimal - scheduleDatabaseVacuum() } override fun userIdServerName(): String { @@ -491,8 +510,8 @@ class RustMatrixClient( runCatchingExceptions { innerClient.setUserStatus(status.into()) }.onSuccess { - // try to refresh data once it's updated - getUserProfile() + // The subscription pushes the update on supported servers; only refresh manually as a fallback. + if (ownProfileTaskHandle == null) getUserProfile() } } @@ -500,8 +519,8 @@ class RustMatrixClient( runCatchingExceptions { innerClient.clearUserStatus() }.onSuccess { - // try to refresh data once it's updated - getUserProfile() + // The subscription pushes the update on supported servers; only refresh manually as a fallback. + if (ownProfileTaskHandle == null) getUserProfile() } } @@ -616,6 +635,7 @@ class RustMatrixClient( sessionCoroutineScope.cancel() clientDelegateTaskHandle?.cancelAndDestroy() + ownProfileTaskHandle?.cancelAndDestroy() sessionVerificationService.destroy() sessionDelegate.clearCurrentClient() 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 de2c5c0141..d4b9063931 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 @@ -155,6 +155,7 @@ class RustMatrixClientFactory( val syncService = client.syncService() .withSharePos(true) .withOfflineMode() + .withProfilesExtension() .finish() return RustMatrixClient( diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt index efae379415..455afe2b30 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt @@ -26,6 +26,7 @@ import org.matrix.rustcomponents.sdk.NoHandle import org.matrix.rustcomponents.sdk.NotificationClient import org.matrix.rustcomponents.sdk.NotificationProcessSetup import org.matrix.rustcomponents.sdk.NotificationSettings +import org.matrix.rustcomponents.sdk.ProfileListener import org.matrix.rustcomponents.sdk.PusherIdentifiers import org.matrix.rustcomponents.sdk.PusherKind import org.matrix.rustcomponents.sdk.RoomDirectorySearch @@ -55,6 +56,8 @@ class FakeFfiClient( private val getStoreSizesResult: () -> StoreSizes = { lambdaError() }, private val createRoomResult: (CreateRoomParameters) -> String = { lambdaError() }, private val homeserverCapabilities: HomeserverCapabilities = FakeFfiHomeserverCapabilities(), + private val isUserStatusSupportedResult: () -> Boolean = { false }, + private val subscribeToOwnProfileResult: (ProfileListener) -> Unit = {}, private val closeResult: () -> Unit = {}, ) : Client(NoHandle) { override fun userId(): String = userId @@ -92,6 +95,13 @@ class FakeFfiClient( return FakeFfiTaskHandle() } + override suspend fun isUserStatusSupported(): Boolean = isUserStatusSupportedResult() + + override fun subscribeToOwnProfile(listener: ProfileListener): TaskHandle { + subscribeToOwnProfileResult(listener) + return FakeFfiTaskHandle() + } + override suspend fun getProfile(userId: String): UserProfile { return getProfileResult(userId) } From 93ca5374d6e8aa377f0e963f5b7cfabc16a8c601 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 3 Aug 2026 17:50:42 +0200 Subject: [PATCH 013/338] Add status emoji badge to current user avatar in home top bar --- .../home/impl/components/HomeTopBar.kt | 132 +++++++++++++++++- 1 file changed, 127 insertions(+), 5 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt index 8163d29d92..63d28dca21 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt @@ -8,6 +8,8 @@ package io.element.android.features.home.impl.components +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.RowScope @@ -15,9 +17,11 @@ import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawing +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.pager.VerticalPager import androidx.compose.foundation.pager.rememberPagerState @@ -25,8 +29,11 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.minimumInteractiveComponentSize import androidx.compose.material3.rememberTopAppBarState +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -37,10 +44,20 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.CompositingStrategy +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import io.element.android.appconfig.RoomListConfig import io.element.android.compound.theme.ElementTheme @@ -71,10 +88,13 @@ import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.api.user.UserStatus import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.matrix.ui.model.toEmojiText import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings @@ -151,7 +171,7 @@ fun HomeTopBar( }, // We want a 16dp left padding for the navigationIcon : // 4dp from default TopAppBarHorizontalPadding - // 8dp from AccountIcon default padding (because of IconButton) + // 8dp from AccountIcon default padding (48dp touch target around 32dp avatar) // 4dp extra padding using left insets windowInsets = WindowInsets(left = 4.dp), ) @@ -316,19 +336,36 @@ private fun AccountIcon( modifier: Modifier = Modifier, ) { val testTag = if (isCurrentAccount) Modifier.testTag(TestTags.homeScreenSettings) else Modifier - IconButton( - modifier = modifier.then(testTag), - onClick = onClick, + val interactionSource = remember { MutableInteractionSource() } + Box( + modifier = modifier + .then(testTag) + .minimumInteractiveComponentSize() + .clickable( + interactionSource = interactionSource, + onClick = onClick, + indication = ripple(bounded = false), + ), + contentAlignment = Alignment.Center, ) { - Box { val avatarData by remember(matrixUser) { derivedStateOf { matrixUser.getAvatarData(size = AvatarSize.CurrentUserTopBar) } } + val statusEmoji = matrixUser.displayedStatus?.toEmojiText() + val avatarModifier = if (statusEmoji != null) { + Modifier.eraseStatusEmojiBackground( + parentSize = AvatarSize.CurrentUserTopBar.dp, + layoutDirection = LocalLayoutDirection.current, + ) + } else { + Modifier + } Avatar( avatarData = avatarData, avatarType = AvatarType.User, + modifier = avatarModifier, contentDescription = if (isCurrentAccount) { if (showAvatarIndicator) { stringResource(CommonStrings.a11y_settings_with_required_action) @@ -339,11 +376,69 @@ private fun AccountIcon( null }, ) + if (statusEmoji != null) { + StatusEmojiBadge( + emoji = statusEmoji, + modifier = Modifier.align(Alignment.BottomEnd), + ) + } if (showAvatarIndicator) { RedIndicatorAtom( modifier = Modifier.align(Alignment.TopEnd) ) } + } +} + +private val statusEmojiBadgeSize = 20.dp +private val statusEmojiBadgeRadius = statusEmojiBadgeSize / 2 +private val statusEmojiBadgeOffset = 8.dp + +private fun Modifier.eraseStatusEmojiBackground( + parentSize: Dp, + layoutDirection: LayoutDirection, +): Modifier = this + .graphicsLayer { + compositingStrategy = CompositingStrategy.Offscreen + } + .drawWithContent { + drawContent() + drawCircle( + color = Color.Black, + center = Offset( + x = if (layoutDirection == LayoutDirection.Ltr) { + (parentSize - statusEmojiBadgeRadius + statusEmojiBadgeOffset).toPx() + } else { + (statusEmojiBadgeRadius - statusEmojiBadgeOffset).toPx() + }, + y = size.height - statusEmojiBadgeRadius.toPx(), + ), + radius = statusEmojiBadgeRadius.toPx(), + blendMode = BlendMode.Clear, + ) + } + +@Composable +private fun StatusEmojiBadge( + emoji: String, + modifier: Modifier = Modifier, +) { + val density = LocalDensity.current + // Ensure the emoji size is not affected by the user's font scale settings + val fixedFontScaleDensity = remember(density.density) { + Density(density = density.density, fontScale = 1f) + } + Box( + modifier = modifier + .offset(x = statusEmojiBadgeOffset) + .size(statusEmojiBadgeSize), + contentAlignment = Alignment.Center, + ) { + CompositionLocalProvider(LocalDensity provides fixedFontScaleDensity) { + Text( + text = emoji, + style = ElementTheme.typography.fontBodyMdRegular, + ) } } } @@ -432,6 +527,33 @@ internal fun HomeTopBarWithIndicatorPreview() = ElementPreview { ) } +@OptIn(ExperimentalMaterial3Api::class) +@PreviewsDayNight +@Composable +internal fun HomeTopBarWithStatusPreview() = ElementPreview { + HomeTopBar( + selectedNavigationItem = HomeNavigationBarItem.Chats, + currentUserAndNeighbors = persistentListOf( + aMatrixUser( + id = "@id:domain", + displayName = USER_NAME_ALICE, + displayedStatus = DisplayedStatus.UserSet(UserStatus(emoji = "🌴", text = "Away")), + ) + ), + showAvatarIndicator = false, + areSearchResultsDisplayed = false, + scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()), + onOpenSettings = {}, + onAccountSwitch = {}, + onToggleSearch = {}, + canReportBug = true, + displayFilters = true, + filtersState = aRoomListFiltersState(), + spaceFiltersState = anUnselectedSpaceFiltersState(), + onMenuActionClick = {}, + ) +} + @OptIn(ExperimentalMaterial3Api::class) @PreviewsDayNight @Composable From c35a0a386420af8bae2b09b3275af0f4e73cae96 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 6 Aug 2026 11:09:49 +0200 Subject: [PATCH 014/338] Sync automatic call status with user status flag and server support --- appnav/build.gradle.kts | 1 + .../appnav/loggedin/LoggedInPresenter.kt | 13 +++++ .../appnav/loggedin/LoggedInPresenterTest.kt | 58 +++++++++++++++++++ .../libraries/matrix/api/MatrixClient.kt | 3 + .../libraries/matrix/impl/RustMatrixClient.kt | 4 ++ .../libraries/matrix/test/FakeMatrixClient.kt | 3 + 6 files changed, 82 insertions(+) diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index ce9764360b..7b86b434b9 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -60,6 +60,7 @@ dependencies { testCommonDependencies(libs) testImplementation(projects.features.login.test) testImplementation(projects.features.share.test) + testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.oauth.test) testImplementation(projects.libraries.permissions.test) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt index 0723ad4606..d46fb2f931 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt @@ -30,6 +30,8 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState @@ -65,6 +67,7 @@ class LoggedInPresenter( private val buildMeta: BuildMeta, private val networkMonitor: NetworkMonitor, private val localNetworkPermissionAdvisor: LocalNetworkPermissionAdvisor, + private val featureFlagService: FeatureFlagService, permissionsPresenterFactory: PermissionsPresenter.Factory, ) : Presenter { private val localNetworkPermissionsPresenter: PermissionsPresenter = @@ -120,6 +123,16 @@ class LoggedInPresenter( }.launchIn(this) } + LaunchedEffect(Unit) { + // Keep automatic call status (m.call) in sync with the feature flag and homeserver support. + featureFlagService.isFeatureEnabledFlow(FeatureFlags.UserStatus) + .onEach { isUserStatusEnabled -> + val enabled = isUserStatusEnabled && matrixClient.isUserStatusSupported().getOrDefault(false) + matrixClient.enableAutomaticCallStatus(enabled) + } + .launchIn(this) + } + val networkConnectivity by networkMonitor.connectivity.collectAsState() LaunchedEffect(networkConnectivity) { if (networkConnectivity == NetworkStatus.Connected) { diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt index faa19d9e61..cc330c2758 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt @@ -17,6 +17,9 @@ import im.vector.app.features.analytics.plan.UserProperties import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.encryption.EncryptionService @@ -48,6 +51,7 @@ import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.consumeItemsUntilPredicate +import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -127,6 +131,7 @@ class LoggedInPresenterTest { buildMeta = buildMeta, networkMonitor = networkMonitor, localNetworkPermissionAdvisor = FakeLocalNetworkPermissionAdvisor(), + featureFlagService = FakeFeatureFlagService(), permissionsPresenterFactory = FakePermissionsPresenterFactory(), ).test { encryptionService.emitRecoveryState(RecoveryState.UNKNOWN) @@ -346,6 +351,57 @@ class LoggedInPresenterTest { } } + @Test + fun `present - does not enable automatic call status when server does not support it`() = runTest { + val enableAutomaticCallStatusLambda = lambdaRecorder { } + val matrixClient = FakeMatrixClient( + accountManagementUrlResult = { Result.success(null) }, + enableAutomaticCallStatusLambda = enableAutomaticCallStatusLambda, + ).apply { + isUserStatusSupportedResult = Result.success(false) + } + createLoggedInPresenter( + matrixClient = matrixClient, + featureFlagService = FakeFeatureFlagService( + initialState = mapOf(FeatureFlags.UserStatus.key to true), + ), + ).test { + cancelAndConsumeRemainingEvents() + assert(enableAutomaticCallStatusLambda) + .isCalledOnce() + .with(value(false)) + } + } + + @Test + fun `present - reacts to user status flag being toggled at runtime`() = runTest { + val enableAutomaticCallStatusLambda = lambdaRecorder { } + val matrixClient = FakeMatrixClient( + accountManagementUrlResult = { Result.success(null) }, + enableAutomaticCallStatusLambda = enableAutomaticCallStatusLambda, + ).apply { + isUserStatusSupportedResult = Result.success(true) + } + val featureFlagService = FakeFeatureFlagService( + initialState = mapOf(FeatureFlags.UserStatus.key to false), + ) + createLoggedInPresenter( + matrixClient = matrixClient, + featureFlagService = featureFlagService, + ).test { + awaitItem() + // Turning the flag on at runtime enables it. + featureFlagService.setFeatureEnabled(FeatureFlags.UserStatus, true) + cancelAndConsumeRemainingEvents() + assert(enableAutomaticCallStatusLambda) + .isCalledExactly(2) + .withSequence( + listOf(value(false)), + listOf(value(true)), + ) + } + } + private suspend fun ReceiveTurbine.awaitFirstItem(): T { skipItems(1) return awaitItem() @@ -362,6 +418,7 @@ class LoggedInPresenterTest { ), buildMeta: BuildMeta = aBuildMeta(), networkMonitor: FakeNetworkMonitor = FakeNetworkMonitor(), + featureFlagService: FeatureFlagService = FakeFeatureFlagService(), ): LoggedInPresenter { return LoggedInPresenter( matrixClient = matrixClient, @@ -373,6 +430,7 @@ class LoggedInPresenterTest { buildMeta = buildMeta, networkMonitor = networkMonitor, localNetworkPermissionAdvisor = FakeLocalNetworkPermissionAdvisor(), + featureFlagService = featureFlagService, permissionsPresenterFactory = FakePermissionsPresenterFactory(), ) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 4ceacebbd7..9ea7d7fcf1 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -102,6 +102,9 @@ interface MatrixClient { /** Whether the homeserver advertises support for user status (MSC4426). */ suspend fun isUserStatusSupported(): Result + + /** Enable or disable automatically setting the user's status to "in a call" (m.call) while in a call. */ + fun enableAutomaticCallStatus(enabled: Boolean) suspend fun joinRoom(roomId: RoomId): Result suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result 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 faf1503b58..2330ef4642 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 @@ -530,6 +530,10 @@ class RustMatrixClient( } } + override fun enableAutomaticCallStatus(enabled: Boolean) { + innerClient.enableAutomaticCallStatus(enabled) + } + override suspend fun joinRoom(roomId: RoomId): Result = withContext(sessionDispatcher) { runCatchingExceptions { innerClient.joinRoomById(roomId.value).destroy() diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 56569cb1ae..77fc2db816 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -130,6 +130,7 @@ class FakeMatrixClient( private val getMapStyleUrlResult: () -> Result = { lambdaError() }, private val getDatabaseSizesLambda: () -> Result = { lambdaError() }, private val resetWellKnownConfigLambda: () -> Result = { lambdaError() }, + private val enableAutomaticCallStatusLambda: (Boolean) -> Unit = { }, override val contentScanner: ContentScanner? = null, ) : MatrixClient { var setDisplayNameCalled: Boolean = false @@ -291,6 +292,8 @@ class FakeMatrixClient( override suspend fun isUserStatusSupported(): Result = isUserStatusSupportedResult + override fun enableAutomaticCallStatus(enabled: Boolean) = enableAutomaticCallStatusLambda(enabled) + override suspend fun joinRoom(roomId: RoomId): Result = joinRoomLambda(roomId) override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result { From a7d2b86fe00fd23e83078dc008c28e5517ed8f3f Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 6 Aug 2026 16:15:44 +0200 Subject: [PATCH 015/338] Show loading and failure feedback when updating user status --- .../impl/root/PreferencesRootView.kt | 144 ++++++++++-------- .../impl/userstatus/UserStatusPresenter.kt | 12 +- .../impl/userstatus/UserStatusState.kt | 2 + .../userstatus/UserStatusStateProvider.kt | 3 + .../userstatus/UserStatusPresenterTest.kt | 39 +++++ .../components/async/AsyncActionIndicator.kt | 109 +++++++++++++ .../async/AsyncActionIndicatorTest.kt | 123 +++++++++++++++ 7 files changed, 369 insertions(+), 63 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionIndicator.kt create mode 100644 libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/component/async/AsyncActionIndicatorTest.kt diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 7ec711fdeb..2e449f5820 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -9,9 +9,12 @@ package io.element.android.features.preferences.impl.root import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -26,7 +29,10 @@ import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.user.UserPreferences import io.element.android.features.preferences.impl.userstatus.UserStatusState import io.element.android.features.preferences.impl.userstatus.UserStatusView +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage +import io.element.android.libraries.designsystem.components.async.AsyncActionIndicator +import io.element.android.libraries.designsystem.components.async.AsyncIndicator import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.PreferencePage @@ -73,72 +79,88 @@ fun PreferencesRootView( ) { val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage) - // Include pref from other modules - PreferencePage( - modifier = modifier, - onBackClick = onBackClick, - title = stringResource(id = CommonStrings.common_settings), - snackbarHost = { SnackbarHost(snackbarHostState) } - ) { - UserPreferences( - modifier = Modifier.clickable { - onOpenUserProfile(state.myUser) - }, - matrixUser = state.myUser, - ) - if (state.isMultiAccountEnabled) { - MultiAccountSection( - state = state, - onAddAccountClick = onAddAccountClick, + Box(modifier = modifier) { + // Include pref from other modules + PreferencePage( + onBackClick = onBackClick, + title = stringResource(id = CommonStrings.common_settings), + snackbarHost = { SnackbarHost(snackbarHostState) } + ) { + UserPreferences( + modifier = Modifier.clickable { + onOpenUserProfile(state.myUser) + }, + matrixUser = state.myUser, ) - } - if (state.userStatusState != null) { - UserStatusSection( - userStatusState = state.userStatusState, - emojiPickerRenderer = emojiPickerRenderer, - showTopDivider = !state.isMultiAccountEnabled, - ) - } - // 'Account' section - ManageAccountSection( - state = state, - onManageAccountClick = onManageAccountClick, - onLinkNewDeviceClick = onLinkNewDeviceClick, - onOpenBlockedUsers = onOpenBlockedUsers - ) - // 'Manage my app' section - ManageAppSection( - state = state, - onOpenNotificationSettings = onOpenNotificationSettings, - onOpenLockScreenSettings = onOpenLockScreenSettings, - onSecureBackupClick = onSecureBackupClick, - ) - - // General section - GeneralSection( - state = state, - onOpenAbout = onOpenAbout, - onOpenAnalytics = onOpenAnalytics, - onOpenRageShake = onOpenRageShake, - onOpenAdvancedSettings = onOpenAdvancedSettings, - onOpenDeveloperSettings = onOpenDeveloperSettings, - onOpenLabs = onOpenLabs, - onSignOutClick = onSignOutClick, - onDeactivateClick = onDeactivateClick, - ) - // Version - Footer( - version = state.version, - deviceId = state.deviceId, - onClick = if (!state.showDeveloperSettings) { - { state.eventSink(PreferencesRootEvent.OnVersionInfoClick) } - } else { - null + if (state.isMultiAccountEnabled) { + MultiAccountSection( + state = state, + onAddAccountClick = onAddAccountClick, + ) } - ) + if (state.userStatusState != null) { + UserStatusSection( + userStatusState = state.userStatusState, + emojiPickerRenderer = emojiPickerRenderer, + showTopDivider = !state.isMultiAccountEnabled, + ) + } + // 'Account' section + ManageAccountSection( + state = state, + onManageAccountClick = onManageAccountClick, + onLinkNewDeviceClick = onLinkNewDeviceClick, + onOpenBlockedUsers = onOpenBlockedUsers + ) + // 'Manage my app' section + ManageAppSection( + state = state, + onOpenNotificationSettings = onOpenNotificationSettings, + onOpenLockScreenSettings = onOpenLockScreenSettings, + onSecureBackupClick = onSecureBackupClick, + ) + + // General section + GeneralSection( + state = state, + onOpenAbout = onOpenAbout, + onOpenAnalytics = onOpenAnalytics, + onOpenRageShake = onOpenRageShake, + onOpenAdvancedSettings = onOpenAdvancedSettings, + onOpenDeveloperSettings = onOpenDeveloperSettings, + onOpenLabs = onOpenLabs, + onSignOutClick = onSignOutClick, + onDeactivateClick = onDeactivateClick, + ) + // Version + Footer( + version = state.version, + deviceId = state.deviceId, + onClick = if (!state.showDeveloperSettings) { + { state.eventSink(PreferencesRootEvent.OnVersionInfoClick) } + } else { + null + } + ) + } + state.userStatusState?.let { + UserStatusUpdateIndicator(it.updateStatusAction) + } } } +@Composable +private fun BoxScope.UserStatusUpdateIndicator(updateStatusAction: AsyncAction) { + AsyncActionIndicator( + asyncAction = updateStatusAction, + modifier = Modifier + .align(Alignment.TopCenter) + .statusBarsPadding(), + loading = { AsyncIndicator.Loading(text = stringResource(CommonStrings.common_saving)) }, + failure = { _ -> AsyncIndicator.Failure(text = stringResource(CommonStrings.common_failed)) }, + ) +} + @Composable private fun ColumnScope.UserStatusSection( userStatusState: UserStatusState, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenter.kt index 60f02ad05a..2358b3502d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenter.kt @@ -18,7 +18,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.runUpdatingState import io.element.android.libraries.emoji.api.picker.EmojiPickerPresenter import io.element.android.libraries.emoji.api.recentemojis.EmptyGetRecentEmojis import io.element.android.libraries.matrix.api.MatrixClient @@ -38,6 +40,7 @@ class UserStatusPresenter( var isEmojiPickerVisible by remember { mutableStateOf(false) } val customTextFieldState = rememberTextFieldState() val coroutineScope = rememberCoroutineScope() + val updateStatusAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } fun handleEvent(event: UserStatusEvent) { when (event) { @@ -47,11 +50,15 @@ class UserStatusPresenter( UserStatusEvent.CancelCustomInput -> pickerState = UserStatusPickerState.Hidden is UserStatusEvent.SetStatus -> { pickerState = UserStatusPickerState.Hidden - coroutineScope.launch { matrixClient.setUserStatus(event.status) } + coroutineScope.launch { + updateStatusAction.runUpdatingState { matrixClient.setUserStatus(event.status) } + } } UserStatusEvent.ClearStatus -> { pickerState = UserStatusPickerState.Hidden - coroutineScope.launch { matrixClient.clearUserStatus() } + coroutineScope.launch { + updateStatusAction.runUpdatingState { matrixClient.clearUserStatus() } + } } UserStatusEvent.OpenCustomInput -> { val raw = userProfile.rawStatus @@ -93,6 +100,7 @@ class UserStatusPresenter( displayedStatus = userProfile.displayedStatus, rawStatus = userProfile.rawStatus, pickerState = effectivePickerState, + updateStatusAction = updateStatusAction.value, eventSink = ::handleEvent, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusState.kt index fa225ae277..6189ee6dd0 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusState.kt @@ -11,6 +11,7 @@ import androidx.annotation.StringRes import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.runtime.Immutable import io.element.android.features.preferences.impl.R +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.emoji.api.picker.EmojiPickerState import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.api.user.UserStatus @@ -19,6 +20,7 @@ data class UserStatusState( val displayedStatus: DisplayedStatus?, val rawStatus: UserStatus?, val pickerState: UserStatusPickerState, + val updateStatusAction: AsyncAction, val eventSink: (UserStatusEvent) -> Unit, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusStateProvider.kt index 13a1d31743..6b198183a7 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusStateProvider.kt @@ -9,6 +9,7 @@ package io.element.android.features.preferences.impl.userstatus import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.api.user.UserStatus @@ -44,10 +45,12 @@ fun aUserStatusState( displayedStatus: DisplayedStatus? = null, rawStatus: UserStatus? = null, pickerState: UserStatusPickerState = UserStatusPickerState.Hidden, + updateStatusAction: AsyncAction = AsyncAction.Uninitialized, eventSink: (UserStatusEvent) -> Unit = {}, ) = UserStatusState( displayedStatus = displayedStatus, rawStatus = rawStatus, pickerState = pickerState, + updateStatusAction = updateStatusAction, eventSink = eventSink, ) diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenterTest.kt index a09afdeb6f..e9897259ae 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenterTest.kt @@ -8,9 +8,11 @@ package io.element.android.features.preferences.impl.userstatus import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.emoji.test.fakeEmojiPickerPresenterFactory import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.api.user.UserStatus +import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest @@ -61,6 +63,24 @@ class UserStatusPresenterTest { val newState = awaitItem() assertThat(newState.pickerState).isEqualTo(UserStatusPickerState.Hidden) assertThat(newState.displayedStatus).isEqualTo(DisplayedStatus.UserSet(status)) + assertThat(newState.updateStatusAction).isInstanceOf(AsyncAction.Success::class.java) + assertThat(client.setUserStatusCalled).isTrue() + } + } + + @Test + fun `Set event failure surfaces a Failure action`() = runTest { + val client = FakeMatrixClient().apply { + setUserStatusResult = Result.failure(AN_EXCEPTION) + } + createPresenter(client).test { + val status = UserStatus(emoji = "💬", text = "In a meeting") + awaitItem().eventSink(UserStatusEvent.SetStatus(status)) + val newState = awaitItem() + assertThat(newState.pickerState).isEqualTo(UserStatusPickerState.Hidden) + // On failure the SDK does not emit a new profile, so nothing is displayed. + assertThat(newState.displayedStatus).isNull() + assertThat(newState.updateStatusAction).isInstanceOf(AsyncAction.Failure::class.java) assertThat(client.setUserStatusCalled).isTrue() } } @@ -121,6 +141,25 @@ class UserStatusPresenterTest { val clearedState = awaitItem() assertThat(clearedState.pickerState).isEqualTo(UserStatusPickerState.Hidden) assertThat(clearedState.displayedStatus).isNull() + assertThat(clearedState.updateStatusAction).isInstanceOf(AsyncAction.Success::class.java) + assertThat(client.clearUserStatusCalled).isTrue() + } + } + + @Test + fun `Clear event failure surfaces a Failure action`() = runTest { + val client = FakeMatrixClient().apply { + clearUserStatusResult = Result.failure(AN_EXCEPTION) + } + createPresenter(client).test { + val status = UserStatus(emoji = "☕", text = "Be right back") + awaitItem().eventSink(UserStatusEvent.SetStatus(status)) + awaitItem().eventSink(UserStatusEvent.ClearStatus) + val clearedState = awaitItem() + assertThat(clearedState.pickerState).isEqualTo(UserStatusPickerState.Hidden) + // Clear failed, so the previously set status is still displayed. + assertThat(clearedState.displayedStatus).isEqualTo(DisplayedStatus.UserSet(status)) + assertThat(clearedState.updateStatusAction).isInstanceOf(AsyncAction.Failure::class.java) assertThat(client.clearUserStatusCalled).isTrue() } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionIndicator.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionIndicator.kt new file mode 100644 index 0000000000..058225939f --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionIndicator.kt @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.designsystem.components.async + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import io.element.android.libraries.architecture.AsyncAction +import kotlinx.coroutines.delay +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.TimeMark +import kotlin.time.TimeSource + +private val DefaultShowDelay = 300.milliseconds +private val DefaultMinDisplayDuration = 500.milliseconds +private val DefaultFailureDuration = 500.milliseconds + +/** + * An [AsyncIndicatorHost] driven by an [AsyncAction], with anti-flicker timing. + * + * While [asyncAction] is [AsyncAction.Loading], [loading] is displayed, but only if the operation + * runs longer than [showDelay]; once shown it stays visible for at least [minDisplayDuration]. This + * prevents the indicator from flashing for operations that complete almost instantly. + * + * When [asyncAction] is [AsyncAction.Failure], [failure] is displayed immediately (failures are + * never debounced) and auto-dismisses after [failureDuration]. + * + * @param asyncAction the action whose progress drives the indicator. + * @param modifier the modifier to apply to the underlying [AsyncIndicatorHost]. + * @param state the [AsyncIndicatorState] backing the host. + * @param showDelay how long the loading state must persist before the indicator is shown. + * @param minDisplayDuration the minimum duration the loading indicator stays visible once shown. + * @param failureDuration how long the failure indicator stays visible before auto-dismissing. + * @param loading the composable to display while loading. + * @param failure the composable to display on failure. + */ +@Composable +fun AsyncActionIndicator( + asyncAction: AsyncAction<*>, + modifier: Modifier = Modifier, + state: AsyncIndicatorState = rememberAsyncIndicatorState(), + showDelay: Duration = DefaultShowDelay, + minDisplayDuration: Duration = DefaultMinDisplayDuration, + failureDuration: Duration = DefaultFailureDuration, + loading: @Composable () -> Unit, + failure: @Composable (Throwable) -> Unit, +) { + AsyncIndicatorHost(modifier = modifier, state = state) + AsyncActionIndicatorEffect( + asyncAction = asyncAction, + state = state, + showDelay = showDelay, + minDisplayDuration = minDisplayDuration, + failureDuration = failureDuration, + loading = loading, + failure = failure, + ) +} + +@Composable +internal fun AsyncActionIndicatorEffect( + asyncAction: AsyncAction<*>, + state: AsyncIndicatorState, + showDelay: Duration, + minDisplayDuration: Duration, + failureDuration: Duration, + loading: @Composable () -> Unit, + failure: @Composable (Throwable) -> Unit, + timeSource: TimeSource = TimeSource.Monotonic, +) { + // Mark set when the loader actually becomes visible, null while it is hidden. + var loaderShownAt by remember { mutableStateOf(null) } + LaunchedEffect(asyncAction) { + when (asyncAction) { + is AsyncAction.Loading -> { + // If the operation completes before this delay elapses, the effect is cancelled + // and the loader is never shown. + delay(showDelay) + state.enqueue(composable = loading) + loaderShownAt = timeSource.markNow() + } + is AsyncAction.Failure -> { + // Always surface failures immediately, even if the loader was never shown. + state.enqueue(durationMs = failureDuration.inWholeMilliseconds, composable = { failure(asyncAction.error) }) + loaderShownAt = null + } + is AsyncAction.Success -> { + // Hold the loader for its minimum duration before clearing it. + loaderShownAt?.let { shownAt -> + val remaining = minDisplayDuration - shownAt.elapsedNow() + if (remaining > Duration.ZERO) delay(remaining) + } + state.clear() + loaderShownAt = null + } + else -> Unit + } + } +} diff --git a/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/component/async/AsyncActionIndicatorTest.kt b/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/component/async/AsyncActionIndicatorTest.kt new file mode 100644 index 0000000000..426aa71ce7 --- /dev/null +++ b/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/component/async/AsyncActionIndicatorTest.kt @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.designsystem.component.async + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.designsystem.components.async.AsyncActionIndicatorEffect +import io.element.android.libraries.designsystem.components.async.AsyncIndicatorState +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.test.advanceTimeBy +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.TestTimeSource +import kotlin.time.TimeSource + +@OptIn(ExperimentalCoroutinesApi::class) +class AsyncActionIndicatorTest { + private val showDelay = 300.milliseconds + private val minDisplay = 500.milliseconds + + @Test + fun `loader is not shown before the show delay elapses`() = runTest { + val state = AsyncIndicatorState() + val action = mutableStateOf>(AsyncAction.Uninitialized) + indicatorFlow(action, state).test { + action.value = AsyncAction.Loading + advanceTimeBy(100.milliseconds) + runCurrent() + assertThat(state.currentAnimationState.targetState).isFalse() + // Once the show delay has fully elapsed, the loader is shown. + advanceTimeBy(300.milliseconds) + runCurrent() + assertThat(state.currentAnimationState.targetState).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `loader is never shown when the operation completes before the show delay`() = runTest { + val state = AsyncIndicatorState() + val action = mutableStateOf>(AsyncAction.Uninitialized) + indicatorFlow(action, state).test { + action.value = AsyncAction.Loading + advanceTimeBy(100.milliseconds) + runCurrent() + action.value = AsyncAction.Success(Unit) + advanceTimeBy(1000.milliseconds) + runCurrent() + assertThat(state.currentAnimationState.targetState).isFalse() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `failure is shown immediately, without waiting for the show delay`() = runTest { + val state = AsyncIndicatorState() + val action = mutableStateOf>(AsyncAction.Uninitialized) + indicatorFlow(action, state).test { + action.value = AsyncAction.Failure(Exception("failed")) + runCurrent() + assertThat(state.currentAnimationState.targetState).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `loader stays visible for the minimum duration once shown`() = runTest { + val state = AsyncIndicatorState() + val action = mutableStateOf>(AsyncAction.Uninitialized) + val timeSource = TestTimeSource() + indicatorFlow(action, state, timeSource).test { + // Show the loader. + action.value = AsyncAction.Loading + advanceTimeBy(400.milliseconds) + runCurrent() + assertThat(state.currentAnimationState.targetState).isTrue() + // 200ms of display has already elapsed when the operation succeeds. + timeSource += 200.milliseconds + action.value = AsyncAction.Success(Unit) + runCurrent() + // Still within the minimum display duration (200ms shown, 300ms remaining) -> stays visible. + advanceTimeBy(200.milliseconds) + runCurrent() + assertThat(state.currentAnimationState.targetState).isTrue() + // Past the minimum display duration -> cleared. + advanceTimeBy(200.milliseconds) + runCurrent() + assertThat(state.currentAnimationState.targetState).isFalse() + cancelAndIgnoreRemainingEvents() + } + } + + private fun indicatorFlow( + action: State>, + state: AsyncIndicatorState, + timeSource: TimeSource = TimeSource.Monotonic, + ): Flow = moleculeFlow(RecompositionMode.Immediate) { + AsyncActionIndicatorEffect( + asyncAction = action.value, + state = state, + showDelay = showDelay, + minDisplayDuration = minDisplay, + failureDuration = minDisplay, + loading = {}, + failure = {}, + timeSource = timeSource, + ) + state.currentAnimationState.targetState + } +} From 9c66cd331f61de6e51bf1393b05ec9cf675a3f4c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 May 2026 18:09:06 +0200 Subject: [PATCH 016/338] [Link new device] Handle split steps in the SDK --- .../impl/LinkNewDeviceFlowNode.kt | 15 +++++++++ .../ContinuationMessageSender.kt | 13 ++++++++ .../api/linknewdevice/LinkDesktopHandler.kt | 2 ++ .../api/linknewdevice/LinkMobileHandler.kt | 7 ++++- .../RustContinuationMessageSender.kt | 31 +++++++++++++++++++ .../linknewdevice/RustLinkDesktopHandler.kt | 4 +++ .../linknewdevice/RustLinkMobileHandler.kt | 8 ++++- .../FakeContinuationMessageSender.kt | 25 +++++++++++++++ .../RustLinkDesktopHandlerTest.kt | 18 ++++++----- .../RustLinkMobileHandlerTest.kt | 18 ++++++----- .../android/tests/testutils/ExpectedResult.kt | 28 +++++++++++++++++ 11 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/ContinuationMessageSender.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustContinuationMessageSender.kt create mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/FakeContinuationMessageSender.kt create mode 100644 tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ExpectedResult.kt diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index d686b22860..b1492f53cc 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -54,8 +54,10 @@ import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileStep import io.element.android.libraries.matrix.api.logs.LoggerTags import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import timber.log.Timber @@ -102,6 +104,7 @@ class LinkNewDeviceFlowNode( @Suppress("AssignedValueIsNeverRead") linkDesktopHandlerJob = observeLinkNewDesktopHandler() }, + onResume = ::onResume, onDestroy = { linkMobileHandlerJob?.cancel() linkDesktopHandlerJob?.cancel() @@ -209,6 +212,18 @@ class LinkNewDeviceFlowNode( .launchIn(sessionCoroutineScope) } + private fun onResume() = sessionCoroutineScope.launch { + // Application is resumed, if the step is waiting for auth, send the confirmation + (linkNewMobileHandler.stepFlow.first() as? LinkMobileStep.WaitingForAuth)?.let { + Timber.tag(tag.value).d("Resuming while waiting for auth on mobile, sending confirmation") + it.continuationMessageSender.confirm() + } + (linkNewDesktopHandler.stepFlow.value as? LinkDesktopStep.WaitingForAuth)?.let { + Timber.tag(tag.value).d("Resuming while waiting for auth on desktop, sending confirmation") + it.continuationMessageSender.confirm() + } + } + private fun navigateToError(errorType: ErrorType) { // Map the error to an error screen val error = when (errorType) { diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/ContinuationMessageSender.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/ContinuationMessageSender.kt new file mode 100644 index 0000000000..0bbbaf3187 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/ContinuationMessageSender.kt @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api.linknewdevice + +interface ContinuationMessageSender { + suspend fun cancel(): Result + suspend fun confirm(): Result +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkDesktopHandler.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkDesktopHandler.kt index d600c1a8bf..343624aa34 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkDesktopHandler.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkDesktopHandler.kt @@ -18,8 +18,10 @@ interface LinkDesktopHandler { sealed interface LinkDesktopStep { data object Uninitialized : LinkDesktopStep data object Starting : LinkDesktopStep + data class WaitingForAuth( val verificationUri: String, + val continuationMessageSender: ContinuationMessageSender, ) : LinkDesktopStep data class EstablishingSecureChannel( diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt index eb2d49b5b0..f187b8275a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt @@ -22,7 +22,12 @@ sealed interface LinkMobileStep { data object Starting : LinkMobileStep data class QrReady(val data: String) : LinkMobileStep data object QrRotating : LinkMobileStep - data class WaitingForAuth(val verificationUri: String) : LinkMobileStep + + data class WaitingForAuth( + val verificationUri: String, + val continuationMessageSender: ContinuationMessageSender, + ) : LinkMobileStep + data class QrScanned(val checkCodeSender: CheckCodeSender) : LinkMobileStep data class Error(val errorType: ErrorType) : LinkMobileStep data object SyncingSecrets : LinkMobileStep diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustContinuationMessageSender.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustContinuationMessageSender.kt new file mode 100644 index 0000000000..98cf29418c --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustContinuationMessageSender.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.linknewdevice + +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.matrix.api.linknewdevice.ContinuationMessageSender +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext +import org.matrix.rustcomponents.sdk.ContinuationMessageSender as FfiContinuationMessageSender + +class RustContinuationMessageSender( + private val inner: FfiContinuationMessageSender, + private val sessionDispatcher: CoroutineDispatcher, +) : ContinuationMessageSender { + override suspend fun cancel(): Result = withContext(sessionDispatcher) { + runCatchingExceptions { + inner.cancel() + } + } + + override suspend fun confirm(): Result = withContext(sessionDispatcher) { + runCatchingExceptions { + inner.confirm() + } + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandler.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandler.kt index 83e657eb07..8c9695ae4b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandler.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandler.kt @@ -75,6 +75,10 @@ class RustLinkDesktopHandler( GrantQrLoginProgress.SyncingSecrets -> LinkDesktopStep.SyncingSecrets is GrantQrLoginProgress.WaitingForAuth -> LinkDesktopStep.WaitingForAuth( verificationUri = verificationUri, + continuationMessageSender = RustContinuationMessageSender( + inner = continuationSender, + sessionDispatcher = sessionDispatcher, + ) ) is GrantQrLoginProgress.EstablishingSecureChannel -> LinkDesktopStep.EstablishingSecureChannel( checkCode = checkCode, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt index a44a8d5943..33f1ad736d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt @@ -84,7 +84,13 @@ class RustLinkMobileHandler( ) GrantGeneratedQrLoginProgress.Starting -> LinkMobileStep.Starting GrantGeneratedQrLoginProgress.SyncingSecrets -> LinkMobileStep.SyncingSecrets - is GrantGeneratedQrLoginProgress.WaitingForAuth -> LinkMobileStep.WaitingForAuth(verificationUri) + is GrantGeneratedQrLoginProgress.WaitingForAuth -> LinkMobileStep.WaitingForAuth( + verificationUri = verificationUri, + continuationMessageSender = RustContinuationMessageSender( + inner = continuationSender, + sessionDispatcher = sessionDispatcher, + ) + ) } } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/FakeContinuationMessageSender.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/FakeContinuationMessageSender.kt new file mode 100644 index 0000000000..d1544ec8bb --- /dev/null +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/FakeContinuationMessageSender.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.linknewdevice + +import io.element.android.libraries.matrix.api.linknewdevice.ContinuationMessageSender +import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.tests.testutils.simulateLongTask + +class FakeContinuationMessageSender( + private val cancelResult: () -> Result = { lambdaError() }, + private val confirmResult: () -> Result = { lambdaError() }, +) : ContinuationMessageSender { + override suspend fun cancel(): Result = simulateLongTask { + cancelResult() + } + + override suspend fun confirm(): Result = simulateLongTask { + confirmResult() + } +} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandlerTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandlerTest.kt index c0781972ac..8a0a046946 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandlerTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandlerTest.kt @@ -16,6 +16,8 @@ import io.element.android.libraries.matrix.api.linknewdevice.LinkDesktopStep import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiContinuationMessageSender import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiGrantLoginWithQrCodeHandler import io.element.android.libraries.matrix.test.QR_CODE_DATA +import io.element.android.tests.testutils.ExpectedResult +import io.element.android.tests.testutils.match import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch @@ -50,16 +52,18 @@ class RustLinkDesktopHandlerTest { runCurrent() // progress from the handler is mapped and emitted listOf( - GrantQrLoginProgress.Starting to LinkDesktopStep.Starting, - GrantQrLoginProgress.SyncingSecrets to LinkDesktopStep.SyncingSecrets, + GrantQrLoginProgress.Starting to ExpectedResult(LinkDesktopStep.Starting), + GrantQrLoginProgress.SyncingSecrets to ExpectedResult(LinkDesktopStep.SyncingSecrets), GrantQrLoginProgress.WaitingForAuth("aVerificationUri", FakeFfiContinuationMessageSender()) - to LinkDesktopStep.WaitingForAuth("aVerificationUri"), + to ExpectedResult( + resultClass = LinkDesktopStep.WaitingForAuth::class.java, + ), GrantQrLoginProgress.EstablishingSecureChannel(1.toUByte(), "1") - to LinkDesktopStep.EstablishingSecureChannel(1.toUByte(), "1"), - GrantQrLoginProgress.Done to LinkDesktopStep.Done, - ).forEach { (progress, expectedStep) -> + to ExpectedResult(LinkDesktopStep.EstablishingSecureChannel(1.toUByte(), "1")), + GrantQrLoginProgress.Done to ExpectedResult(LinkDesktopStep.Done), + ).forEach { (progress, expectedResult) -> handler.emitScanProgress(progress) - assertThat(awaitItem()).isEqualTo(expectedStep) + assertThat(awaitItem()).match(expectedResult) } // scan returns, no new event is emitted completable.complete(Unit) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt index 8c0306c8ad..f1f568e3ca 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt @@ -18,6 +18,8 @@ import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiContinuati import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiGrantLoginWithQrCodeHandler import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiQrCodeData import io.element.android.libraries.matrix.test.QR_CODE_DATA_RECIPROCATE +import io.element.android.tests.testutils.ExpectedResult +import io.element.android.tests.testutils.match import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch @@ -51,18 +53,18 @@ class RustLinkMobileHandlerTest { runCurrent() // progress from the handler is mapped and emitted listOf( - GrantGeneratedQrLoginProgress.Starting to LinkMobileStep.Starting::class.java, - GrantGeneratedQrLoginProgress.SyncingSecrets to LinkMobileStep.SyncingSecrets::class.java, + GrantGeneratedQrLoginProgress.Starting to ExpectedResult(LinkMobileStep.Starting), + GrantGeneratedQrLoginProgress.SyncingSecrets to ExpectedResult(LinkMobileStep.SyncingSecrets), GrantGeneratedQrLoginProgress.WaitingForAuth("aVerificationUri", FakeFfiContinuationMessageSender()) - to LinkMobileStep.WaitingForAuth::class.java, + to ExpectedResult(LinkMobileStep.WaitingForAuth::class.java), GrantGeneratedQrLoginProgress.QrScanned(FakeFfiCheckCodeSender()) - to LinkMobileStep.QrScanned::class.java, + to ExpectedResult(LinkMobileStep.QrScanned::class.java), GrantGeneratedQrLoginProgress.QrReady(FakeFfiQrCodeData(toBytesResult = { QR_CODE_DATA_RECIPROCATE })) - to LinkMobileStep.QrReady::class.java, - GrantGeneratedQrLoginProgress.Done to LinkMobileStep.Done::class.java, - ).forEach { (progress, expectedStepClass) -> + to ExpectedResult(LinkMobileStep.QrReady::class.java), + GrantGeneratedQrLoginProgress.Done to ExpectedResult(LinkMobileStep.Done), + ).forEach { (progress, expectedResult) -> handler.emitGenerateProgress(progress) - assertThat(awaitItem()).isInstanceOf(expectedStepClass) + assertThat(awaitItem()).match(expectedResult) } // generate returns, no new event is emitted completable.complete(Unit) diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ExpectedResult.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ExpectedResult.kt new file mode 100644 index 0000000000..b5e383f324 --- /dev/null +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ExpectedResult.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.tests.testutils + +import com.google.common.truth.Subject + +/** + * A helper class to assert expected results in tests. It can be used to assert either the type of result or its value. + */ +data class ExpectedResult( + val resultClass: Class, + val result: T? = null, +) { + constructor(result: T) : this(result::class.java, result) +} + +fun Subject.match(expectedResult: ExpectedResult) { + if (expectedResult.result != null) { + isEqualTo(expectedResult.result) + } else { + isInstanceOf(expectedResult.resultClass) + } +} From 0e563332ce06a7b9a750c4c5c8917929b9dc6f9a Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 6 Aug 2026 16:16:13 +0200 Subject: [PATCH 017/338] Clean up formatting/detekt --- .../components/async/AsyncActionIndicator.kt | 17 +++++++++++------ .../matrix/api/user/DisplayedStatus.kt | 1 - .../matrix/impl/room/JoinedRustRoom.kt | 2 +- .../android/tests/konsist/KonsistPreviewTest.kt | 1 + 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionIndicator.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionIndicator.kt index 058225939f..f1a58c243f 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionIndicator.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionIndicator.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import io.element.android.libraries.architecture.AsyncAction @@ -36,24 +37,24 @@ private val DefaultFailureDuration = 500.milliseconds * never debounced) and auto-dismisses after [failureDuration]. * * @param asyncAction the action whose progress drives the indicator. + * @param loading the composable to display while loading. + * @param failure the composable to display on failure. * @param modifier the modifier to apply to the underlying [AsyncIndicatorHost]. * @param state the [AsyncIndicatorState] backing the host. * @param showDelay how long the loading state must persist before the indicator is shown. * @param minDisplayDuration the minimum duration the loading indicator stays visible once shown. * @param failureDuration how long the failure indicator stays visible before auto-dismissing. - * @param loading the composable to display while loading. - * @param failure the composable to display on failure. */ @Composable fun AsyncActionIndicator( asyncAction: AsyncAction<*>, + loading: @Composable () -> Unit, + failure: @Composable (Throwable) -> Unit, modifier: Modifier = Modifier, state: AsyncIndicatorState = rememberAsyncIndicatorState(), showDelay: Duration = DefaultShowDelay, minDisplayDuration: Duration = DefaultMinDisplayDuration, failureDuration: Duration = DefaultFailureDuration, - loading: @Composable () -> Unit, - failure: @Composable (Throwable) -> Unit, ) { AsyncIndicatorHost(modifier = modifier, state = state) AsyncActionIndicatorEffect( @@ -78,6 +79,9 @@ internal fun AsyncActionIndicatorEffect( failure: @Composable (Throwable) -> Unit, timeSource: TimeSource = TimeSource.Monotonic, ) { + // Referenced inside the restarting LaunchedEffect below, so keep them up to date. + val currentLoading by rememberUpdatedState(loading) + val currentFailure by rememberUpdatedState(failure) // Mark set when the loader actually becomes visible, null while it is hidden. var loaderShownAt by remember { mutableStateOf(null) } LaunchedEffect(asyncAction) { @@ -86,12 +90,13 @@ internal fun AsyncActionIndicatorEffect( // If the operation completes before this delay elapses, the effect is cancelled // and the loader is never shown. delay(showDelay) - state.enqueue(composable = loading) + state.enqueue(composable = currentLoading) loaderShownAt = timeSource.markNow() } is AsyncAction.Failure -> { // Always surface failures immediately, even if the loader was never shown. - state.enqueue(durationMs = failureDuration.inWholeMilliseconds, composable = { failure(asyncAction.error) }) + val error = asyncAction.error + state.enqueue(durationMs = failureDuration.inWholeMilliseconds, composable = { currentFailure(error) }) loaderShownAt = null } is AsyncAction.Success -> { diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/DisplayedStatus.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/DisplayedStatus.kt index 64cf68cda7..8e4ced7202 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/DisplayedStatus.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/DisplayedStatus.kt @@ -13,7 +13,6 @@ import kotlinx.parcelize.Parcelize @Immutable sealed interface DisplayedStatus : Parcelable { - /** Useful for adding extension methods. */ companion object diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt index db4cd5f0db..d454d7c775 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt @@ -42,7 +42,6 @@ import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings import io.element.android.libraries.matrix.impl.core.RustSendHandle import io.element.android.libraries.matrix.impl.mapper.map -import io.element.android.libraries.matrix.impl.user.map import io.element.android.libraries.matrix.impl.room.history.map import io.element.android.libraries.matrix.impl.room.join.map import io.element.android.libraries.matrix.impl.room.knock.RustKnockRequest @@ -54,6 +53,7 @@ import io.element.android.libraries.matrix.impl.room.threads.RustThreadsListServ import io.element.android.libraries.matrix.impl.roomdirectory.map import io.element.android.libraries.matrix.impl.timeline.RustTimeline import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper +import io.element.android.libraries.matrix.impl.user.map import io.element.android.libraries.matrix.impl.util.MessageEventContent import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import io.element.android.libraries.matrix.impl.widget.RustWidgetDriver diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt index e2a387eede..e066a6b7ec 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt @@ -98,6 +98,7 @@ class KonsistPreviewTest { "HomeTopBarSpaceFiltersSelectedPreview", "HomeTopBarSpacesPreview", "HomeTopBarWithIndicatorPreview", + "HomeTopBarWithStatusPreview", "IconsOtherPreview", "MarkdownTextComposerEditPreview", "MatrixBadgeAtomInfoPreview", From df6ccd9dc30312b766f41e22685e36756e60c5bd Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Thu, 6 Aug 2026 11:31:25 -0700 Subject: [PATCH 018/338] Consolidate account provider selection into a single editable screen --- .../features/login/impl/LoginFlowNode.kt | 43 ----- .../ConfirmAccountProviderEvents.kt | 1 + .../ConfirmAccountProviderNode.kt | 2 - .../ConfirmAccountProviderPresenter.kt | 46 +++++- .../ConfirmAccountProviderState.kt | 14 +- .../ConfirmAccountProviderStateProvider.kt | 10 +- .../ConfirmAccountProviderView.kt | 128 ++++++++++----- .../ConfirmAccountProviderPresenterTest.kt | 148 +++++++++--------- .../src/main/res/values/temporary.xml | 5 + 9 files changed, 226 insertions(+), 171 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index 978d28dfa3..7437051e01 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -23,7 +23,6 @@ import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.replace -import com.bumble.appyx.navmodel.backstack.operation.singleTop import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject @@ -33,14 +32,12 @@ import io.element.android.features.login.api.LoginEntryPoint import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.classic.ElementClassicConnection import io.element.android.features.login.impl.qrcode.QrCodeLoginFlowNode -import io.element.android.features.login.impl.screens.changeaccountprovider.ChangeAccountProviderNode import io.element.android.features.login.impl.screens.chooseaccountprovider.ChooseAccountProviderNode import io.element.android.features.login.impl.screens.classic.ClassicFlowNode import io.element.android.features.login.impl.screens.confirmaccountprovider.ConfirmAccountProviderNode import io.element.android.features.login.impl.screens.createaccount.CreateAccountNode import io.element.android.features.login.impl.screens.loginpassword.LoginPasswordNode import io.element.android.features.login.impl.screens.onboarding.OnBoardingNode -import io.element.android.features.login.impl.screens.searchaccountprovider.SearchAccountProviderNode import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.architecture.BackstackView @@ -130,12 +127,6 @@ class LoginFlowNode( @Parcelize data object ChooseAccountProvider : NavTarget - @Parcelize - data object ChangeAccountProvider : NavTarget - - @Parcelize - data object SearchAccountProvider : NavTarget - @Parcelize data class LoginPassword( val initialLogin: String = "", @@ -281,43 +272,9 @@ class LoginFlowNode( override fun navigateToLoginPassword() { backstack.push(NavTarget.LoginPassword()) } - - override fun navigateToChangeAccountProvider() { - backstack.push(NavTarget.ChangeAccountProvider) - } } createNode(buildContext, plugins = listOf(inputs, callback)) } - NavTarget.ChangeAccountProvider -> { - val callback = object : ChangeAccountProviderNode.Callback { - override fun onDone() { - // Go back to the Account Provider screen - val confirmAccountProvider = backstack.elements.value.firstOrNull { - it.key.navTarget is NavTarget.ConfirmAccountProvider - }?.key?.navTarget ?: NavTarget.ConfirmAccountProvider(isAccountCreation = false) - backstack.singleTop(confirmAccountProvider) - } - - override fun navigateToSearchAccountProvider() { - backstack.push(NavTarget.SearchAccountProvider) - } - } - - createNode(buildContext, plugins = listOf(callback)) - } - NavTarget.SearchAccountProvider -> { - val callback = object : SearchAccountProviderNode.Callback { - override fun onDone() { - // Go back to the Account Provider screen - val confirmAccountProvider = backstack.elements.value.firstOrNull { - it.key.navTarget is NavTarget.ConfirmAccountProvider - }?.key?.navTarget ?: NavTarget.ConfirmAccountProvider(isAccountCreation = false) - backstack.singleTop(confirmAccountProvider) - } - } - - createNode(buildContext, plugins = listOf(callback)) - } is NavTarget.LoginPassword -> { val inputs = LoginPasswordNode.Inputs( initialLogin = navTarget.initialLogin, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderEvents.kt index d66606b22c..1fb024156b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderEvents.kt @@ -9,6 +9,7 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider sealed interface ConfirmAccountProviderEvents { + data class UserInputChanged(val accountProvider: String) : ConfirmAccountProviderEvents data object Continue : ConfirmAccountProviderEvents data object ClearError : ConfirmAccountProviderEvents } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt index 928a493dc1..417b0d4850 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt @@ -46,7 +46,6 @@ class ConfirmAccountProviderNode( fun navigateToLoginPassword() fun navigateToOAuth(oAuthDetails: OAuthDetails) fun navigateToCreateAccount(url: String) - fun navigateToChangeAccountProvider() } private val callback: Callback = callback() @@ -61,7 +60,6 @@ class ConfirmAccountProviderNode( onOAuthDetails = callback::navigateToOAuth, onNeedLoginPassword = callback::navigateToLoginPassword, onCreateAccountContinue = callback::navigateToCreateAccount, - onChange = callback::navigateToChangeAccountProvider, onLearnMoreClick = { openLearnMorePage(context) }, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt index 0111b1e7d7..de6ec28774 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt @@ -9,14 +9,23 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject +import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.features.login.impl.changeserver.ChangeServerEvents +import io.element.android.features.login.impl.changeserver.ChangeServerState import io.element.android.features.login.impl.login.LoginModeEvent import io.element.android.features.login.impl.login.LoginModeState +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @AssistedInject @@ -24,6 +33,7 @@ class ConfirmAccountProviderPresenter( @Assisted private val params: Params, private val accountProviderDataSource: AccountProviderDataSource, private val loginModePresenter: Presenter, + private val changeServerPresenter: Presenter, ) : Presenter { data class Params( val isAccountCreation: Boolean, @@ -38,25 +48,49 @@ class ConfirmAccountProviderPresenter( override fun present(): ConfirmAccountProviderState { val accountProvider by accountProviderDataSource.flow.collectAsState() val loginModeState = loginModePresenter.present() + val changeServerState = changeServerPresenter.present() - fun handleEvent(event: ConfirmAccountProviderEvents) { - when (event) { - ConfirmAccountProviderEvents.Continue -> loginModeState.eventSink( + // Editable input, seeded from the current (history-defaulted) account provider until the user edits it. + var userInput by rememberSaveable { mutableStateOf(null) } + val accountProviderInput = userInput ?: accountProvider.url + val latestAccountProviderInput by rememberUpdatedState(accountProviderInput) + + // Once the chosen account provider has been validated and persisted, proceed with the actual sign in / sign up. + LaunchedEffect(changeServerState.changeServerAction) { + if (changeServerState.changeServerAction is AsyncData.Success) { + loginModeState.eventSink( LoginModeEvent.Submit( isAccountCreation = params.isAccountCreation, - homeserverUrl = accountProvider.url, + homeserverUrl = latestAccountProviderInput.trim(), resolvedHomeserverUrl = null, loginHint = null, ) ) - ConfirmAccountProviderEvents.ClearError -> loginModeState.eventSink(LoginModeEvent.ClearError) + } + } + + fun handleEvent(event: ConfirmAccountProviderEvents) { + when (event) { + is ConfirmAccountProviderEvents.UserInputChanged -> { + userInput = event.accountProvider + } + // Validate (and persist) the chosen account provider before proceeding. This also enforces the + // enterprise account-provider access control, which the login submit does not do on its own. + ConfirmAccountProviderEvents.Continue -> changeServerState.eventSink( + ChangeServerEvents.ChangeServer(AccountProvider(url = accountProviderInput.trim())) + ) + ConfirmAccountProviderEvents.ClearError -> { + loginModeState.eventSink(LoginModeEvent.ClearError) + changeServerState.eventSink(ChangeServerEvents.ClearError) + } } } return ConfirmAccountProviderState( - accountProvider = accountProvider, + accountProviderInput = accountProviderInput, isAccountCreation = params.isAccountCreation, loginModeState = loginModeState, + changeServerState = changeServerState, eventSink = ::handleEvent, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt index 6e6deb8c2c..0c211341ef 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt @@ -8,17 +8,23 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider -import io.element.android.features.login.impl.accountprovider.AccountProvider +import io.element.android.features.login.impl.changeserver.ChangeServerState import io.element.android.features.login.impl.login.LoginModeState import io.element.android.libraries.architecture.AsyncData data class ConfirmAccountProviderState( - val accountProvider: AccountProvider, + val accountProviderInput: String, val isAccountCreation: Boolean, val loginModeState: LoginModeState, + val changeServerState: ChangeServerState, val eventSink: (ConfirmAccountProviderEvents) -> Unit ) { val submitEnabled: Boolean - get() = accountProvider.url.isNotEmpty() && - (loginModeState.loginMode is AsyncData.Uninitialized || loginModeState.loginMode is AsyncData.Loading) + get() = accountProviderInput.isNotBlank() && + loginModeState.loginMode is AsyncData.Uninitialized && + changeServerState.changeServerAction is AsyncData.Uninitialized + + val isLoading: Boolean + get() = loginModeState.loginMode is AsyncData.Loading || + changeServerState.changeServerAction is AsyncData.Loading } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt index f597ad6b54..73c5e5f550 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt @@ -9,8 +9,8 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.login.impl.accountprovider.AccountProvider -import io.element.android.features.login.impl.accountprovider.anAccountProvider +import io.element.android.features.login.impl.changeserver.ChangeServerState +import io.element.android.features.login.impl.changeserver.aChangeServerState import io.element.android.features.login.impl.login.LoginModeState import io.element.android.features.login.impl.login.aLoginModeState import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported @@ -31,13 +31,15 @@ open class ConfirmAccountProviderStateProvider : PreviewParameterProvider Unit = {}, ) = ConfirmAccountProviderState( - accountProvider = accountProvider, + accountProviderInput = accountProviderInput, isAccountCreation = isAccountCreation, loginModeState = loginModeState, + changeServerState = changeServerState, eventSink = eventSink ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index 95fbff25ab..3a7d2831c8 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -8,29 +8,38 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.login.impl.R +import io.element.android.features.login.impl.changeserver.ChangeServerView import io.element.android.features.login.impl.login.LoginModeEvent import io.element.android.features.login.impl.login.LoginModeView -import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.BigIcon +import io.element.android.libraries.designsystem.components.form.textFieldState +import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusNext 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.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.matrix.api.auth.OAuthDetails import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionDialogView import io.element.android.libraries.testtags.TestTags @@ -44,14 +53,8 @@ fun ConfirmAccountProviderView( onNeedLoginPassword: () -> Unit, onLearnMoreClick: () -> Unit, onCreateAccountContinue: (url: String) -> Unit, - onChange: () -> Unit, modifier: Modifier = Modifier, ) { - val isLoading by remember(state.loginModeState.loginMode) { - derivedStateOf { - state.loginModeState.loginMode is AsyncData.Loading - } - } val eventSink = state.eventSink HeaderFooterPage( @@ -62,55 +65,97 @@ fun ConfirmAccountProviderView( iconStyle = BigIcon.Style.Default(CompoundIcons.UserProfileSolid()), title = stringResource( id = if (state.isAccountCreation) { - R.string.screen_account_provider_signup_title + CommonStrings.screen_select_server_title_register } else { - R.string.screen_account_provider_signin_title - }, - state.accountProvider.title + CommonStrings.screen_select_server_title_login + } ), - subTitle = stringResource( - id = if (state.isAccountCreation) { - R.string.screen_account_provider_signup_subtitle - } else { - R.string.screen_account_provider_signin_subtitle - }, - ) + subTitle = null, ) }, footer = { ButtonColumnMolecule { Button( text = stringResource(id = CommonStrings.action_continue), - showProgress = isLoading, - onClick = { eventSink.invoke(ConfirmAccountProviderEvents.Continue) }, - enabled = state.submitEnabled || isLoading, + showProgress = state.isLoading, + onClick = { eventSink(ConfirmAccountProviderEvents.Continue) }, + enabled = state.submitEnabled && !state.isLoading, modifier = Modifier .fillMaxWidth() .testTag(TestTags.loginContinue) ) - TextButton( - text = stringResource(id = R.string.screen_account_provider_change), - onClick = onChange, - enabled = !isLoading, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.loginChangeServer) - ) } } ) { - LoginModeView( - loginMode = state.loginModeState.loginMode, - onClearError = { - eventSink(ConfirmAccountProviderEvents.ClearError) + var input by textFieldState(stateValue = state.accountProviderInput) + val focusManager = LocalFocusManager.current + TextField( + value = input, + onValueChange = { + input = it + eventSink(ConfirmAccountProviderEvents.UserInputChanged(it)) + }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 40.dp) + .onTabOrEnterKeyFocusNext(focusManager) + .testTag(TestTags.changeServerServer), + label = stringResource(id = CommonStrings.screen_select_server_textfield_header), + supportingText = stringResource( + id = if (state.isAccountCreation) { + CommonStrings.screen_select_server_textfield_footer_register + } else { + CommonStrings.screen_select_server_textfield_footer_login + } + ), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Uri, + imeAction = ImeAction.Done, + ), + keyboardActions = KeyboardActions(onDone = { + eventSink(ConfirmAccountProviderEvents.Continue) + }), + singleLine = true, + trailingIcon = if (input.isNotEmpty()) { + { + Box( + Modifier.clickable( + onClickLabel = stringResource(CommonStrings.action_clear), + role = Role.Button, + ) { + input = "" + eventSink(ConfirmAccountProviderEvents.UserInputChanged("")) + } + ) { + Icon( + imageVector = CompoundIcons.Close(), + contentDescription = stringResource(CommonStrings.action_clear) + ) + } + } + } else { + null }, - onLearnMoreClick = onLearnMoreClick, - onOAuthDetails = onOAuthDetails, - onNeedLoginPassword = onNeedLoginPassword, - onCreateAccountContinue = onCreateAccountContinue, ) } + // Renders the account-provider validation errors, progress and permission dialogs. + // The successful validation itself is observed by the presenter, which then proceeds with the login flow. + ChangeServerView( + state = state.changeServerState, + onLearnMoreClick = onLearnMoreClick, + onSuccess = {}, + ) + + LoginModeView( + loginMode = state.loginModeState.loginMode, + onClearError = { eventSink(ConfirmAccountProviderEvents.ClearError) }, + onLearnMoreClick = onLearnMoreClick, + onOAuthDetails = onOAuthDetails, + onNeedLoginPassword = onNeedLoginPassword, + onCreateAccountContinue = onCreateAccountContinue, + ) + LocalNetworkPermissionDialogView( dialog = state.loginModeState.localNetworkPermissionDialog, onSubmit = { @@ -133,6 +178,5 @@ internal fun ConfirmAccountProviderViewPreview( onNeedLoginPassword = {}, onCreateAccountContinue = {}, onLearnMoreClick = {}, - onChange = {}, ) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index ce8e60c73f..0257d68ea9 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -8,15 +8,21 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider +import app.cash.turbine.ReceiveTurbine import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig +import io.element.android.features.enterprise.test.FakeEnterpriseService +import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource +import io.element.android.features.login.impl.changeserver.ChangeServerPresenter +import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate import io.element.android.features.login.impl.login.LoginMode import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported import io.element.android.features.login.impl.screens.onboarding.createLoginModePresenter import io.element.android.features.login.impl.web.FakeWebClientUrlForAuthenticationRetriever import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationRetriever +import io.element.android.features.wellknown.test.FakeWellknownRetriever import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.test.AN_EXCEPTION @@ -25,6 +31,9 @@ import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails import io.element.android.libraries.oauth.api.OAuthAction import io.element.android.libraries.oauth.api.OAuthActionFlow import io.element.android.libraries.oauth.test.customtab.FakeOAuthActionFlow +import io.element.android.libraries.permissions.test.FakeLocalNetworkPermissionAdvisor +import io.element.android.libraries.permissions.test.FakePermissionsPresenter +import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest @@ -42,7 +51,7 @@ class ConfirmAccountProviderPresenterTest { val initialState = awaitItem() assertThat(initialState.isAccountCreation).isFalse() assertThat(initialState.submitEnabled).isTrue() - assertThat(initialState.accountProvider.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) + assertThat(initialState.accountProviderInput).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) assertThat(initialState.loginModeState.loginMode).isEqualTo(AsyncData.Uninitialized) } } @@ -60,13 +69,9 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) - val loadingState = awaitItem() - assertThat(loadingState.submitEnabled).isTrue() - assertThat(loadingState.loginModeState.loginMode).isInstanceOf(AsyncData.Loading::class.java) - val successState = awaitItem() - assertThat(successState.submitEnabled).isFalse() - assertThat(successState.loginModeState.loginMode).isInstanceOf(AsyncData.Success::class.java) + val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isEqualTo(LoginMode.PasswordLogin) + cancelAndIgnoreRemainingEvents() } } @@ -83,13 +88,9 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) - val loadingState = awaitItem() - assertThat(loadingState.submitEnabled).isTrue() - assertThat(loadingState.loginModeState.loginMode).isInstanceOf(AsyncData.Loading::class.java) - val successState = awaitItem() - assertThat(successState.submitEnabled).isFalse() - assertThat(successState.loginModeState.loginMode).isInstanceOf(AsyncData.Success::class.java) + val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) + cancelAndIgnoreRemainingEvents() } } @@ -108,17 +109,13 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) - val loadingState = awaitItem() - assertThat(loadingState.submitEnabled).isTrue() - assertThat(loadingState.loginModeState.loginMode).isInstanceOf(AsyncData.Loading::class.java) - val successState = awaitItem() - assertThat(successState.submitEnabled).isFalse() - assertThat(successState.loginModeState.loginMode).isInstanceOf(AsyncData.Success::class.java) + val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) authenticationService.givenOAuthCancelError(AN_EXCEPTION) defaultOAuthActionFlow.post(OAuthAction.GoBack()) - val cancelFailureState = awaitItem() + val cancelFailureState = awaitLoginMode { it is AsyncData.Failure } assertThat(cancelFailureState.loginModeState.loginMode).isInstanceOf(AsyncData.Failure::class.java) + cancelAndIgnoreRemainingEvents() } } @@ -137,16 +134,12 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) - val loadingState = awaitItem() - assertThat(loadingState.submitEnabled).isTrue() - assertThat(loadingState.loginModeState.loginMode).isInstanceOf(AsyncData.Loading::class.java) - val successState = awaitItem() - assertThat(successState.submitEnabled).isFalse() - assertThat(successState.loginModeState.loginMode).isInstanceOf(AsyncData.Success::class.java) + val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) defaultOAuthActionFlow.post(OAuthAction.GoBack()) - val cancelFinalState = awaitItem() + val cancelFinalState = awaitLoginMode { it is AsyncData.Uninitialized } assertThat(cancelFinalState.loginModeState.loginMode).isInstanceOf(AsyncData.Uninitialized::class.java) + cancelAndIgnoreRemainingEvents() } } @@ -165,12 +158,11 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) - val loadingState = awaitItem() - assertThat(loadingState.submitEnabled).isTrue() - assertThat(loadingState.loginModeState.loginMode).isInstanceOf(AsyncData.Loading::class.java) + awaitLoginMode { it is AsyncData.Loading } defaultOAuthActionFlow.post(OAuthAction.GoBack(toUnblock = true)) - val cancelFinalState = awaitItem() + val cancelFinalState = awaitLoginMode { it is AsyncData.Uninitialized } assertThat(cancelFinalState.loginModeState.loginMode).isInstanceOf(AsyncData.Uninitialized::class.java) + cancelAndIgnoreRemainingEvents() } } @@ -189,19 +181,13 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) - val loadingState = awaitItem() - assertThat(loadingState.submitEnabled).isTrue() - assertThat(loadingState.loginModeState.loginMode).isInstanceOf(AsyncData.Loading::class.java) - val successState = awaitItem() - assertThat(successState.submitEnabled).isFalse() - assertThat(successState.loginModeState.loginMode).isInstanceOf(AsyncData.Success::class.java) + val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) authenticationService.givenLoginError(AN_EXCEPTION) defaultOAuthActionFlow.post(OAuthAction.Success("aUrl")) - val cancelLoadingState = awaitItem() - assertThat(cancelLoadingState.loginModeState.loginMode).isInstanceOf(AsyncData.Loading::class.java) - val cancelFailureState = awaitItem() + val cancelFailureState = awaitLoginMode { it is AsyncData.Failure } assertThat(cancelFailureState.loginModeState.loginMode).isInstanceOf(AsyncData.Failure::class.java) + cancelAndIgnoreRemainingEvents() } } @@ -220,16 +206,12 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) - val loadingState = awaitItem() - assertThat(loadingState.submitEnabled).isTrue() - assertThat(loadingState.loginModeState.loginMode).isInstanceOf(AsyncData.Loading::class.java) - val successState = awaitItem() - assertThat(successState.submitEnabled).isFalse() - assertThat(successState.loginModeState.loginMode).isInstanceOf(AsyncData.Success::class.java) + val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) defaultOidcActionFlow.post(OAuthAction.Success("aUrl")) - val successSuccessState = awaitItem() + val successSuccessState = awaitLoginMode { it is AsyncData.Loading } assertThat(successSuccessState.loginModeState.loginMode).isInstanceOf(AsyncData.Loading::class.java) + cancelAndIgnoreRemainingEvents() } } @@ -246,10 +228,10 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) - skipItems(1) // Loading - val failureState = awaitItem() - assertThat(failureState.submitEnabled).isFalse() - assertThat(failureState.loginModeState.loginMode).isInstanceOf(AsyncData.Failure::class.java) + // The account provider validation fails, so the login is never attempted. + val failureState = awaitState { it.changeServerState.changeServerAction is AsyncData.Failure } + assertThat(failureState.loginModeState.loginMode).isEqualTo(AsyncData.Uninitialized) + cancelAndIgnoreRemainingEvents() } } @@ -266,18 +248,15 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() - // Submit will return an error + // Submit will return an error while validating the account provider initialState.eventSink(ConfirmAccountProviderEvents.Continue) - skipItems(1) // Loading - // Check an error was returned - val submittedState = awaitItem() - assertThat(submittedState.loginModeState.loginMode).isInstanceOf(AsyncData.Failure::class.java) + val submittedState = awaitState { it.changeServerState.changeServerAction is AsyncData.Failure } // Assert the error is then cleared submittedState.eventSink(ConfirmAccountProviderEvents.ClearError) - val clearedState = awaitItem() + val clearedState = awaitState { it.changeServerState.changeServerAction is AsyncData.Uninitialized } assertThat(clearedState.loginModeState.loginMode).isEqualTo(AsyncData.Uninitialized) } } @@ -286,7 +265,7 @@ class ConfirmAccountProviderPresenterTest { fun `present - confirm account creation without oidc and without url generates an error`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails()) + Result.success(aMatrixHomeServerDetails(supportsPasswordLogin = true)) }, ) val presenter = createConfirmAccountProviderPresenter( @@ -299,14 +278,14 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink(ConfirmAccountProviderEvents.Continue) - skipItems(1) // Loading // Check an error was returned - val submittedState = awaitItem() + val submittedState = awaitLoginMode { it is AsyncData.Failure } assertThat(submittedState.loginModeState.loginMode.errorOrNull()).isInstanceOf(AccountCreationNotSupported::class.java) // Assert the error is then cleared submittedState.eventSink(ConfirmAccountProviderEvents.ClearError) - val clearedState = awaitItem() + val clearedState = awaitLoginMode { it is AsyncData.Uninitialized } assertThat(clearedState.loginModeState.loginMode).isEqualTo(AsyncData.Uninitialized) + cancelAndIgnoreRemainingEvents() } } @@ -324,10 +303,9 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink(ConfirmAccountProviderEvents.Continue) - skipItems(1) // Loading - val submittedState = awaitItem() - assertThat(submittedState.loginModeState.loginMode).isInstanceOf(AsyncData.Success::class.java) + val submittedState = awaitLoginMode { it is AsyncData.Success } assertThat(submittedState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) + cancelAndIgnoreRemainingEvents() } } @@ -347,10 +325,9 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink(ConfirmAccountProviderEvents.Continue) - skipItems(1) // Loading - val submittedState = awaitItem() - assertThat(submittedState.loginModeState.loginMode).isInstanceOf(AsyncData.Success::class.java) + val submittedState = awaitLoginMode { it is AsyncData.Success } assertThat(submittedState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) + cancelAndIgnoreRemainingEvents() } } @@ -359,7 +336,7 @@ class ConfirmAccountProviderPresenterTest { val aUrl = "aUrl" val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails()) + Result.success(aMatrixHomeServerDetails(supportsPasswordLogin = true)) }, ) val presenter = createConfirmAccountProviderPresenter( @@ -370,9 +347,26 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink(ConfirmAccountProviderEvents.Continue) - skipItems(1) // Loading - val submittedState = awaitItem() + val submittedState = awaitLoginMode { it is AsyncData.Success } assertThat(submittedState.loginModeState.loginMode.dataOrNull()).isEqualTo(LoginMode.AccountCreation(aUrl)) + cancelAndIgnoreRemainingEvents() + } + } + + /** + * Awaits until the emitted state's login mode matches [predicate], skipping the intermediate + * account-provider validation states, and returns that state. + */ + private suspend fun ReceiveTurbine.awaitLoginMode( + predicate: (AsyncData) -> Boolean, + ): ConfirmAccountProviderState = awaitState { predicate(it.loginModeState.loginMode) } + + private suspend fun ReceiveTurbine.awaitState( + predicate: (ConfirmAccountProviderState) -> Boolean, + ): ConfirmAccountProviderState { + while (true) { + val item = awaitItem() + if (predicate(item)) return item } } @@ -390,5 +384,19 @@ class ConfirmAccountProviderPresenterTest { oAuthActionFlow = defaultOAuthActionFlow, webClientUrlForAuthenticationRetriever = webClientUrlForAuthenticationRetriever, ), + changeServerPresenter = ChangeServerPresenter( + authenticationService = matrixAuthenticationService, + accountProviderDataSource = accountProviderDataSource, + defaultAccountProviderAccessControl = DefaultAccountProviderAccessControl( + enterpriseService = FakeEnterpriseService( + isAllowedToConnectToHomeserverResult = { true }, + ), + wellknownRetriever = FakeWellknownRetriever(), + ), + localNetworkPermissionGate = LocalNetworkPermissionGate( + advisor = FakeLocalNetworkPermissionAdvisor(), + permissionsPresenterFactory = FakePermissionsPresenterFactory(FakePermissionsPresenter()), + ), + ), ) } diff --git a/libraries/ui-strings/src/main/res/values/temporary.xml b/libraries/ui-strings/src/main/res/values/temporary.xml index c704e811ff..ca01aead32 100644 --- a/libraries/ui-strings/src/main/res/values/temporary.xml +++ b/libraries/ui-strings/src/main/res/values/temporary.xml @@ -10,4 +10,9 @@ "Collapsed" Active Inactive + "Enter your account provider" + "Choose an account provider" + "Account provider" + "If your account is provided by your company or a community, then change the account provider (example: companyname.com)." + "Create an account on matrix.org for free. If you want to create an account with a different provider, then change it accordingly (example: communityname.org)." From c2b3b0fe0f546a5c4192e9bb5b4cb490d9d1e66d Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Thu, 6 Aug 2026 11:31:32 -0700 Subject: [PATCH 019/338] Remove the account provider search screen and homeserver resolver --- .../AccountProviderOtherView.kt | 77 ------- .../login/impl/resolver/HomeserverData.kt | 14 -- .../login/impl/resolver/HomeserverResolver.kt | 82 ------- .../ChangeAccountProviderNode.kt | 51 ----- .../ChangeAccountProviderPresenter.kt | 57 ----- .../ChangeAccountProviderState.kt | 19 -- .../ChangeAccountProviderStateProvider.kt | 37 --- .../ChangeAccountProviderView.kt | 129 ----------- .../SearchAccountProviderEvents.kt | 17 -- .../SearchAccountProviderNode.kt | 49 ---- .../SearchAccountProviderPresenter.kt | 77 ------- .../SearchAccountProviderState.kt | 20 -- .../SearchAccountProviderStateProvider.kt | 48 ---- .../SearchAccountProviderView.kt | 214 ------------------ .../ChangeAccountProviderPresenterTest.kt | 113 --------- .../SearchAccountProviderPresenterTest.kt | 179 --------------- 16 files changed, 1183 deletions(-) delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderOtherView.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderState.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderStateProvider.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderEvents.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderState.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt delete mode 100644 features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt delete mode 100644 features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderOtherView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderOtherView.kt deleted file mode 100644 index 8b9b22022e..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderOtherView.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.accountprovider - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role -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.login.impl.R -import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom -import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize -import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.HorizontalDivider -import io.element.android.libraries.designsystem.theme.components.Text - -/** - * https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=604-60817 - */ -@Composable -fun AccountProviderOtherView( - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier - .fillMaxWidth() - .clickable(role = Role.Button) { onClick() } - ) { - HorizontalDivider() - Row( - modifier = Modifier - .fillMaxWidth() - .heightIn(min = 44.dp) - .padding(vertical = 4.dp, horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - RoundedIconAtom( - size = RoundedIconAtomSize.Medium, - imageVector = CompoundIcons.Search(), - tint = ElementTheme.colors.iconPrimary, - ) - Text( - modifier = Modifier - .padding(start = 16.dp) - .weight(1f), - text = stringResource(R.string.screen_change_account_provider_other), - style = ElementTheme.typography.fontBodyLgMedium, - color = ElementTheme.colors.textPrimary, - ) - } - } -} - -@PreviewsDayNight -@Composable -internal fun AccountProviderOtherViewPreview() = ElementPreview { - AccountProviderOtherView( - onClick = { }, - ) -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt deleted file mode 100644 index d9994deb9a..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.resolver - -data class HomeserverData( - // The computed homeserver url, for which a wellknown file has been retrieved, or just a valid Url - val homeserverUrl: String, -) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt deleted file mode 100644 index 8c6cab79ce..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.resolver - -import dev.zacsweers.metro.Inject -import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.core.coroutine.parallelMap -import io.element.android.libraries.core.uri.ensureProtocol -import io.element.android.libraries.core.uri.isValidUrl -import io.element.android.libraries.matrix.api.auth.HomeServerLoginCompatibilityChecker -import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionAdvisor -import kotlinx.coroutines.currentCoroutineContext -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.withContext -import timber.log.Timber -import java.util.Collections - -/** - * Resolve homeserver base on search terms. - */ -@Inject -class HomeserverResolver( - private val dispatchers: CoroutineDispatchers, - private val homeServerLoginCompatibilityChecker: HomeServerLoginCompatibilityChecker, - private val localNetworkPermissionAdvisor: LocalNetworkPermissionAdvisor, -) { - fun resolve(userInput: String): Flow> = flow { - val flowContext = currentCoroutineContext() - val trimmedUserInput = userInput.trim() - if (trimmedUserInput.length < 4) return@flow - val candidateBase = trimmedUserInput.ensureProtocol().removeSuffix("/") - val list = getUrlCandidates(candidateBase) - val currentList = Collections.synchronizedList(mutableListOf()) - // Run all the requests in parallel - withContext(dispatchers.io) { - list.parallelMap { url -> - // Skip the compatibility probe if we'd need ACCESS_LOCAL_NETWORK first — - // otherwise the probe hangs on the TCP timeout for ~30s. Emit the URL as a - // candidate directly; the actual sign-in flow triggers the permission prompt. - val shouldRequestPermissionOrIsValid = localNetworkPermissionAdvisor.shouldRequestPermissionFor(url) || - homeServerLoginCompatibilityChecker.check(url) - .onFailure { Timber.w(it, "Failed to check compatibility with homeserver $url") } - .getOrNull() - ?: return@parallelMap - - // Emit the list as soon as possible - if (shouldRequestPermissionOrIsValid) { - currentList.add(HomeserverData(homeserverUrl = url)) - withContext(flowContext) { - emit(currentList.toList()) - } - } - } - } - // If list is empty, and candidateBase is a valid an URL, do not block the user. - // A unsupported homeserver / homeserver not found error will be displayed if the user continues - if (currentList.isEmpty() && candidateBase.isValidUrl()) { - emit(listOf(HomeserverData(homeserverUrl = candidateBase))) - } - } - - private fun getUrlCandidates(data: String): List { - return buildList { - if (data.contains(".")) { - // TLD detected? - } else { - add("$data.org") - add("$data.com") - add("$data.io") - } - // Always try what the user has entered - add(data) - } - } -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt deleted file mode 100644 index 018a25dc89..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.changeaccountprovider - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.AssistedInject -import io.element.android.annotations.ContributesNode -import io.element.android.features.login.impl.util.openLearnMorePage -import io.element.android.libraries.architecture.callback - -@ContributesNode(AppScope::class) -@AssistedInject -class ChangeAccountProviderNode( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, - private val presenter: ChangeAccountProviderPresenter, -) : Node(buildContext, plugins = plugins) { - interface Callback : Plugin { - fun onDone() - fun navigateToSearchAccountProvider() - } - - private val callback: Callback = callback() - - @Composable - override fun View(modifier: Modifier) { - val state = presenter.present() - val context = LocalContext.current - ChangeAccountProviderView( - state = state, - modifier = modifier, - onBackClick = ::navigateUp, - onLearnMoreClick = { openLearnMorePage(context) }, - onSuccess = callback::onDone, - onOtherProviderClick = callback::navigateToSearchAccountProvider, - ) - } -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt deleted file mode 100644 index 41605352ef..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.changeaccountprovider - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import dev.zacsweers.metro.Inject -import io.element.android.appconfig.AuthenticationConfig -import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.features.enterprise.api.canConnectToAnyHomeserver -import io.element.android.features.login.impl.accountprovider.AccountProvider -import io.element.android.features.login.impl.changeserver.ChangeServerState -import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.core.uri.ensureProtocol -import kotlinx.collections.immutable.toImmutableList - -@Inject -class ChangeAccountProviderPresenter( - private val changeServerPresenter: Presenter, - private val enterpriseService: EnterpriseService, -) : Presenter { - @Composable - override fun present(): ChangeAccountProviderState { - val staticAccountProviderList = remember { - enterpriseService.defaultHomeserverList() - .filter { it != EnterpriseService.ANY_ACCOUNT_PROVIDER } - .map { it.ensureProtocol() } - .ifEmpty { listOf(AuthenticationConfig.MATRIX_ORG_URL) } - .map { url -> - AccountProvider( - url = url, - subtitle = null, - isPublic = url == AuthenticationConfig.MATRIX_ORG_URL, - isMatrixOrg = url == AuthenticationConfig.MATRIX_ORG_URL, - ) - } - .toImmutableList() - } - - val canSearchForAccountProviders = remember { - enterpriseService.canConnectToAnyHomeserver() - } - - val changeServerState = changeServerPresenter.present() - return ChangeAccountProviderState( - accountProviders = staticAccountProviderList, - canSearchForAccountProviders = canSearchForAccountProviders, - changeServerState = changeServerState, - ) - } -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderState.kt deleted file mode 100644 index 3068a1cb57..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderState.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.changeaccountprovider - -import io.element.android.features.login.impl.accountprovider.AccountProvider -import io.element.android.features.login.impl.changeserver.ChangeServerState -import kotlinx.collections.immutable.ImmutableList - -data class ChangeAccountProviderState( - val accountProviders: ImmutableList, - val canSearchForAccountProviders: Boolean, - val changeServerState: ChangeServerState, -) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderStateProvider.kt deleted file mode 100644 index 5cec1961a4..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderStateProvider.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.changeaccountprovider - -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.login.impl.accountprovider.AccountProvider -import io.element.android.features.login.impl.accountprovider.anAccountProvider -import io.element.android.features.login.impl.changeserver.ChangeServerState -import io.element.android.features.login.impl.changeserver.aChangeServerState -import kotlinx.collections.immutable.toImmutableList - -open class ChangeAccountProviderStateProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - aChangeAccountProviderState(), - aChangeAccountProviderState(canSearchForAccountProviders = false), - // Add other state here - ) -} - -fun aChangeAccountProviderState( - accountProviders: List = listOf( - anAccountProvider() - ), - canSearchForAccountProviders: Boolean = true, - changeServerState: ChangeServerState = aChangeServerState(), -) = ChangeAccountProviderState( - accountProviders = accountProviders.toImmutableList(), - canSearchForAccountProviders = canSearchForAccountProviders, - changeServerState = changeServerState, -) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt deleted file mode 100644 index 212bab515d..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -@file:OptIn(ExperimentalMaterial3Api::class) - -package io.element.android.features.login.impl.screens.changeaccountprovider - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.imePadding -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -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.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp -import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.login.impl.R -import io.element.android.features.login.impl.accountprovider.AccountProviderOtherView -import io.element.android.features.login.impl.accountprovider.AccountProviderView -import io.element.android.features.login.impl.changeserver.ChangeServerEvents -import io.element.android.features.login.impl.changeserver.ChangeServerView -import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule -import io.element.android.libraries.designsystem.components.BigIcon -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.lazyColumnContentPadding -import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets - -/** - * https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=604-60817 - */ -@Composable -fun ChangeAccountProviderView( - state: ChangeAccountProviderState, - onBackClick: () -> Unit, - onLearnMoreClick: () -> Unit, - onSuccess: () -> Unit, - onOtherProviderClick: () -> Unit, - modifier: Modifier = Modifier, -) { - Scaffold( - modifier = modifier, - topBar = { - TopAppBar( - title = {}, - navigationIcon = { BackButton(onClick = onBackClick) } - ) - }, - contentWindowInsets = scaffoldScrollableContentInsets, - ) { padding -> - Box( - modifier = Modifier - .fillMaxSize() - .imePadding() - .padding(padding) - .consumeWindowInsets(padding) - ) { - Column( - modifier = Modifier - .verticalScroll(state = rememberScrollState()) - .padding(lazyColumnContentPadding) - ) { - IconTitleSubtitleMolecule( - modifier = Modifier.padding(top = 16.dp, bottom = 32.dp, start = 16.dp, end = 16.dp), - iconStyle = BigIcon.Style.Default(CompoundIcons.HomeSolid()), - title = stringResource(id = R.string.screen_change_account_provider_title), - subTitle = stringResource(id = R.string.screen_change_account_provider_subtitle), - ) - - state.accountProviders.forEach { item -> - val alteredItem = if (item.isMatrixOrg) { - // Set the subtitle from the resource - item.copy( - subtitle = stringResource(id = R.string.screen_change_account_provider_matrix_org_subtitle), - ) - } else { - item - } - AccountProviderView( - item = alteredItem, - onClick = { - state.changeServerState.eventSink.invoke(ChangeServerEvents.ChangeServer(alteredItem)) - } - ) - } - // Other - if (state.canSearchForAccountProviders) { - AccountProviderOtherView( - onClick = onOtherProviderClick - ) - } - Spacer(Modifier.height(32.dp)) - } - ChangeServerView( - state = state.changeServerState, - onLearnMoreClick = onLearnMoreClick, - onSuccess = onSuccess, - ) - } - } -} - -@PreviewsDayNight -@Composable -internal fun ChangeAccountProviderViewPreview(@PreviewParameter(ChangeAccountProviderStateProvider::class) state: ChangeAccountProviderState) = ElementPreview { - ChangeAccountProviderView( - state = state, - onBackClick = { }, - onLearnMoreClick = { }, - onSuccess = { }, - onOtherProviderClick = { }, - ) -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderEvents.kt deleted file mode 100644 index 8816de3946..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderEvents.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.searchaccountprovider - -sealed interface SearchAccountProviderEvents { - /** - * The user has typed something, expect to get a list of matching account provider results - * in the state. - */ - data class UserInput(val input: String) : SearchAccountProviderEvents -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt deleted file mode 100644 index ddbcc4c847..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.searchaccountprovider - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.AssistedInject -import io.element.android.annotations.ContributesNode -import io.element.android.features.login.impl.util.openLearnMorePage -import io.element.android.libraries.architecture.callback - -@ContributesNode(AppScope::class) -@AssistedInject -class SearchAccountProviderNode( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, - private val presenter: SearchAccountProviderPresenter, -) : Node(buildContext, plugins = plugins) { - interface Callback : Plugin { - fun onDone() - } - - private val callback: Callback = callback() - - @Composable - override fun View(modifier: Modifier) { - val state = presenter.present() - val context = LocalContext.current - SearchAccountProviderView( - state = state, - modifier = modifier, - onBackClick = ::navigateUp, - onLearnMoreClick = { openLearnMorePage(context) }, - onSuccess = callback::onDone, - ) - } -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt deleted file mode 100644 index 2efe4501c6..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.searchaccountprovider - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import dev.zacsweers.metro.Inject -import io.element.android.features.login.impl.changeserver.ChangeServerState -import io.element.android.features.login.impl.resolver.HomeserverData -import io.element.android.features.login.impl.resolver.HomeserverResolver -import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.architecture.Presenter -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch - -@Inject -class SearchAccountProviderPresenter( - private val homeserverResolver: HomeserverResolver, - private val changeServerPresenter: Presenter, -) : Presenter { - @Composable - override fun present(): SearchAccountProviderState { - var userInput by rememberSaveable { - mutableStateOf("") - } - val changeServerState = changeServerPresenter.present() - - val data: MutableState>> = remember { - mutableStateOf(AsyncData.Uninitialized) - } - - LaunchedEffect(userInput) { - onUserInput(userInput, data) - } - - fun handleEvent(event: SearchAccountProviderEvents) { - when (event) { - is SearchAccountProviderEvents.UserInput -> { - userInput = event.input - } - } - } - - return SearchAccountProviderState( - userInput = userInput, - userInputResult = data.value, - changeServerState = changeServerState, - eventSink = ::handleEvent, - ) - } - - private fun CoroutineScope.onUserInput(userInput: String, data: MutableState>>) = launch { - data.value = AsyncData.Uninitialized - // Debounce - delay(500) - data.value = AsyncData.Loading() - homeserverResolver.resolve(userInput).collect { - data.value = AsyncData.Success(it) - } - if (data.value !is AsyncData.Success) { - data.value = AsyncData.Uninitialized - } - } -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderState.kt deleted file mode 100644 index dc02a997a6..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderState.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.searchaccountprovider - -import io.element.android.features.login.impl.changeserver.ChangeServerState -import io.element.android.features.login.impl.resolver.HomeserverData -import io.element.android.libraries.architecture.AsyncData - -data class SearchAccountProviderState( - val userInput: String, - val userInputResult: AsyncData>, - val changeServerState: ChangeServerState, - val eventSink: (SearchAccountProviderEvents) -> Unit -) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt deleted file mode 100644 index 2a8512c38a..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.searchaccountprovider - -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.appconfig.AuthenticationConfig -import io.element.android.features.login.impl.changeserver.aChangeServerState -import io.element.android.features.login.impl.resolver.HomeserverData -import io.element.android.libraries.architecture.AsyncData - -open class SearchAccountProviderStateProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - aSearchAccountProviderState(), - aSearchAccountProviderState(userInputResult = AsyncData.Success(aHomeserverDataList())), - // Add other state here - ) -} - -fun aSearchAccountProviderState( - userInput: String = "", - userInputResult: AsyncData> = AsyncData.Uninitialized, -) = SearchAccountProviderState( - userInput = userInput, - userInputResult = userInputResult, - changeServerState = aChangeServerState(), - eventSink = {} -) - -fun aHomeserverDataList(): List { - return listOf( - aHomeserverData(homeserverUrl = AuthenticationConfig.MATRIX_ORG_URL), - aHomeserverData(homeserverUrl = "https://no.sliding.sync"), - aHomeserverData(homeserverUrl = "https://invalid"), - ) -} - -fun aHomeserverData( - homeserverUrl: String = AuthenticationConfig.MATRIX_ORG_URL, -): HomeserverData { - return HomeserverData(homeserverUrl = homeserverUrl) -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt deleted file mode 100644 index 8335dde112..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -@file:OptIn(ExperimentalMaterial3Api::class) - -package io.element.android.features.login.impl.screens.searchaccountprovider - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.imePadding -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusDirection -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp -import io.element.android.appconfig.AuthenticationConfig -import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.login.impl.R -import io.element.android.features.login.impl.accountprovider.AccountProvider -import io.element.android.features.login.impl.accountprovider.AccountProviderView -import io.element.android.features.login.impl.changeserver.ChangeServerEvents -import io.element.android.features.login.impl.changeserver.ChangeServerView -import io.element.android.features.login.impl.resolver.HomeserverData -import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule -import io.element.android.libraries.designsystem.components.BigIcon -import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.components.form.textFieldState -import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusNext -import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator -import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.theme.components.TextField -import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.testtags.TestTags -import io.element.android.libraries.testtags.testTag -import io.element.android.libraries.ui.strings.CommonStrings - -/** - * https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=611-61435 - */ -@Composable -fun SearchAccountProviderView( - state: SearchAccountProviderState, - onBackClick: () -> Unit, - onLearnMoreClick: () -> Unit, - onSuccess: () -> Unit, - modifier: Modifier = Modifier, -) { - val eventSink = state.eventSink - Scaffold( - modifier = modifier, - topBar = { - TopAppBar( - title = {}, - navigationIcon = { BackButton(onClick = onBackClick) } - ) - } - ) { padding -> - Box( - modifier = Modifier - .fillMaxSize() - .imePadding() - .padding(padding) - .consumeWindowInsets(padding) - ) { - LazyColumn(modifier = Modifier.fillMaxWidth(), state = rememberLazyListState()) { - item { - IconTitleSubtitleMolecule( - modifier = Modifier.padding(top = 16.dp, bottom = 40.dp, start = 16.dp, end = 16.dp), - iconStyle = BigIcon.Style.Default(CompoundIcons.Search()), - title = stringResource(id = R.string.screen_account_provider_form_title), - subTitle = stringResource(id = R.string.screen_account_provider_form_subtitle), - ) - } - item { - // TextInput - var userInputState by textFieldState(stateValue = state.userInput) - val focusManager = LocalFocusManager.current - TextField( - value = userInputState, - // readOnly = isLoading, - modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) - .onTabOrEnterKeyFocusNext(focusManager) - .testTag(TestTags.changeServerServer), - onValueChange = { - userInputState = it - eventSink(SearchAccountProviderEvents.UserInput(it)) - }, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Uri, - imeAction = ImeAction.Done, - ), - keyboardActions = KeyboardActions(onDone = { - focusManager.moveFocus(FocusDirection.Down) - }), - singleLine = true, - trailingIcon = if (userInputState.isNotEmpty()) { - { - Box( - Modifier.clickable( - onClickLabel = stringResource(CommonStrings.action_clear), - role = Role.Button, - ) { - userInputState = "" - eventSink(SearchAccountProviderEvents.UserInput("")) - } - ) { - Icon( - imageVector = CompoundIcons.Close(), - contentDescription = stringResource(CommonStrings.action_clear) - ) - } - } - } else { - null - }, - supportingText = stringResource(id = R.string.screen_account_provider_form_notice), - ) - } - - when (state.userInputResult) { - is AsyncData.Failure -> { - // Ignore errors (let the user type more chars) - } - is AsyncData.Loading -> { - item { - Box( - modifier = Modifier - .fillMaxSize() - ) { - CircularProgressIndicator( - modifier = Modifier.align(Alignment.Center) - ) - } - } - } - is AsyncData.Success -> { - items(state.userInputResult.data) { homeserverData -> - val item = homeserverData.toAccountProvider() - AccountProviderView( - item = item, - onClick = { - state.changeServerState.eventSink.invoke(ChangeServerEvents.ChangeServer(item)) - } - ) - } - } - AsyncData.Uninitialized -> Unit - } - item { - Spacer(Modifier.height(32.dp)) - } - } - ChangeServerView( - state = state.changeServerState, - onLearnMoreClick = onLearnMoreClick, - onSuccess = onSuccess, - ) - } - } -} - -@Composable -private fun HomeserverData.toAccountProvider(): AccountProvider { - val isMatrixOrg = homeserverUrl == AuthenticationConfig.MATRIX_ORG_URL - return AccountProvider( - url = homeserverUrl, - subtitle = if (isMatrixOrg) stringResource(id = R.string.screen_change_account_provider_matrix_org_subtitle) else null, - // There is no need to know for other servers right now - isPublic = isMatrixOrg, - isMatrixOrg = isMatrixOrg, - ) -} - -@PreviewsDayNight -@Composable -internal fun SearchAccountProviderViewPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) = ElementPreview { - SearchAccountProviderView( - state = state, - onBackClick = {}, - onLearnMoreClick = {}, - onSuccess = {}, - ) -} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt deleted file mode 100644 index 7d81f05c0c..0000000000 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.changeaccountprovider - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.features.enterprise.test.FakeEnterpriseService -import io.element.android.features.login.impl.accountprovider.AccountProvider -import io.element.android.features.login.impl.changeserver.aChangeServerState -import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER -import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2 -import io.element.android.tests.testutils.WarmUpRule -import io.element.android.tests.testutils.test -import kotlinx.coroutines.test.runTest -import org.junit.Rule -import org.junit.Test - -class ChangeAccountProviderPresenterTest { - @get:Rule - val warmUpRule = WarmUpRule() - - @Test - fun `present - initial state`() = runTest { - val presenter = ChangeAccountProviderPresenter( - changeServerPresenter = { aChangeServerState() }, - enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { emptyList() } - ), - ) - presenter.test { - val initialState = awaitItem() - assertThat(initialState.accountProviders).isEqualTo( - listOf( - AccountProvider( - url = "https://matrix.org", - title = "matrix.org", - subtitle = null, - isPublic = true, - isMatrixOrg = true, - ) - ) - ) - assertThat(initialState.canSearchForAccountProviders).isTrue() - } - } - - @Test - fun `present - fixed list of account providers`() = runTest { - val presenter = ChangeAccountProviderPresenter( - changeServerPresenter = { aChangeServerState() }, - enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { - listOf(AN_ACCOUNT_PROVIDER, AN_ACCOUNT_PROVIDER_2) - } - ), - ) - presenter.test { - val initialState = awaitItem() - assertThat(initialState.accountProviders).isEqualTo( - listOf( - AccountProvider( - url = "https://matrix.org", - title = "matrix.org", - subtitle = null, - isPublic = true, - isMatrixOrg = true, - ), - AccountProvider( - url = "https://element.io", - title = "element.io", - subtitle = null, - isPublic = false, - isMatrixOrg = false, - ) - ) - ) - assertThat(initialState.canSearchForAccountProviders).isFalse() - } - } - - @Test - fun `present - opened list of account providers`() = runTest { - val presenter = ChangeAccountProviderPresenter( - changeServerPresenter = { aChangeServerState() }, - enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { - listOf(AN_ACCOUNT_PROVIDER, EnterpriseService.ANY_ACCOUNT_PROVIDER) - } - ), - ) - presenter.test { - val initialState = awaitItem() - assertThat(initialState.accountProviders).isEqualTo( - listOf( - AccountProvider( - url = "https://matrix.org", - title = "matrix.org", - subtitle = null, - isPublic = true, - isMatrixOrg = true, - ) - ) - ) - assertThat(initialState.canSearchForAccountProviders).isTrue() - } - } -} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt deleted file mode 100644 index 29c5d4a86c..0000000000 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.searchaccountprovider - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.login.impl.changeserver.aChangeServerState -import io.element.android.features.login.impl.resolver.HomeserverResolver -import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.matrix.test.auth.FakeHomeServerLoginCompatibilityChecker -import io.element.android.libraries.permissions.test.FakeLocalNetworkPermissionAdvisor -import io.element.android.tests.testutils.WarmUpRule -import io.element.android.tests.testutils.lambda.lambdaRecorder -import io.element.android.tests.testutils.lambda.value -import io.element.android.tests.testutils.test -import io.element.android.tests.testutils.testCoroutineDispatchers -import kotlinx.coroutines.test.runTest -import org.junit.Rule -import org.junit.Test - -class SearchAccountProviderPresenterTest { - @get:Rule - val warmUpRule = WarmUpRule() - - @Test - fun `present - initial state`() = runTest { - val fakeLoginCompatibilityChecker = FakeHomeServerLoginCompatibilityChecker(checkResult = { Result.success(true) }) - val presenter = SearchAccountProviderPresenter( - homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker, FakeLocalNetworkPermissionAdvisor()), - changeServerPresenter = { aChangeServerState() } - ) - presenter.test { - val initialState = awaitItem() - assertThat(initialState.userInput).isEmpty() - assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized) - } - } - - @Test - fun `present - error while checking login compatibility`() = runTest { - val fakeLoginCompatibilityChecker = FakeHomeServerLoginCompatibilityChecker(checkResult = { Result.failure(IllegalStateException("Oops")) }) - val presenter = SearchAccountProviderPresenter( - homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker, FakeLocalNetworkPermissionAdvisor()), - changeServerPresenter = { aChangeServerState() } - ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("https://test.org")) - val withInputState = awaitItem() - assertThat(withInputState.userInput).isEqualTo("https://test.org") - assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized) - assertThat(awaitItem().userInputResult).isInstanceOf(AsyncData.Loading::class.java) - assertThat(awaitItem().userInputResult).isEqualTo( - AsyncData.Success( - listOf( - aHomeserverData(homeserverUrl = "https://test.org") - ) - ) - ) - } - } - - @Test - fun `present - enter text no result`() = runTest { - val fakeWellknownRetriever = FakeHomeServerLoginCompatibilityChecker(checkResult = { Result.success(false) }) - val presenter = SearchAccountProviderPresenter( - homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRetriever, FakeLocalNetworkPermissionAdvisor()), - changeServerPresenter = { aChangeServerState() } - ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test")) - val withInputState = awaitItem() - assertThat(withInputState.userInput).isEqualTo("test") - assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized) - assertThat(awaitItem().userInputResult).isInstanceOf(AsyncData.Loading::class.java) - assertThat(awaitItem().userInputResult).isEqualTo( - AsyncData.Success( - listOf( - aHomeserverData(homeserverUrl = "https://test"), - ) - ) - ) - } - } - - @Test - fun `present - enter text one result with wellknown`() = runTest { - val checkResult = lambdaRecorder> { - when (it) { - "https://test.org" -> Result.success(false) - "https://test.com" -> Result.success(false) - "https://test.io" -> Result.success(true) - "https://test" -> Result.success(false) - else -> error("should not happen") - } - } - val fakeLoginCompatibilityChecker = FakeHomeServerLoginCompatibilityChecker(checkResult = checkResult) - val presenter = SearchAccountProviderPresenter( - homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker, FakeLocalNetworkPermissionAdvisor()), - changeServerPresenter = { aChangeServerState() } - ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test")) - val withInputState = awaitItem() - assertThat(withInputState.userInput).isEqualTo("test") - assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized) - assertThat(awaitItem().userInputResult).isInstanceOf(AsyncData.Loading::class.java) - assertThat(awaitItem().userInputResult).isEqualTo( - AsyncData.Success( - listOf( - aHomeserverData(homeserverUrl = "https://test.io") - ) - ) - ) - checkResult.assertions().isCalledExactly(4) - .withSequence( - listOf(value("https://test.org")), - listOf(value("https://test.com")), - listOf(value("https://test.io")), - listOf(value("https://test")), - ) - } - } - - @Test - fun `present - enter text two results with wellknown`() = runTest { - val checkResult = lambdaRecorder> { - when (it) { - "https://test.org" -> Result.success(true) - "https://test.com" -> Result.success(false) - "https://test.io" -> Result.success(true) - "https://test" -> Result.success(false) - else -> error("should not happen") - } - } - val fakeLoginCompatibilityChecker = FakeHomeServerLoginCompatibilityChecker(checkResult = checkResult) - val presenter = SearchAccountProviderPresenter( - homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker, FakeLocalNetworkPermissionAdvisor()), - changeServerPresenter = { aChangeServerState() } - ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test")) - val withInputState = awaitItem() - assertThat(withInputState.userInput).isEqualTo("test") - assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized) - assertThat(awaitItem().userInputResult).isInstanceOf(AsyncData.Loading::class.java) - assertThat(awaitItem().userInputResult).isEqualTo( - AsyncData.Success( - listOf( - aHomeserverData(homeserverUrl = "https://test.org"), - ) - ) - ) - assertThat(awaitItem().userInputResult).isEqualTo( - AsyncData.Success( - listOf( - aHomeserverData(homeserverUrl = "https://test.org"), - aHomeserverData(homeserverUrl = "https://test.io"), - ) - ) - ) - checkResult.assertions().isCalledExactly(4) - .withSequence( - listOf(value("https://test.org")), - listOf(value("https://test.com")), - listOf(value("https://test.io")), - listOf(value("https://test")), - ) - } - } -} From d04a3853b4822bb3c12a10c3e6b86dbd23aefe01 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Thu, 6 Aug 2026 12:11:18 -0700 Subject: [PATCH 020/338] Add inline autocomplete for the account provider field --- .../ConfirmAccountProviderPresenter.kt | 23 ++++++++-- .../ConfirmAccountProviderState.kt | 2 + .../ConfirmAccountProviderStateProvider.kt | 6 +++ .../ConfirmAccountProviderView.kt | 44 ++++++++++++++++++ .../ConfirmAccountProviderPresenterTest.kt | 46 +++++++++++++++++++ .../src/main/res/values/temporary.xml | 1 + 6 files changed, 119 insertions(+), 3 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt index de6ec28774..c3f51a5177 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue @@ -27,6 +28,7 @@ import io.element.android.features.login.impl.login.LoginModeEvent import io.element.android.features.login.impl.login.LoginModeState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.preferences.api.store.AppPreferencesStore @AssistedInject class ConfirmAccountProviderPresenter( @@ -34,6 +36,7 @@ class ConfirmAccountProviderPresenter( private val accountProviderDataSource: AccountProviderDataSource, private val loginModePresenter: Presenter, private val changeServerPresenter: Presenter, + private val appPreferencesStore: AppPreferencesStore, ) : Presenter { data class Params( val isAccountCreation: Boolean, @@ -49,11 +52,24 @@ class ConfirmAccountProviderPresenter( val accountProvider by accountProviderDataSource.flow.collectAsState() val loginModeState = loginModePresenter.present() val changeServerState = changeServerPresenter.present() + val homeserverHistory by appPreferencesStore.getHomeserverHistoryFlow().collectAsState(emptyList()) // Editable input, seeded from the current (history-defaulted) account provider until the user edits it. var userInput by rememberSaveable { mutableStateOf(null) } val accountProviderInput = userInput ?: accountProvider.url - val latestAccountProviderInput by rememberUpdatedState(accountProviderInput) + + // Offer the most recent previously-used account provider that the current input is a prefix of. + val accountProviderSuggestion = remember(accountProviderInput, homeserverHistory) { + val input = accountProviderInput.trim() + input.takeIf { it.isNotEmpty() } + ?.let { prefix -> + homeserverHistory.firstOrNull { it.length > prefix.length && it.startsWith(prefix, ignoreCase = true) } + } + } + + // Continue accepts the suggestion when one is offered, otherwise the raw input. + val accountProviderToSubmit = accountProviderSuggestion ?: accountProviderInput + val latestAccountProviderToSubmit by rememberUpdatedState(accountProviderToSubmit) // Once the chosen account provider has been validated and persisted, proceed with the actual sign in / sign up. LaunchedEffect(changeServerState.changeServerAction) { @@ -61,7 +77,7 @@ class ConfirmAccountProviderPresenter( loginModeState.eventSink( LoginModeEvent.Submit( isAccountCreation = params.isAccountCreation, - homeserverUrl = latestAccountProviderInput.trim(), + homeserverUrl = latestAccountProviderToSubmit.trim(), resolvedHomeserverUrl = null, loginHint = null, ) @@ -77,7 +93,7 @@ class ConfirmAccountProviderPresenter( // Validate (and persist) the chosen account provider before proceeding. This also enforces the // enterprise account-provider access control, which the login submit does not do on its own. ConfirmAccountProviderEvents.Continue -> changeServerState.eventSink( - ChangeServerEvents.ChangeServer(AccountProvider(url = accountProviderInput.trim())) + ChangeServerEvents.ChangeServer(AccountProvider(url = accountProviderToSubmit.trim())) ) ConfirmAccountProviderEvents.ClearError -> { loginModeState.eventSink(LoginModeEvent.ClearError) @@ -88,6 +104,7 @@ class ConfirmAccountProviderPresenter( return ConfirmAccountProviderState( accountProviderInput = accountProviderInput, + accountProviderSuggestion = accountProviderSuggestion, isAccountCreation = params.isAccountCreation, loginModeState = loginModeState, changeServerState = changeServerState, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt index 0c211341ef..a934378507 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt @@ -14,6 +14,8 @@ import io.element.android.libraries.architecture.AsyncData data class ConfirmAccountProviderState( val accountProviderInput: String, + // The full account provider from history that the current input is a prefix of, offered as inline autocomplete. + val accountProviderSuggestion: String?, val isAccountCreation: Boolean, val loginModeState: LoginModeState, val changeServerState: ChangeServerState, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt index 73c5e5f550..53fa763c2f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt @@ -20,6 +20,10 @@ open class ConfirmAccountProviderStateProvider : PreviewParameterProvider get() = sequenceOf( aConfirmAccountProviderState(), + aConfirmAccountProviderState( + accountProviderInput = "element", + accountProviderSuggestion = "element.io", + ), aConfirmAccountProviderState( isAccountCreation = true, ), @@ -32,12 +36,14 @@ open class ConfirmAccountProviderStateProvider : PreviewParameterProvider Unit = {}, ) = ConfirmAccountProviderState( accountProviderInput = accountProviderInput, + accountProviderSuggestion = accountProviderSuggestion, isAccountCreation = isAccountCreation, loginModeState = loginModeState, changeServerState = changeServerState, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index 3a7d2831c8..470b59926b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -16,15 +16,25 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.OffsetMapping +import androidx.compose.ui.text.input.TransformedText +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.login.impl.changeserver.ChangeServerView import io.element.android.features.login.impl.login.LoginModeEvent @@ -89,6 +99,15 @@ fun ConfirmAccountProviderView( ) { var input by textFieldState(stateValue = state.accountProviderInput) val focusManager = LocalFocusManager.current + // Inline autocomplete: render the un-typed remainder of the suggested account provider in grey. + val suggestionSuffix = state.accountProviderSuggestion + ?.takeIf { it.length > input.length && it.startsWith(input, ignoreCase = true) } + ?.substring(input.length) + .orEmpty() + val ghostColor = ElementTheme.colors.textSecondary + val ghostTransformation = remember(suggestionSuffix, ghostColor) { + GhostSuffixVisualTransformation(suggestionSuffix, ghostColor) + } TextField( value = input, onValueChange = { @@ -101,6 +120,7 @@ fun ConfirmAccountProviderView( .onTabOrEnterKeyFocusNext(focusManager) .testTag(TestTags.changeServerServer), label = stringResource(id = CommonStrings.screen_select_server_textfield_header), + placeholder = stringResource(id = CommonStrings.screen_select_server_textfield_placeholder), supportingText = stringResource( id = if (state.isAccountCreation) { CommonStrings.screen_select_server_textfield_footer_register @@ -108,6 +128,7 @@ fun ConfirmAccountProviderView( CommonStrings.screen_select_server_textfield_footer_login } ), + visualTransformation = ghostTransformation, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done, @@ -167,6 +188,29 @@ fun ConfirmAccountProviderView( ) } +/** + * Appends [suffix] (in [color]) after the user's text as a non-editable inline autocomplete hint. + * The underlying field value stays limited to what the user actually typed, so the cursor can never + * enter the suggested region. + */ +private class GhostSuffixVisualTransformation( + private val suffix: String, + private val color: Color, +) : VisualTransformation { + override fun filter(text: AnnotatedString): TransformedText { + if (suffix.isEmpty()) return TransformedText(text, OffsetMapping.Identity) + val transformed = buildAnnotatedString { + append(text) + withStyle(SpanStyle(color = color)) { append(suffix) } + } + val offsetMapping = object : OffsetMapping { + override fun originalToTransformed(offset: Int) = offset + override fun transformedToOriginal(offset: Int) = offset.coerceAtMost(text.length) + } + return TransformedText(transformed, offsetMapping) + } +} + @PreviewsDayNight @Composable internal fun ConfirmAccountProviderViewPreview( diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index 0257d68ea9..eba6a4474f 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -34,6 +34,8 @@ import io.element.android.libraries.oauth.test.customtab.FakeOAuthActionFlow import io.element.android.libraries.permissions.test.FakeLocalNetworkPermissionAdvisor import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory +import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest @@ -353,6 +355,48 @@ class ConfirmAccountProviderPresenterTest { } } + @Test + fun `present - offers an autocomplete suggestion from the account provider history`() = runTest { + val presenter = createConfirmAccountProviderPresenter( + appPreferencesStore = InMemoryAppPreferencesStore( + homeserverHistory = listOf("https://randomcommunity.org"), + ), + ) + presenter.test { + val initialState = awaitItem() + assertThat(initialState.accountProviderSuggestion).isNull() + initialState.eventSink(ConfirmAccountProviderEvents.UserInputChanged("https://random")) + val suggestionState = awaitState { it.accountProviderInput == "https://random" } + assertThat(suggestionState.accountProviderSuggestion).isEqualTo("https://randomcommunity.org") + } + } + + @Test + fun `present - continue accepts the autocomplete suggestion rather than the typed prefix`() = runTest { + val submittedUrls = mutableListOf() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { url -> + submittedUrls.add(url) + Result.success(aMatrixHomeServerDetails(supportsPasswordLogin = true)) + }, + ) + val presenter = createConfirmAccountProviderPresenter( + matrixAuthenticationService = authenticationService, + appPreferencesStore = InMemoryAppPreferencesStore( + homeserverHistory = listOf("https://randomcommunity.org"), + ), + ) + presenter.test { + val initialState = awaitItem() + initialState.eventSink(ConfirmAccountProviderEvents.UserInputChanged("https://random")) + val suggestionState = awaitState { it.accountProviderSuggestion != null } + suggestionState.eventSink(ConfirmAccountProviderEvents.Continue) + awaitLoginMode { it is AsyncData.Success } + assertThat(submittedUrls.first()).isEqualTo("https://randomcommunity.org") + cancelAndIgnoreRemainingEvents() + } + } + /** * Awaits until the emitted state's login mode matches [predicate], skipping the intermediate * account-provider validation states, and returns that state. @@ -376,9 +420,11 @@ class ConfirmAccountProviderPresenterTest { matrixAuthenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), defaultOAuthActionFlow: OAuthActionFlow = FakeOAuthActionFlow(), webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever(), + appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(), ) = ConfirmAccountProviderPresenter( params = params, accountProviderDataSource = accountProviderDataSource, + appPreferencesStore = appPreferencesStore, loginModePresenter = createLoginModePresenter( authenticationService = matrixAuthenticationService, oAuthActionFlow = defaultOAuthActionFlow, diff --git a/libraries/ui-strings/src/main/res/values/temporary.xml b/libraries/ui-strings/src/main/res/values/temporary.xml index ca01aead32..2b5788042e 100644 --- a/libraries/ui-strings/src/main/res/values/temporary.xml +++ b/libraries/ui-strings/src/main/res/values/temporary.xml @@ -13,6 +13,7 @@ "Enter your account provider" "Choose an account provider" "Account provider" + "example.com" "If your account is provided by your company or a community, then change the account provider (example: companyname.com)." "Create an account on matrix.org for free. If you want to create an account with a different provider, then change it accordingly (example: communityname.org)." From e1daec6728a88c8b48ab5ace5f223e1f77e44448 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2026 08:06:16 +0000 Subject: [PATCH 021/338] Update dependency androidx.constraintlayout:constraintlayout-compose to v1.1.2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f934d1918c..818d7767c4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ firebaseAppDistribution = "5.3.0" core = "1.18.0" datastore = "1.2.1" constraintlayout = "2.2.2" -constraintlayout_compose = "1.1.1" +constraintlayout_compose = "1.1.2" lifecycle = "2.11.0" activity = "1.13.0" media3 = "1.10.1" From b7c0374c7c7a820ef245870034848b8c73e49daf Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 7 Aug 2026 09:53:47 +0000 Subject: [PATCH 022/338] Update screenshots --- ....home.impl.components_HomeTopBarWithIndicator_Day_0_en.png | 4 ++-- ...ome.impl.components_HomeTopBarWithIndicator_Night_0_en.png | 4 ++-- ...res.home.impl.components_HomeTopBarWithStatus_Day_0_en.png | 3 +++ ...s.home.impl.components_HomeTopBarWithStatus_Night_0_en.png | 3 +++ .../snapshots/images/features.home.impl_HomeViewA11y_en.png | 4 ++-- ...krequests.impl.banner_KnockRequestsBannerView_Day_5_en.png | 4 ++-- ...equests.impl.banner_KnockRequestsBannerView_Night_5_en.png | 4 ++-- ...knockrequests.impl.list_KnockRequestsListView_Day_3_en.png | 4 ++-- ...ockrequests.impl.list_KnockRequestsListView_Night_3_en.png | 4 ++-- ...knewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en.png | 4 ++-- ...ewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_0_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_1_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_2_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_3_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_4_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_5_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_6_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_7_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_8_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_9_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_0_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_1_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_2_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_3_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_4_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_5_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_6_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_7_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_8_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_9_en.png | 4 ++-- ....lockscreen.impl.components_PinEntryTextField_Day_0_en.png | 4 ++-- ...ockscreen.impl.components_PinEntryTextField_Night_0_en.png | 4 ++-- ...nts.preview.imageeditor_AttachmentImageEditorView_2_en.png | 4 ++-- ...nts.preview.imageeditor_AttachmentImageEditorView_5_en.png | 4 ++-- ...rypto.identity_MessagesViewWithIdentityChange_Day_0_en.png | 4 ++-- ...rypto.identity_MessagesViewWithIdentityChange_Day_1_en.png | 4 ++-- ...rypto.identity_MessagesViewWithIdentityChange_Day_2_en.png | 4 ++-- ...pto.identity_MessagesViewWithIdentityChange_Night_0_en.png | 4 ++-- ...pto.identity_MessagesViewWithIdentityChange_Night_1_en.png | 4 ++-- ...pto.identity_MessagesViewWithIdentityChange_Night_2_en.png | 4 ++-- ...re.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png | 4 ++-- ...re.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png | 4 ++-- ....resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png | 4 ++-- ....resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png | 4 ++-- ...meline.components.event_ATimelineItemEventRow_Day_0_en.png | 4 ++-- ...line.components.event_ATimelineItemEventRow_Night_0_en.png | 4 ++-- ....components_TimelineItemEventRowDisambiguated_Day_0_en.png | 4 ++-- ...omponents_TimelineItemEventRowDisambiguated_Night_0_en.png | 4 ++-- ...ents_TimelineItemEventRowWithReplyInformative_Day_0_en.png | 4 ++-- ...ents_TimelineItemEventRowWithReplyInformative_Day_1_en.png | 4 ++-- ...ts_TimelineItemEventRowWithReplyInformative_Night_0_en.png | 4 ++-- ...ts_TimelineItemEventRowWithReplyInformative_Night_1_en.png | 4 ++-- ...components_TimelineItemEventRowWithReplyOther_Day_0_en.png | 4 ++-- ...components_TimelineItemEventRowWithReplyOther_Day_1_en.png | 4 ++-- ...mponents_TimelineItemEventRowWithReplyOther_Night_0_en.png | 4 ++-- ...mponents_TimelineItemEventRowWithReplyOther_Night_1_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_0_en.png | 4 ++-- ...ine.components_TimelineItemEventRowWithReply_Day_10_en.png | 4 ++-- ...ine.components_TimelineItemEventRowWithReply_Day_11_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_1_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_2_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_3_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_4_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_5_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_6_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_7_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_8_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_9_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_0_en.png | 4 ++-- ...e.components_TimelineItemEventRowWithReply_Night_10_en.png | 4 ++-- ...e.components_TimelineItemEventRowWithReply_Night_11_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_1_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_2_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_3_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_4_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_5_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_6_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_7_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_8_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_9_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_0_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_10_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_11_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_1_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_2_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_3_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_4_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_5_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_6_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_7_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_8_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_9_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_0_en.png | 4 ++-- .../features.messages.impl_MessagesView_Night_10_en.png | 4 ++-- .../features.messages.impl_MessagesView_Night_11_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_1_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_2_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_3_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_4_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_5_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_6_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_7_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_8_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_9_en.png | 4 ++-- ...references.impl.blockedusers_BlockedUsersView_Day_4_en.png | 4 ++-- ...references.impl.blockedusers_BlockedUsersView_Day_5_en.png | 4 ++-- ...ferences.impl.blockedusers_BlockedUsersView_Night_4_en.png | 4 ++-- ...ferences.impl.blockedusers_BlockedUsersView_Night_5_en.png | 4 ++-- ...es.preferences.impl.userstatus_UserStatusView_Day_5_en.png | 4 ++-- ...es.preferences.impl.userstatus_UserStatusView_Day_6_en.png | 4 ++-- ....preferences.impl.userstatus_UserStatusView_Night_5_en.png | 4 ++-- ....preferences.impl.userstatus_UserStatusView_Night_6_en.png | 4 ++-- ...embermoderation.impl_RoomMemberModerationView_Day_5_en.png | 4 ++-- ...embermoderation.impl_RoomMemberModerationView_Day_7_en.png | 4 ++-- ...bermoderation.impl_RoomMemberModerationView_Night_5_en.png | 4 ++-- ...bermoderation.impl_RoomMemberModerationView_Night_7_en.png | 4 ++-- ...eme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png | 4 ++-- ...e.components_HorizontalFloatingToolbarNoFab_Night_0_en.png | 4 ++-- ...em.theme.components_HorizontalFloatingToolbar_Day_0_en.png | 4 ++-- ....theme.components_HorizontalFloatingToolbar_Night_0_en.png | 4 ++-- 121 files changed, 244 insertions(+), 238 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Day_0_en.png index 5eb704b885..2a515ca368 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c917c554a717315d953b441fa3cbc9242426f0f92ccc4618f6752b6f15b90a83 -size 22462 +oid sha256:dd697e25338f15e3dfeb82370f1726cc337fa74a5e65240cad22c404ec949976 +size 22485 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Night_0_en.png index 031e8841e6..7dd2daea6e 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34f87a3995ba43f02150837517922c678d7def6573c7d3f4c26940c450a388c7 -size 20722 +oid sha256:0567d8419972054d7dce9a080b856324a1bc97529ff2d15776aba6c023eb90e2 +size 20730 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Day_0_en.png new file mode 100644 index 0000000000..4fa1c78188 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd896bfc1111dcb04d94584c19a395e69612dd9ceda8beea6432c14aa4f18023 +size 23126 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Night_0_en.png new file mode 100644 index 0000000000..9111455bec --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:262622cd1124902ab45ccd645d33193dd8d0e4a99ba7e4d2cff786393b350118 +size 21299 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png index 344bd350d6..aca66273b4 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5dde8ed5c776f4701f24dd7d411dd78f48adab261efad71e961c845e24d12f3 -size 144367 +oid sha256:9cf305c0e52881fb40fee2ba7bdcdc3dfa5c899577b3e252af19e37ee2efacf7 +size 143213 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png index ae365f5143..5fe8e62f96 100644 --- a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:645349979f0789dd674b9efcaee305a35955725c5c4d24599984aefc3a61b18f -size 32005 +oid sha256:b2e211a700dfcefab1927926ecd2ab1eee2ceaed6980657e1bad1e70bbc69dd5 +size 29522 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en.png index f0bfd0f2b4..33110584a9 100644 --- a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e82c6e4ff563e7ae870431528d9413ac915e16ca0151aa3c86ff5081bfc72478 -size 27563 +oid sha256:dbdf9740221b82aaaf332c95e7299594f0b4fdd258b2e9c733133bdd4a01f692 +size 28378 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png index 93d24c8613..d002a79378 100644 --- a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3cd12ed914f8c161ef845feaaec416eec9ef1ae6d23d71fb444c93d1388738ea -size 41736 +oid sha256:ab30267a07b1ceb43c1a7f854e758c68123812d99054f6e94db4cccf3214a2b4 +size 41479 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Night_3_en.png index 438d96608d..42ecbe8a68 100644 --- a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae0b92a52a7f2c0533af7d7f0fbddc6497778e7c5dcedc3344a2ba60a6a948f8 -size 40439 +oid sha256:471abc7c54d0f90ad5a923bd21abbf0960bbb9cbbdb22313a779083baf61ab3f +size 40671 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en.png index 48598e410f..23bb00edfe 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c79c83bde346bec65f67c34805407e46b351b2c91772c13e0a6c23ae09bf7e8 -size 30345 +oid sha256:7270b97b04cd403a5d5e34b55b3303be08497fcb5d750d024565ffa17d20802c +size 31808 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en.png index d22d7f98cf..9945685453 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff17ff66d5c92ab19ce8064e2e14326e9f2921c1b7313a3974547b2932cb7fb1 -size 32295 +oid sha256:29ee97865bc634eb1f30d6866a0a2badc91b5453ded11d7f4905c46db9c0c324 +size 29775 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_0_en.png index 33ef3511ec..374acc4a7a 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f03643af6b4643c0fbfee2d98138aa0350d0fb541de0a8a627570030a8f3812 -size 20466 +oid sha256:f879dd8636314113c9962bedae376f34057fd7af62a3725ef9aca7913ef08abc +size 20453 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_1_en.png index b1bdcee7a4..60bf32612b 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f53a6b803268437c30350857951422dc1723a38922db4441a97bcb926a0e63ff -size 38724 +oid sha256:e44d67574a6d3df53f9c9a6d588461dda932cc77142c07605e2a53a0f7c33819 +size 36440 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_2_en.png index b597367206..a91262bdf2 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77ce52ef94d449d8eb37a8d84b297800a1663bc38dc27e2ea87e6f7da373195b -size 37222 +oid sha256:a1482966c7528de972c7a9af647eb249329777b4f60f05ffd1794a1cdc8115dd +size 34889 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png index 56bc529007..caa435aca2 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76c4f1674cda818e0684869050fbad2cb31c02146726d1ee5e57481940218d29 -size 30363 +oid sha256:455ad29386413801da964f034b284c3693ed2281510cfdb0e6a0e50478904098 +size 32472 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_4_en.png index 33ef3511ec..374acc4a7a 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f03643af6b4643c0fbfee2d98138aa0350d0fb541de0a8a627570030a8f3812 -size 20466 +oid sha256:f879dd8636314113c9962bedae376f34057fd7af62a3725ef9aca7913ef08abc +size 20453 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_5_en.png index be619b419b..740c6fd618 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc017e7707b83549cda8c06ffe8466596372132ba0b58dd91cfb20c451b6a417 -size 22303 +oid sha256:0ca62fd1055aef95b255d40b7f8933e455a7fd7014c96e9e5eef04a9cff5549a +size 22324 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_6_en.png index 201c56516e..380f1ec5b2 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f5ca554e1ae330865af0d267934d8d1c6dfe42cc3958db3b15b13d376e8721c -size 26667 +oid sha256:1ddc6b2ec2417a2794e9ca0bfd88fae8f63e6d6f567582498157720092acd957 +size 26734 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png index 3d4aa9edc5..75f447d578 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd18d8bb6e61f616451439dff8690afc7473461c87adf11bb9870319d8e06fc4 -size 38447 +oid sha256:a205abcee059aef3f902cac7716a5bb53127b98ab29d97b0b44cbaa6ba5cdabe +size 42394 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_8_en.png index fb4db2984e..d4f298adb5 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f8c9a2dd2c3677281b6486af6a0b8a5405dc59bff0390b6ce1bca1cc63d88b0d -size 42546 +oid sha256:d9a51a8b9c8799af008fca5e684c1fd107b6240f0e5f273b1559022c70ce5e31 +size 46363 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_9_en.png index d194937309..f6c445eee1 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebeb2f14074a9bc91dc892bfa056c9c67e83a64c64d645c77d312d64895e3815 -size 21611 +oid sha256:dbb35e33035a9c22164128414bde7f6df848193040eef3bd7f073b0b182a7306 +size 21670 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_0_en.png index 70de0a28d5..c07cad6219 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc9a8778869e47b9874c4300cac52045c4be5fac6590acbaa719a065249ed3f5 -size 19856 +oid sha256:8bd18f0f95a41705cb18e753f7b99492aeba1a994cb02c95275bd7c14b5d983a +size 19886 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png index d9f4faabc7..21eb862eda 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:124381cc86542b2704b619e798f62f58dc7f3b95ab8a1c4eec3fa4b849462f9e -size 34867 +oid sha256:e0cd7909eb2cce4ce2ad297791527468a18b43343c4f4c7b1bb7ab5ade6cfc9d +size 36959 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_2_en.png index 432cbde734..91d309165f 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f373df8ce07aa19d467d43306f5a8d6c78915e4e166e08e39bb39cb303b5faec -size 33305 +oid sha256:33c7e31962e562c4217629795ad0d79be10add9723e657f1a8dc3c4c16ac4ed4 +size 35476 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_3_en.png index 317a87e2e5..b9e79d2337 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebbeb66a3f67e48668cadb96069f50feba5f4346119ea62c9dfd83ffd621b7b2 -size 31182 +oid sha256:8721e0c604b0e12ba22db934ab93794e6c2143d1b4bc4e2f3d297f9121c1e688 +size 29233 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_4_en.png index 70de0a28d5..c07cad6219 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc9a8778869e47b9874c4300cac52045c4be5fac6590acbaa719a065249ed3f5 -size 19856 +oid sha256:8bd18f0f95a41705cb18e753f7b99492aeba1a994cb02c95275bd7c14b5d983a +size 19886 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png index d261494eb8..46f4412ab9 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a3d7220c216b6fee2384ba2cb263903a8cfa45f3f97302c722188a4a827c575 -size 21630 +oid sha256:299310684787f8c18e4219e69125ce48d32d229b4bb3087da7e102aa87ea1ac7 +size 21599 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_6_en.png index 56ef074549..259466128e 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24b8f832cbaa9aaad66187255adcefbb2d7c4407b911c2675a261b6f480aa277 -size 25858 +oid sha256:11c3fe98b80dd8823e3eeafccda7aa5430dc6a691d2568f2a186c14f6a340360 +size 25824 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_7_en.png index e66ccc603d..5732afb34a 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e31aa9639a3805e3644fadbb01ab3503aedee068655920555a4817ae38b6376 -size 40312 +oid sha256:dfaee97064c6391c9e611e30bfb36d3abb8d10d2e3312cd21afa3b2b362637cf +size 36805 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_8_en.png index f2f4d7c766..549c353b64 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77e1a15b548d944e57b7d71229c28692e32ad9340d04ec0c43c52dafe16e3ae1 -size 44172 +oid sha256:2b56fa8390836f84984130ac90af3a8e2244ab06d1460133883aa266e73b4567 +size 40742 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png index dcf2ab131b..1cf67b2855 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cab8ea5b016f20de07dd0134836c42f9fd51562837fe4da2be8a8d38ca1f95f8 -size 19914 +oid sha256:e68d95e8c4734d26e0853c886f8253c3ff21508fdf97dac8879e67d25a683e06 +size 19899 diff --git a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.components_PinEntryTextField_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.components_PinEntryTextField_Day_0_en.png index 3a22d6959f..72c3e43f5a 100644 --- a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.components_PinEntryTextField_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.components_PinEntryTextField_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2046f1225b2f58812502196f4b7d1c7a4d87f740b32e40dd82762fade1c42ea2 -size 16167 +oid sha256:24edc821b92f0686eb1cdc3a8214adacf490f9f586637e7c23adeaf904e6704a +size 16292 diff --git a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.components_PinEntryTextField_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.components_PinEntryTextField_Night_0_en.png index 10223f5bf6..5c49c08509 100644 --- a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.components_PinEntryTextField_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.components_PinEntryTextField_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62384687c273cb57dbd25bfaa558d83e3efc527fc4dfff4aca8bf85ab1ce9e4b -size 16446 +oid sha256:58a6cbe83f86bbd32c8576a4d62a26794560f53b60c9bf5dc74aa0dd50e2dc87 +size 16238 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png index 3192cf2a5e..8edee63bc1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1885bbe8b0ea497ac20d7bcdd0c6a93ba0533e898378d35e1cafdc8c9924fd56 -size 279984 +oid sha256:de82970da241172b94f822f8792e6fc721dcfc7f0f67e3523962e253e9750884 +size 255933 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png index f899ad5d43..ccfdd7d9ed 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ca032575e4ebd75a2ebc5a4cb9f966e48cdfa56f4ff42d6d9309fa3577a1adb -size 310609 +oid sha256:fe6cb72de5079be2ac02ec39f74bac0d20492cd6f5509b71ad9cfc4cc52955d3 +size 326468 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png index 693a36b7dd..5f51b27f5e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3dbb8ef39b6a7a0c2f576d8bd101aa97c89da696f23e6d5836846494471c01a1 -size 55799 +oid sha256:3274314ab9e39c23a6b9d66adf80ac8bb7b364ce60a300f96582374a18bc821f +size 54590 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png index b1850a50a5..e1f6ee6430 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f1dd0da147212e13a6c0ad5ad8f85ce55666c444076c238ea27eba5605cfab1f -size 56909 +oid sha256:5e27c7c51fe07cca51b92a601aa9c58deab609bfa5e82f8f6f88eaab6ae59ec3 +size 64307 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png index 4185f46b51..5289eaca63 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:085963fcb425147d35a9ad947a7a3e4ba4712bd3d2f32e93099ff15cffe83b88 -size 64761 +oid sha256:65f9ee083a73669a0d3f9d88141a610ea53f3c39e89d8f706d414b04555b4764 +size 59321 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png index 2e70435fa5..d3f7da6320 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a3667390d9d88894bbdd5d72b0b714b29c9ff160653e310e97b0da87b6ba8b0 -size 54724 +oid sha256:8a94a4cc176b5d52ef9b21339749324adeef9857b6cdaa2ae973903e2408662a +size 55904 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png index 0f6de1b844..bd3b0196d6 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28512b0fda0b71b98eab03cee15c9d3163825b1a3545670cfa277ddd5434c87a -size 58417 +oid sha256:6f7f7415abbe51858f938e16ad03c6aab983fb3d5e2a53a028bae3c031446646 +size 67848 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png index 6c774d4cec..f4d51ccc61 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ed6a5f1a2055e19a6b8fb0af4738845bd2184f755c323022c52154cdfa07e05 -size 60909 +oid sha256:8a8f5aaaa7ebb0e61b07867590d99a8791c5d04cad1ce0f334d23dce6f26c6e6 +size 67276 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png index 02d0fc8eeb..bd73e322ab 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f77d8243c23d3f37dff86dec53671cbde9fafe1314f6e6b445abdfe47d751ad6 -size 56187 +oid sha256:12020ba97720f2374f9ace172693f08be01522e4ca30d3970efa91d802581e81 +size 3657 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png index bd73e322ab..93622cedfe 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12020ba97720f2374f9ace172693f08be01522e4ca30d3970efa91d802581e81 -size 3657 +oid sha256:87193a7c9eb9d40234970e5f5be02e80f2b3ffe91e2c9ca6afd58b8bccdd5911 +size 55075 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png index 0cf6c10c4e..bd73e322ab 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49f0bb16d3d57b59242e704e3dd4e8885c59721a42129837ff6a4f0d7d02de11 -size 54433 +oid sha256:12020ba97720f2374f9ace172693f08be01522e4ca30d3970efa91d802581e81 +size 3657 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png index adec213a8c..bd73e322ab 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ebee4aec3dbab6e1bb05fda7c5e423f0e931560743d0f0ccbac6b70fa9ad808 -size 52921 +oid sha256:12020ba97720f2374f9ace172693f08be01522e4ca30d3970efa91d802581e81 +size 3657 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png index 12bd190dd1..0ab00359b7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce15ca9f89274620728715e3448ce671e2253f4fed22a44035460e9a3709ca0b -size 647404 +oid sha256:7b5e6f4c5ec97e67bb20f03f25f18d9f32d5d1aed5ff30d161b331894ad03443 +size 647384 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png index 160d58f9ec..0ccdcbb603 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4e7b28b78cfbcbb6abf00317eea945859ac6465cecf81a3ad15801b0de721e4 -size 646216 +oid sha256:d0424685437a433b35b589668b409d55f26ab41bcae01af0121a54033c56a8c2 +size 646194 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png index 0435f55de6..ab6392a424 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff839731834d7d0c94fa9a2c412014ddbaeaa053c5af1c8cbd00a3f3a583f364 -size 379511 +oid sha256:94f89a37549a3f198442db467bc7d8539f0b5649d97927bfe731f090d6dfb03d +size 379505 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png index 9cbd5c1d95..f0b5506b89 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0e170b4bea262de612bf3374d475fcd25470ff91b7554d20809c51857a78cec -size 377963 +oid sha256:04ea6c2947a21d3d4a3d43e9c4750c4f2a49056536a4da3f34c95df8353e9de3 +size 377990 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png index 1f0fdabe6d..3ac59caeea 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8bb5a6de6601da8d7fe59f110a054577e2e25dae4bb449f60c04e7d7a1b9551 -size 365073 +oid sha256:1a94d5198e27338675c0e15df5874ba3272ee315b8d77291dc7ccc7c754ed1f4 +size 365082 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png index 86018764ac..195aa04fe2 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8344427a0fe87cbf0883ff53a9ee4dcf90976a7011f6618d85961df48b2c53a9 -size 370228 +oid sha256:76e5ae44362792d570cd82f3910c8bd0acde509b8418c5892e0385e7d636f042 +size 370223 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png index 058a3992c3..29771235f0 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00fe1e676086766a3eac3e4457eff52c989789fc5d8d0df2624c019ed25ce9af -size 363322 +oid sha256:60b9f2b920cd2a2a414805d1f0f77925232d9ae60bd607fa18d4395cda2dd73d +size 363291 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png index c6d4541184..db4ea8296a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c00e504593d8eb61b598f05f46ee4d591757975a80c0b4227b240847d2cd4868 -size 368510 +oid sha256:2f0d24b8011564ee7a8b2f8c2e6a24eda668ee42d451d862f884acb7926aa06c +size 368529 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png index 84d9d4e5c3..94428abb56 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c609a633ab23eae5729ecb57a17a385bac65e81cbf0180c01e0847f1028039e -size 347890 +oid sha256:36d7cf2510d546d2b330b1d1b1674ce58df0558c547e443794a1e7b2222add87 +size 347897 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png index 7ddac02717..73b7e674e2 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4ac79f3b8fb4e231244bd96b244a4f2526d6ed28d558f9d50734d83bbb74992 -size 363909 +oid sha256:991deacdfb7bc371a2df960a2364ae3b2187e588aa5afe7f39409e367040dd9c +size 363893 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png index bac23aab98..22ff4965d1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e30686c7c037ef102717e0316543faf4d3ed2671c21eca96a667150daab380d6 -size 346614 +oid sha256:787151f8f242a8794a804bf14831f6ca680814d617e85feb3652103e8a0f20cd +size 346565 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png index 4e17de6908..b6e4650d9d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1e15400fa2c5ee6087316501445c551a3932b65e16de31e68acc5efbd16a559 -size 361603 +oid sha256:2cd357a1fbaa425eca95587bff2f768a45e773f983f75664875653ba1900e534 +size 361625 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png index 01d4dca84b..67490ba700 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef2bf449671b652cb152b3baab92ab100eab77a54229ab49daeab156f2f484d0 -size 369724 +oid sha256:f4431e2d9f30123949ea5676ce60f642234adcf9b5a33f2acc3f80ebf6609a66 +size 369730 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png index f0dfbc3321..1600afff71 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7a6302320bd1e4258e03487539bf46b0b12781dfc8a22b3f9f267017a6146fa -size 354751 +oid sha256:273351cc9c533b2424859a0de00a8a3786628abc3a88175705c4205dedf7e9aa +size 354737 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png index e38cf03ad6..c4943b892c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41a0e404dea7f52359b24b3ec3926b1405429b165cfed44c63b3922ea6d3cb46 -size 368114 +oid sha256:4c410e853089cc2c4a2c48a00dd9a8babc8095aed72bb4625057444710559c6c +size 368129 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png index 68f8148879..b5dac8434c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ca8cf82aa8ab1bdad56a27584b27789402aeb7ad8fadd038280a376fb75a72c -size 345094 +oid sha256:1a92f8af94682facac66d4aacb6e49f345561c15be2f8b689041b78aeaaae831 +size 345062 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png index 8aa2cca147..1b99468924 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:02ecb903124273e24151ea2088f6b99144f7dcadba00a582b6210dd23835b5c9 -size 357743 +oid sha256:3a9509411777f342261cab58b92a022b3c9f8f0f7a9e4d0527120a91976575f0 +size 357733 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png index b5032c29b3..cececc229a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dce23463c335d01eb67293cfcede5d85e96ab2ba035cc9a188d42e8e6866bc63 -size 356831 +oid sha256:f0c6dafaa41849af00d5097a85bd88e3a054a0a9e41c0466a08e6ff8dd6102b2 +size 356857 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png index a332744358..14a40a5676 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:588967869cbd25ed8b82aff5618175660a8a9e45a5a861b1c7867cf1913f575b -size 364646 +oid sha256:a11044fe90227f1f316a1ade8587e545bb620e11cca76865fc2eb83cdb817fc5 +size 364655 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png index 133fd3903f..d7b969fd5b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:426d1c844a8c88281c5fa4c61916d93278fdfd38f8cdf2dc54bf1a821f2de3eb -size 397167 +oid sha256:2528df873713755f542f61cd794080929586c1bd5f316c0378ce9e837c21d50b +size 397145 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png index 0041a870b9..63bd9c3f6d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2bd77c08be705c66225f16c29fd6826decd92b41922d0f8ffc0d1a63d8c59d2 -size 356193 +oid sha256:a6cd94664f89bdad2a9761ea8b20acb83fed3ed7cb5be41af8fe4852643c0bfc +size 356175 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png index f67906a37c..9fcf389708 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffa68c8398d789444151855496e5df4ffa03218a383db6ea46fd4f979829b823 -size 355831 +oid sha256:9daeee1aaa04247da3c8bb5aaae9947aab1ed2d2057995e53d6ebdb63e5cd356 +size 355848 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png index 6e83d6491d..d235670def 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34152c742abc486a48234e22101833bc163cf11979890d06aac41e68e3c404d0 -size 364846 +oid sha256:a00bd766a00b7e7c6615fc53bf469b3ca5056a6c514c096431ae38d99d604306 +size 364845 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png index b10f9741b9..0021619c44 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ec1b0244dab22414c6719b3309426e13128d0c70fb67dc66cd8a60d42521fd0 -size 355411 +oid sha256:7b23d3a37774283067a8919b8c332b1bb5a79e90dceb7216b484b985491282e5 +size 355389 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png index 77abe20982..422c324695 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c3d87862c6d7fc88d0f23cd54c5b9f77028992fdd7c282a195b964dd17e85ac -size 367855 +oid sha256:a1df8d22766981fc76d6d767dc3f40d98cadc7c51be20417b067b91af604232b +size 367884 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png index 1d34761a9b..448bcec677 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7d9b5fc0ff5d7828ad6f4dc0c9bd9520b4920553da97ed7b4e030ee844c0208 -size 353054 +oid sha256:fe0daf9161bc2da194992a2fc9505bf2009c5dbed78cdb35d2eb7ed1380a7df5 +size 353037 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png index 5243218ae3..c081fa16a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a85db2f5958db867578663533f204b2e803372d470da890fd8056ffed0cf46c -size 366254 +oid sha256:6a92bcf5885322e11a706071969fb674b1f6e60e021af153aaa125baa4a602ae +size 366285 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png index b6d1f07279..47a9a6b630 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bee12a6bdb18fea5ac6997671bfcb903296da7278cf69b2456330066ca31dfb -size 342870 +oid sha256:54372845e922d047408dd960d048b9b5eae2263d589117d8e7d197741aa34f32 +size 342827 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png index ceca184bd7..5560c21967 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e82011a72f36889c53ab61ed2b9947c7fb5f868c9b7010904fc10f4ca587082 -size 356082 +oid sha256:af4a48ee80535f2c0a226c4ffa4344054fb63b7b0a88ea2c030a4eb8dd5e9644 +size 356078 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png index 5cdad1df92..5cc5cfe4fc 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e2d8caf5aaae7d3406c703288e491f328552f0acd18e56afd5265b4ceaf1cd1 -size 355272 +oid sha256:6524cc87257cc9d3f135904f4e14594b56df695e71fe576418d2319c472d710b +size 355285 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png index a431a9429a..e9d85990e8 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93c2df04d65f6078ce3f7067fbae8a7317ec42ed0fc013dee962a3953269f3b7 -size 362813 +oid sha256:66d4b6ea4a697735bbb0fac936b89cde99c5077973ba828894bf50b5dae233b3 +size 362848 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png index 0eb106f4a9..b51a89586b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:217ac95bf476c53d2dc6d3d0143b21312bf38b8ab86c8dfd4f9360582b584f9d -size 395476 +oid sha256:e642229c58169775fe702769a306bbef22698516ae6f7ac75a761213c3f195fa +size 395446 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png index ae8495618a..aa322fc2e4 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08057fdc1e1184720b438ddd2a8753f00791baf2456cc30b45de8e6bdeb57e8e -size 354404 +oid sha256:5fae87094ea13b14e2dc7c16ca96e4f8096ff39e962799468fb4ead4c666620f +size 354381 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png index 9d617f6688..40105c1498 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dabad9dcd5b8f0d4bb964a0187ad7c94b023b04552dacd29a077daddfd48d550 -size 354052 +oid sha256:5ed24d190f10c079f8cf176404372d40eb25cc78ce595a22febc540315057b86 +size 354075 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png index 1a8ea3e4e9..0cfd1dc985 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66aba7b4271a990fac85b0bbc7cbdc371fa1e38a4da5bb4e2f62a80b21cb3a8f -size 363012 +oid sha256:545c9e8933e420cc3f8fbba0fbd5162c946c65a20721faeb77c568596ca720cd +size 363045 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png index c8306263a0..c9227ebf94 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:756f0cff90ddd61adce2f467c0384d74d430f6d51c091e4980e45bcc9ee6f0fd -size 353705 +oid sha256:e614f4a8657637c584a283d69124236d00fe1251c2b8d739c5688a57a6fd152e +size 353693 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png index 144411969f..7a5e5e1512 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84415250503d5e586af7b7504aaa915c1e24dec0a3d28446487c474c354e05c9 -size 55534 +oid sha256:0e3467a7606a5a7beaafb510faf008ed567b1e3a9915ede6e2ec09c491af0bb9 +size 56552 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png index 52e023ec49..843adcdff0 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8420417cf8ba717747dead34b9646515f35eb94e1f71aa952b77c3ff433043e -size 49684 +oid sha256:446ae29cbb211665f6d91129a7dee0f718b407fcb2a64d3f2c0e962e1cb47af7 +size 50168 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png index adabb6dde5..ee23a7111d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b071ba6e99b68dcc75f5aaed3a879f2e30614c954b88c6dc05e1dfec57f1079 -size 65771 +oid sha256:38ae233a128e887efc48da73be3d6ce50a7095601315ed49a3c9cb209ffdda26 +size 58352 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png index 6c2a7e8460..2a5991cf59 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:984536295516b542ec6aecf7d07baa3c6dfebbc66403b6d4258a6217616cec93 -size 39991 +oid sha256:115c725873fb206d8f732a8297a6548ba4fa45d7328196b79a2d9e35545de7d5 +size 40347 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png index a1dc68a3d5..7ce91f4684 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:871d92a7565dd233ea87d53658be4e5202775def8e17fea8d386b8975c3b065e -size 53868 +oid sha256:7b749e1260d6ba2b7fef5ae9efd58612f6d67d1788f6c1e5cebc22d58d0fd7b7 +size 58563 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png index 479d07ae5f..f6456f0ec1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d4f03ac10bbb676d272dba906e758b25d633a1da1e249fe23ba825f8d2f4901 -size 55298 +oid sha256:22153168f6f3e84ab789faea29c94fffbc1a5e4c1b9a98deccb0460192fdde23 +size 57213 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png index c90dc8d08f..6a3ffb84b6 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e48a364a28a67d7d090faa23ef88b27dbd47f0a2f552857490728cb6952ce08b -size 53454 +oid sha256:48b91e52306daa750b4b5792535e1f1865ae3b74a36215376a51dafdd0dcd506 +size 54837 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png index 6c0a7b4edc..abf8eb7086 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5aa22c3d79a392625ae231abc046f07f7cdabd91613d5e6f07aede5382aba9b -size 60410 +oid sha256:1e79e1cc59ba62ca8eb99300eb520b05d7ac6d52c077e3010c03b474865b15f4 +size 58374 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png index ac1d94a4dd..5d4f5bdaeb 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be5b086ab6934bae88b39987bc51e709c5adb81bdd04fa028e97832c725fd68a -size 50219 +oid sha256:08fb14008b08d86755c3473e7194ef0a39bb73c3f0d48122ca80c6a019ec98eb +size 48676 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png index 2209fcb54f..b3af9d831a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd0de7412503e93c33cf92dde0cff38bb68555ef176080d913320af5e4942e7a -size 58642 +oid sha256:ce795c4e32a1be3d57f6b1c87588d8c7ed0442996ab9d8a96f1ec5a07627e598 +size 59663 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png index 82ef5e7d6a..2c88d322d4 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41c4bd5cd1bcbedd91de0a64967fb3f3e03b5f0937ae1ea1a93a7c4a1b24a4a0 -size 56262 +oid sha256:963fcec9647d0e5e57bd3fbe3edd389c17be7558c1da7e93fa44f34db34307ee +size 57726 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png index 135d84dd90..281aad62a7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ff538a81d013fdc90dd7be466f710088eea93e081d23448b0731d301a4a3bb2 -size 60702 +oid sha256:85f6f4c64f67faa99b9af65307a0cd3b46a68db52503c9c929375408597995d9 +size 62094 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png index 89fc6708d2..cfa6e4dbc3 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89467c5b84d0a15210d4013013598d9994d3005a2299e44583d78355e6718ad8 -size 55538 +oid sha256:7a3281f35461edb900ea434c68543dc684a5fad7dab7c9aabac510bc59d442b3 +size 56479 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png index 17df34a50a..5f3a619097 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6548f5bf1906e1dddcbcc03bb8f76b42e604075751ea8efbc3ead826bf4cfad7 -size 51258 +oid sha256:19d14e3d779b163053c28e66d3381f0cabe3b540744606e955cd733f9e044089 +size 51713 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png index b3109663f2..6e8468054f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:10bce6df962a5198f019f509b1cdb55e28195587de4b74742ddc29833f1333a4 -size 69216 +oid sha256:262e8d4b8daa2d25593834fd0ab5d7019b0a3dd13707aa96e0701bf9240c6525 +size 59809 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png index 1ff5ace3e3..b32ee34f99 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d875f83a3a1977e2a2cf151dcf9d731108a8590eb3643eb057727421eb1e955 -size 38812 +oid sha256:bde66c53d81f882cde9ed527e8ff82c7e9d6cb1199b440d4b721659f7189bb42 +size 38906 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png index f425dd2c4c..f4ab74029b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd6dc088e16f722b36c2198d6a8611af87dec2451f94ca3c2b6880b97f709b98 -size 54095 +oid sha256:51a5b682243b969bc2d7d609decb823b0509d240f3ff14ee78319260af76a47f +size 58740 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png index b0cd5bc6d4..3974618f39 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e900ff75d02c9db4200e2032444dd7e2f8635973cca3389dd96d7d2bb734061 -size 50757 +oid sha256:718d5c9f24c114f3bf88e000b637ea19598d7bb9b97001ba137fc12a6a1285e4 +size 52608 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png index 486d5da5c6..0ad1e67deb 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e482603e6d03fffb57d4eb135edb6209c748c51b1f07e638a4ccf133b1b87a79 -size 52920 +oid sha256:82e6e38576a507b19e58fce9dda0734ec00bcedcb0baf8f4c7919dbaaf7d2b85 +size 55046 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png index 5a877a59e1..a009d0f584 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a582219d3143acb08563193bf7106e1ee8f6612e359d0a192e9a2f67c6799e3 -size 55375 +oid sha256:478851e9e2e8c80095aa552b98a5d574749200ff49899c9472c913b07459c4b5 +size 53758 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png index 0383bb8f95..f82a7a8219 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c88b3ed8cc69dee59d4d0a726a9e383d00f2815b6525315f7dcd96f9c25ec2a -size 45618 +oid sha256:22ea7fe121a63b35a2c2bb47609e692f8bf2b913871a6baf3d420c04334c6c4a +size 44344 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png index 952751a962..7261c9b671 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed13a2778b8ced86ee41d5460d274b42b714a87f59749e3b26bf75114ef40dd9 -size 58265 +oid sha256:7cf3d727da78b39ecf24d4df7db0884af9008db67cc92274849c1b0978f87726 +size 59252 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png index 0f7fe2db38..cfa62a6626 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a576d52b840730334a38046cf8b57993693fc760b4fbdc00376cb48b7268d76e -size 55890 +oid sha256:bfe848e4c365cf4b4d663145f74e5f8b3f47a4c8994961c530603f0248400316 +size 57117 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png index 4d6afdbbb1..89065e6bf0 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35069d4fae829d9afe479308dd72ad025fa011290d23133a231f4e70f601ee75 -size 62987 +oid sha256:a966c9a1fa4fdd933f4ef9239592e90458a2819ff4a45c857714d2e38c0df5b2 +size 64405 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png index 074820e157..7cca911c45 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7caacb336793b8ae4b83e9a10a82325e25e8b3c036f881898ed28688250eb6e0 -size 55744 +oid sha256:26e14b77fa3c3aeaea58782309e6de8b9047e07945009322990ebf5f4c283309 +size 52770 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png index 7cca911c45..2af459c158 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26e14b77fa3c3aeaea58782309e6de8b9047e07945009322990ebf5f4c283309 -size 52770 +oid sha256:28a95d4b7ebca92c4e0467f3ecd9acdaf7f6257604d190f5d088a9a25e87d637 +size 55305 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png index a68f886f73..616c06bf46 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5a2e306087a967849f61872b56a83f194d1a76df5723f0bbae6e6e2f4a98c63 -size 55725 +oid sha256:0ce2519906f1aaf34700ca9ba4bdeaa72030f1f5aff3f7e24352a1435ac67d98 +size 53966 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png index 36d19c6b72..616c06bf46 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7dfa2767c55a81d3f121b8fa55443b7b05f9369e91639b80e0be349863799ad7 -size 55438 +oid sha256:0ce2519906f1aaf34700ca9ba4bdeaa72030f1f5aff3f7e24352a1435ac67d98 +size 53966 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png index 82f344f714..2c15fd6a37 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d96eefa01a5de803b0cc9d9109c20df86eaff01b73ff24a91945b43d53d5d01 -size 11960 +oid sha256:7260abe16fc2ab4f6ada97ea0f756dc5c9908ff3b61d6ec1723ffde302bb3dc5 +size 11955 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png index fdd8fcd5a5..7c422591ad 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22678a2a8505e1d9d5bfcb44459b6fac69c11a4bf72fae09c48ffa2e54f5a110 -size 12128 +oid sha256:2e16fc1daa0f284c442f66591e1d1f59161272a67998d26740b0b403bfe82495 +size 11866 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png index f6e27475da..acb8afa74a 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ee89826c8aa2ac0371ed31959243f62bf9968806a7b27117cd7b4d849423a33 -size 11667 +oid sha256:bed7565420e11c43acc06896ef298683d2f2dd1dc16c430eab7018e9ead9937a +size 11652 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png index b1bb69efb8..e0a4cb1c97 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e27cb9e501ccbac54926f0252b4098867643a2d2683c5b267f86ad99edf6f5d7 -size 11817 +oid sha256:87811df502c760816e74beb006341627857c39380ce715f1a747ac9fce77b226 +size 11516 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png index 73b8b8e50c..5c597300e4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8a3ad99763929d2783147814e44fa9ec7e7efb7b99949e3580f23c38fb5d971 -size 9398 +oid sha256:21aee17ee9a292bb2f0d3d2ec2db565ebb8d5df753454a5a002f033ed5ed39c5 +size 3853 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png index 5c597300e4..f8a1f24b34 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21aee17ee9a292bb2f0d3d2ec2db565ebb8d5df753454a5a002f033ed5ed39c5 -size 3853 +oid sha256:1342b6427f5b2e3075ae3ccee60bf1d3b11afaccf3e152643d6ef2d65c21b54e +size 9108 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png index d47bf90664..4ae7279fe7 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:520cae2544153ba45f8fe4b021286c2d22da06aa1b60be7f1b13879723ff994c -size 3664 +oid sha256:410a8596d4c70b9a3856d959d6d71ade8e831e8581ff410c9900957a93b3eb50 +size 8114 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png index a87d89c701..d47bf90664 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f3a0cb5d9bae6725a1c527f3d9575aabf85d3edf63a28d9faa23f4f0b65e2d0 -size 7624 +oid sha256:520cae2544153ba45f8fe4b021286c2d22da06aa1b60be7f1b13879723ff994c +size 3664 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png index 58546b8522..ee5c582240 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30357bf18792db5ab20085010251170a724ffe8c00313facd04ee704e806af64 -size 10982 +oid sha256:75de341f705bb22955f9399180519b65ad59c2e1917317d56e50ea4db78f35bc +size 12102 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png index fedead0fa8..52fd9b9693 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4811667dec0f32accc5e2c8a5dd5c0b34017fcff8d1e0376ee7a4708426bca0f -size 9838 +oid sha256:58b9491aaa5554b89d86c62a5c9c44975367538c6baa5562f8dc5768f76d8264 +size 8705 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png index 83302d6ba4..38ad0e4b56 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16dee1dfe746cd40dac9fe8f0af3ecb42cd14bab5cc490917b589beed065ed1b -size 14686 +oid sha256:326d59cf768b2dbd96eb7964d54c8eda509a5cd57c6e62087625e3314da0bb4d +size 13658 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png index 4da2a98aca..687e0d436a 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef0dc4d53daeb986f378b6e4d85928b4b6729336c89cf21e642c88c53688a3ab -size 10292 +oid sha256:d57df7110371c9224e232b707d6a1b10ce14afcb56c803a78220792f2a0d4cf2 +size 11354 From df925c29e4005213355e671eef52e5b1556016dd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2026 14:06:22 +0200 Subject: [PATCH 023/338] Update dependency org.jsoup:jsoup to v1.23.1 (#7332) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Benoit Marty --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ce602f0e93..f365c5cb3b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -199,7 +199,7 @@ serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-jso kotlinx_collections_immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.5.1" showkase = { module = "com.airbnb.android:showkase", version.ref = "showkase" } showkase_processor = { module = "com.airbnb.android:showkase-processor", version.ref = "showkase" } -jsoup = "org.jsoup:jsoup:1.22.2" +jsoup = "org.jsoup:jsoup:1.23.1" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:2.2.0" timber = "com.jakewharton.timber:timber:5.0.1" From 9b39843e694973d8bbb09d27b01da1e00c18a7f0 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 7 Aug 2026 14:22:52 +0200 Subject: [PATCH 024/338] Add withProfilesExtension override to FakeFfiSyncServiceBuilder --- .../matrix/impl/fixtures/fakes/FakeFfiSyncServiceBuilder.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiSyncServiceBuilder.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiSyncServiceBuilder.kt index 2cb67f2519..5a2615aa2b 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiSyncServiceBuilder.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiSyncServiceBuilder.kt @@ -15,5 +15,6 @@ import org.matrix.rustcomponents.sdk.SyncServiceBuilder class FakeFfiSyncServiceBuilder : SyncServiceBuilder(NoHandle) { override fun withOfflineMode(): SyncServiceBuilder = this override fun withSharePos(enable: Boolean): SyncServiceBuilder = this + override fun withProfilesExtension(): SyncServiceBuilder = this override suspend fun finish(): SyncService = FakeFfiSyncService() } From 06862ff3b2b9bf4208eec6b247c871f65ee42ece Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 7 Aug 2026 16:30:03 +0200 Subject: [PATCH 025/338] Test synchronizes on profile persistence instead of advanceUntilIdle --- .../android/libraries/matrix/impl/RustMatrixClientTest.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt index 4ea4bb64e3..ea40104533 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt @@ -32,9 +32,9 @@ import io.element.android.services.toolbox.test.systemclock.FakeSystemClock import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Test import org.matrix.rustcomponents.sdk.Client @@ -72,7 +72,8 @@ class RustMatrixClientTest { @Test fun `retrieving the UserProfile updates the database`() = runTest { - val updateUserProfileResult = lambdaRecorder { _, _, _ -> } + val profilePersisted = CompletableDeferred() + val updateUserProfileResult = lambdaRecorder { _, _, _ -> profilePersisted.complete(Unit) } val sessionStore = InMemorySessionStore( initialList = listOf( aSessionData( @@ -97,7 +98,7 @@ class RustMatrixClientTest { ), sessionStore = sessionStore, ) - advanceUntilIdle() + profilePersisted.await() updateUserProfileResult.assertions().isCalledOnce() .with( value(A_USER_ID.value), From 8f8c3af8c557b5b9ff95938c56575a8c4dc1ed76 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Aug 2026 16:42:49 +0200 Subject: [PATCH 026/338] [LinkNewDevice] Log continuation / cancellation failure. --- .../impl/linknewdevice/RustContinuationMessageSender.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustContinuationMessageSender.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustContinuationMessageSender.kt index 98cf29418c..f4f17fb9a7 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustContinuationMessageSender.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustContinuationMessageSender.kt @@ -11,6 +11,7 @@ import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.matrix.api.linknewdevice.ContinuationMessageSender import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext +import timber.log.Timber import org.matrix.rustcomponents.sdk.ContinuationMessageSender as FfiContinuationMessageSender class RustContinuationMessageSender( @@ -20,12 +21,16 @@ class RustContinuationMessageSender( override suspend fun cancel(): Result = withContext(sessionDispatcher) { runCatchingExceptions { inner.cancel() + }.onFailure { + Timber.e(it, "Error cancelling continuation message") } } override suspend fun confirm(): Result = withContext(sessionDispatcher) { runCatchingExceptions { inner.confirm() + }.onFailure { + Timber.e(it, "Error confirming continuation message") } } } From 668243f658dc13b5f76f959f04a43f3172bacc2f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Aug 2026 16:43:52 +0200 Subject: [PATCH 027/338] Use value instead of first(). --- .../features/linknewdevice/impl/LinkNewDeviceFlowNode.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index b1492f53cc..afa570f0a9 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -54,7 +54,6 @@ import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileStep import io.element.android.libraries.matrix.api.logs.LoggerTags import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -214,7 +213,7 @@ class LinkNewDeviceFlowNode( private fun onResume() = sessionCoroutineScope.launch { // Application is resumed, if the step is waiting for auth, send the confirmation - (linkNewMobileHandler.stepFlow.first() as? LinkMobileStep.WaitingForAuth)?.let { + (linkNewMobileHandler.stepFlow.value as? LinkMobileStep.WaitingForAuth)?.let { Timber.tag(tag.value).d("Resuming while waiting for auth on mobile, sending confirmation") it.continuationMessageSender.confirm() } From 33979b65747eef651a646c7809a17a1c81ffb32b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Aug 2026 15:02:55 +0200 Subject: [PATCH 028/338] [LinkNewDevice] Add a 2 minutes timer to scan a QrCode #7303 --- .../linknewdevice/impl/LinkNewDesktopHandler.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt index a8a8ff14b6..1b0883ec4e 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt @@ -12,10 +12,12 @@ import dev.zacsweers.metro.SingleIn import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.linknewdevice.ErrorType import io.element.android.libraries.matrix.api.linknewdevice.LinkDesktopHandler import io.element.android.libraries.matrix.api.linknewdevice.LinkDesktopStep import io.element.android.libraries.matrix.api.logs.LoggerTags import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -23,6 +25,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import timber.log.Timber +import kotlin.time.Duration.Companion.minutes private val loggerTag = LoggerTag("LinkNewDesktopHandler", LoggerTags.linkNewDevice) @@ -40,17 +43,26 @@ class LinkNewDesktopHandler( get() = linkDesktopStepFlow.asStateFlow() private var currentJob: Job? = null + private var timerJob: Job? = null private var handler: LinkDesktopHandler? = null fun createNewHandler() { currentJob?.cancel() currentJob = null + timerJob?.cancel() + timerJob = null handler = matrixClient.createLinkDesktopHandler().getOrNull() + timerJob = sessionScope.launch { + delay(2.minutes) + linkDesktopStepFlow.emit(LinkDesktopStep.Error(ErrorType.Expired("Scanning QrCode took too long."))) + } } fun reset() { currentJob?.cancel() currentJob = null + timerJob?.cancel() + timerJob = null sessionScope.launch { linkDesktopStepFlow.emit(LinkDesktopStep.Uninitialized) } @@ -59,6 +71,8 @@ class LinkNewDesktopHandler( fun onScannedCode(data: ByteArray) { currentJob?.cancel() currentJob = null + timerJob?.cancel() + timerJob = null val currentHandler = handler if (currentHandler == null) { Timber.tag(loggerTag.value).e("onScannedCode: Handler is not initialized. Call createNewHandler() first.") From 4485c8a4c018dc2e1965316f54a845da9c865b1e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Aug 2026 15:35:09 +0200 Subject: [PATCH 029/338] [LinkNewDevice] Fix second and next attempts to unlock using biometric failures. `cipher.doFinal` cannot be called several times. --- .../lockscreen/impl/biometric/BiometricAuthenticator.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticator.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticator.kt index 74d36b0834..f08d2fc1af 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticator.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticator.kt @@ -59,7 +59,7 @@ class DefaultBiometricAuthentication( private var cryptoObject: CryptoObject? = null override suspend fun setup() { - if (cryptoObject != null) return + Timber.d("Setup biometric authentication") try { val secretKey = ensureKey() val cipher = encryptionDecryptionService.createEncryptionCipher(secretKey) @@ -126,6 +126,9 @@ private class AuthenticationCallback( if (this == null) return false return runCatchingExceptions { doFinal("biometric_challenge".toByteArray()) - }.isSuccess + }.onFailure { + Timber.e(it, "Failed to validate cipher") + } + .isSuccess } } From 2b2d86a804bc26f2fd287575892a4f5ccb177dd1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Aug 2026 15:45:16 +0200 Subject: [PATCH 030/338] Use Kotlin Duration. --- .../linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt index 21071a6831..c496c69f92 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch import timber.log.Timber +import kotlin.time.Duration.Companion.seconds private val tag = LoggerTag("ShowQrCodePresenter", LoggerTags.linkNewDevice) @@ -63,7 +64,7 @@ class ShowQrCodePresenter( linkNewMobileHandler.rotateQrCode() // Ensure that outdated data is not rendered too long while rotating QR code loadingJob = launch { - delay(1000) + delay(1.seconds) value = ShowQrCodeState( data = AsyncData.Loading(), ) From e66efcfc4392df4c656539a6465476d420ece935 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Aug 2026 16:40:26 +0200 Subject: [PATCH 031/338] [LinkNewDevice] Disable QrCode rotation #7303 --- .../impl/screens/qrcode/ShowQrCodeNode.kt | 10 +++++++ .../screens/qrcode/ShowQrCodePresenter.kt | 12 ++++----- .../screens/qrcode/ShowQrCodePresenterTest.kt | 26 +++++++++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt index 20bd50f488..7032458860 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt @@ -39,6 +39,7 @@ class ShowQrCodeNode( private val callback: Callback = callback() private val showQrCodePresenter: ShowQrCodePresenter = showQrCodePresenterFactory.create( initialData = inputs.data, + maxQrCodeRotation = MAX_QR_CODE_ROTATION, ) @Composable @@ -50,4 +51,13 @@ class ShowQrCodeNode( onBackClick = callback::navigateBack, ) } + + companion object { + /** + * Max number of QrCode rotation. + * When QrCode is rotated this number of time, a TimeOut error is emitted. + * Set to 0 to disable QrCode rotation. + */ + const val MAX_QR_CODE_ROTATION = 0 + } } diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt index c496c69f92..1a5eac0522 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt @@ -33,18 +33,22 @@ private val tag = LoggerTag("ShowQrCodePresenter", LoggerTags.linkNewDevice) @AssistedInject class ShowQrCodePresenter( @Assisted private val initialData: String, + @Assisted private val maxQrCodeRotation: Int, private val linkNewMobileHandler: LinkNewMobileHandler, ) : Presenter { @AssistedFactory interface Factory { - fun create(initialData: String): ShowQrCodePresenter + fun create( + initialData: String, + maxQrCodeRotation: Int, + ): ShowQrCodePresenter } private var loadingJob: Job? = null @Composable override fun present(): ShowQrCodeState { - var qrCodeRotationCounter by remember { mutableIntStateOf(MAX_QR_CODE_ROTATION) } + var qrCodeRotationCounter by remember { mutableIntStateOf(maxQrCodeRotation) } val state by produceState( initialValue = ShowQrCodeState( data = AsyncData.Success(initialData), @@ -81,8 +85,4 @@ class ShowQrCodePresenter( return state } - - companion object { - const val MAX_QR_CODE_ROTATION = 10 - } } diff --git a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt index f92cf66102..db7c3e8c01 100644 --- a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt +++ b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt @@ -91,10 +91,36 @@ class ShowQrCodePresenterTest { } } + @Test + fun `present - when handler emits QrRotating, the presenter does not emit anything when rotation is disabled`() = runTest { + val linkMobileHandler = FakeLinkMobileHandler( + startResult = {}, + ) + val matrixClient = FakeMatrixClient( + sessionCoroutineScope = backgroundScope, + createLinkMobileHandlerResult = { Result.success(linkMobileHandler) }, + ) + val linkNewMobileHandler = LinkNewMobileHandler(matrixClient) + linkNewMobileHandler.createAndStartNewHandler() + createPresenter( + linkNewMobileHandler = linkNewMobileHandler, + maxQrCodeRotation = 0, + ).test { + awaitItem() + linkMobileHandler.emitStep( + LinkMobileStep.QrRotating + ) + runCurrent() + expectNoEvents() + } + } + private fun createPresenter( linkNewMobileHandler: LinkNewMobileHandler = LinkNewMobileHandler(FakeMatrixClient()), + maxQrCodeRotation: Int = 10, ) = ShowQrCodePresenter( initialData = "DATA", + maxQrCodeRotation = maxQrCodeRotation, linkNewMobileHandler = linkNewMobileHandler, ) } From 8c2cf9d95e6e849e2c366c98a73768bbae7d3514 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Fri, 7 Aug 2026 08:41:59 -0700 Subject: [PATCH 032/338] Reference the Figma design on the account provider screen --- .../ConfirmAccountProviderPresenter.kt | 2 +- .../confirmaccountprovider/ConfirmAccountProviderView.kt | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt index c3f51a5177..f83bce0510 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt @@ -91,7 +91,7 @@ class ConfirmAccountProviderPresenter( userInput = event.accountProvider } // Validate (and persist) the chosen account provider before proceeding. This also enforces the - // enterprise account-provider access control, which the login submit does not do on its own. + // account-provider access control, which the login submit does not run on its own. ConfirmAccountProviderEvents.Continue -> changeServerState.eventSink( ChangeServerEvents.ChangeServer(AccountProvider(url = accountProviderToSubmit.trim())) ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index 470b59926b..8be8bab7e8 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -56,6 +56,12 @@ import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings +/** + * Account provider entry screen, shared by sign in and account creation. + * Figma (Compound Android Templates): + * - Sign in: https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=41-10251 + * - Create account: https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=41-10239 + */ @Composable fun ConfirmAccountProviderView( state: ConfirmAccountProviderState, From d7e6b64265ceae0549dfe206eeb70e17979b75e4 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Fri, 7 Aug 2026 12:46:11 -0700 Subject: [PATCH 033/338] Dismiss the keyboard on continue and restore it after an error --- .../ConfirmAccountProviderState.kt | 4 +++ .../ConfirmAccountProviderView.kt | 33 ++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt index a934378507..bf6b0cb0e7 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt @@ -29,4 +29,8 @@ data class ConfirmAccountProviderState( val isLoading: Boolean get() = loginModeState.loginMode is AsyncData.Loading || changeServerState.changeServerAction is AsyncData.Loading + + val isShowingError: Boolean + get() = loginModeState.loginMode is AsyncData.Failure || + changeServerState.changeServerAction is AsyncData.Failure } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index 8be8bab7e8..e1be11da3a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -15,12 +15,17 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.AnnotatedString @@ -72,6 +77,26 @@ fun ConfirmAccountProviderView( modifier: Modifier = Modifier, ) { val eventSink = state.eventSink + val focusManager = LocalFocusManager.current + val focusRequester = remember { FocusRequester() } + val keyboardController = LocalSoftwareKeyboardController.current + + fun submit() { + // Dismiss the keyboard and release focus while the account provider is being validated. + focusManager.clearFocus(force = true) + eventSink(ConfirmAccountProviderEvents.Continue) + } + + // Once a validation / login error has been dismissed, return focus and the keyboard to the field so + // the user can correct the account provider and retry. + var wasShowingError by remember { mutableStateOf(false) } + LaunchedEffect(state.isShowingError) { + if (wasShowingError && !state.isShowingError) { + focusRequester.requestFocus() + keyboardController?.show() + } + wasShowingError = state.isShowingError + } HeaderFooterPage( modifier = modifier, @@ -94,7 +119,7 @@ fun ConfirmAccountProviderView( Button( text = stringResource(id = CommonStrings.action_continue), showProgress = state.isLoading, - onClick = { eventSink(ConfirmAccountProviderEvents.Continue) }, + onClick = ::submit, enabled = state.submitEnabled && !state.isLoading, modifier = Modifier .fillMaxWidth() @@ -104,7 +129,6 @@ fun ConfirmAccountProviderView( } ) { var input by textFieldState(stateValue = state.accountProviderInput) - val focusManager = LocalFocusManager.current // Inline autocomplete: render the un-typed remainder of the suggested account provider in grey. val suggestionSuffix = state.accountProviderSuggestion ?.takeIf { it.length > input.length && it.startsWith(input, ignoreCase = true) } @@ -123,6 +147,7 @@ fun ConfirmAccountProviderView( modifier = Modifier .fillMaxWidth() .padding(top = 40.dp) + .focusRequester(focusRequester) .onTabOrEnterKeyFocusNext(focusManager) .testTag(TestTags.changeServerServer), label = stringResource(id = CommonStrings.screen_select_server_textfield_header), @@ -139,9 +164,7 @@ fun ConfirmAccountProviderView( keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done, ), - keyboardActions = KeyboardActions(onDone = { - eventSink(ConfirmAccountProviderEvents.Continue) - }), + keyboardActions = KeyboardActions(onDone = { submit() }), singleLine = true, trailingIcon = if (input.isNotEmpty()) { { From c7d89735079db6ba20ce4dfe05ce2d5125c13e22 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Sun, 9 Aug 2026 01:58:47 +0300 Subject: [PATCH 034/338] Do not append the application MapTiler key to a custom map style url When a homeserver publishes a map_style_url in its .well-known file, the app appended its own MapTiler API key to it, producing a URL with two query strings that the tile server rejects. The map then failed to load for every user of that homeserver, with no way for the operator to opt out. MapTilerTileServerStyleUriBuilder.build() now appends ?key= only to the URL it composes itself from the bundled base url and map ids. A non blank customMapStyleUrl is returned verbatim, so the operator's own credentials survive. This matches Element Web and the self-hosted tile server documentation, both of which treat map_style_url as complete. The two existing tests that pinned the old behaviour are updated, and cases are added for a custom URL that already carries a key, one that carries an unrelated query string, and a blank url falling back to the built-in style. Relates to https://github.com/element-hq/element-x-android/issues/7289 --- .../MapTilerTileServerStyleUriBuilder.kt | 17 +++++----- .../MapTilerTileServerStyleUriBuilderTest.kt | 34 +++++++++++++++++-- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt index 054805d1ab..dd9d99fa04 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt @@ -29,14 +29,15 @@ internal class MapTilerTileServerStyleUriBuilder( customMapStyleUrl: String?, darkMode: Boolean, ): String { - return buildString { - if (customMapStyleUrl.isNullOrBlank()) { - val mapId = if (darkMode) darkMapId else lightMapId - append("$baseUrl/$mapId/style.json") - } else { - append(customMapStyleUrl) - } - append("?key=$apiKey") + return if (customMapStyleUrl.isNullOrBlank()) { + val mapId = if (darkMode) darkMapId else lightMapId + "$baseUrl/$mapId/style.json?key=$apiKey" + } else { + // A custom style url comes from the homeserver .well-known and is expected to be complete: + // it already carries the operator's own credentials, if any. Appending our api key to it + // adds a second query string and breaks the request. + // See https://github.com/element-hq/element-x-android/issues/7289 + customMapStyleUrl } } } diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt index 2e07204594..addc71c3b9 100644 --- a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt +++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt @@ -46,7 +46,7 @@ class MapTilerTileServerStyleUriBuilderTest { customMapStyleUrl = "https://custom.url/style.json", darkMode = false, ) - ).isEqualTo("https://custom.url/style.json?key=anApiKey") + ).isEqualTo("https://custom.url/style.json") } @Test @@ -56,6 +56,36 @@ class MapTilerTileServerStyleUriBuilderTest { customMapStyleUrl = "https://custom.url/style.json", darkMode = true, ) - ).isEqualTo("https://custom.url/style.json?key=anApiKey") + ).isEqualTo("https://custom.url/style.json") + } + + @Test + fun `custom map uri with its own api key is not altered`() { + assertThat( + builder.build( + customMapStyleUrl = "https://api.maptiler.com/maps/streets-v2/style.json?key=anOperatorKey", + darkMode = false, + ) + ).isEqualTo("https://api.maptiler.com/maps/streets-v2/style.json?key=anOperatorKey") + } + + @Test + fun `custom map uri with an existing query is not altered`() { + assertThat( + builder.build( + customMapStyleUrl = "https://self.hosted/style.json?foo=bar", + darkMode = true, + ) + ).isEqualTo("https://self.hosted/style.json?foo=bar") + } + + @Test + fun `blank custom map uri falls back to the built-in style`() { + assertThat( + builder.build( + customMapStyleUrl = " ", + darkMode = false, + ) + ).isEqualTo("https://base.url/aLightMapId/style.json?key=anApiKey") } } From e8ca16b74fefed6e81cd3473c9f41ad0910a5d4f Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Sun, 9 Aug 2026 01:59:01 +0300 Subject: [PATCH 035/338] Ignore a room list filter that is incompatible with an already selected one Some room list filters are mutually exclusive, and the UI expresses that by hiding the incompatible chips rather than disabling them. Nothing enforced the rule on the write path, so two chips tapped in the same frame, before the list could recompose, both applied. The room list then matched nothing while the offending chip was hidden, leaving no way to see or undo what was filtering the rooms. DefaultFilterSelectionStrategy.select() now ignores a filter that is incompatible with one already selected, so the same table buildFilters() uses to hide chips also governs the selection itself. The currently applied filters win and the late tap is dropped, which matches the existing design where an incompatible chip is already on its way out of the UI when it is tapped. toggle(), deselect(), clear() and the FilterSelectionStrategy interface are unchanged. Relates to https://github.com/element-hq/element-x-android/issues/5383 --- .../DefaultFilterSelectionStrategy.kt | 5 +++ .../filters/RoomListFiltersPresenterTest.kt | 45 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/selection/DefaultFilterSelectionStrategy.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/selection/DefaultFilterSelectionStrategy.kt index 847dbc2c39..4121ff9ab4 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/selection/DefaultFilterSelectionStrategy.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/selection/DefaultFilterSelectionStrategy.kt @@ -22,6 +22,11 @@ class DefaultFilterSelectionStrategy : FilterSelectionStrategy { override val filterSelectionStates = MutableStateFlow(buildFilters()) override fun select(filter: RoomListFilter) { + // Incompatible filters are hidden from the UI rather than disabled, so a filter can still be + // selected while its chip is on its way out, typically when two chips are tapped in the same + // frame. Ignore it, the currently applied filters win. + // See https://github.com/element-hq/element-x-android/issues/5383 + if (selectedFilters.any { it in filter.incompatibleFilters }) return selectedFilters.add(filter) filterSelectionStates.value = buildFilters() } diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenterTest.kt index 9fb87c0eec..ae1d7b5083 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenterTest.kt @@ -72,6 +72,51 @@ class RoomListFiltersPresenterTest { } } + @Test + @OptIn(ExperimentalCoroutinesApi::class) + fun `present - toggling two incompatible filters in the same frame keeps the first one`() = runTest { + val presenter = createRoomListFiltersPresenter() + presenter.test { + awaitItem().let { state -> + // Both events are sent on the same state, i.e. before any recomposition, which is what + // happens when the two chips are tapped simultaneously. + state.eventSink.invoke(RoomListFiltersEvent.ToggleFilter(RoomListFilter.Favourites)) + state.eventSink.invoke(RoomListFiltersEvent.ToggleFilter(RoomListFilter.Invites)) + } + advanceUntilIdle() + awaitLastSequentialItem().let { state -> + assertThat(state.selectedFilters()).containsExactly(RoomListFilter.Favourites) + assertThat(state.filterSelectionStates).containsExactly( + filterSelectionState(RoomListFilter.Favourites, true), + filterSelectionState(RoomListFilter.Unread, false), + filterSelectionState(RoomListFilter.People, false), + filterSelectionState(RoomListFilter.Rooms, false), + ).inOrder() + } + } + } + + @Test + @OptIn(ExperimentalCoroutinesApi::class) + fun `present - toggling two incompatible categories in the same frame keeps the first one`() = runTest { + val presenter = createRoomListFiltersPresenter() + presenter.test { + awaitItem().let { state -> + state.eventSink.invoke(RoomListFiltersEvent.ToggleFilter(RoomListFilter.People)) + state.eventSink.invoke(RoomListFiltersEvent.ToggleFilter(RoomListFilter.Rooms)) + } + advanceUntilIdle() + awaitLastSequentialItem().let { state -> + assertThat(state.selectedFilters()).containsExactly(RoomListFilter.People) + assertThat(state.filterSelectionStates).containsExactly( + filterSelectionState(RoomListFilter.People, true), + filterSelectionState(RoomListFilter.Unread, false), + filterSelectionState(RoomListFilter.Favourites, false), + ).inOrder() + } + } + } + @Test @OptIn(ExperimentalCoroutinesApi::class) fun `present - clear filters event`() = runTest { From de0e628512fc121021ff12e7f040f3c8dd977052 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Sun, 9 Aug 2026 03:51:20 +0300 Subject: [PATCH 036/338] Show the account a UnifiedPush registration belongs to in the distributor A distributor such as ntfy or DAVx5 lists every registration it holds, but Element registered each one with no message for the distributor, so an account added from a second session was indistinguishable from the first. RegisterUnifiedPushUseCase.execute now takes the session id and passes it as messageForDistributor, which the connector has accepted since 3.x. The value is the raw Matrix ID, which is not personal data by the project's own logging rules, and no new string resource is needed. UnifiedPushProvider already has the MatrixClient in hand at the only call site, so the PushProvider interface is unchanged and nothing outside the module moves. Relates to https://github.com/element-hq/element-x-android/issues/6426 --- .../unifiedpush/RegisterUnifiedPushUseCase.kt | 10 +++++++--- .../pushproviders/unifiedpush/UnifiedPushProvider.kt | 2 +- .../DefaultRegisterUnifiedPushUseCaseTest.kt | 7 ++++--- .../unifiedpush/FakeRegisterUnifiedPushUseCase.kt | 7 ++++--- .../unifiedpush/UnifiedPushProviderTest.kt | 8 ++++---- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt index 00e5649533..d76d69a653 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt @@ -12,6 +12,7 @@ import android.content.Context import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.di.annotations.ApplicationContext +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler import kotlinx.coroutines.flow.filter @@ -21,7 +22,7 @@ import org.unifiedpush.android.connector.UnifiedPush import kotlin.time.Duration.Companion.seconds interface RegisterUnifiedPushUseCase { - suspend fun execute(distributor: Distributor, clientSecret: String): Result + suspend fun execute(distributor: Distributor, clientSecret: String, sessionId: SessionId): Result } @ContributesBinding(AppScope::class) @@ -29,11 +30,14 @@ class DefaultRegisterUnifiedPushUseCase( @ApplicationContext private val context: Context, private val endpointRegistrationHandler: EndpointRegistrationHandler, ) : RegisterUnifiedPushUseCase { - override suspend fun execute(distributor: Distributor, clientSecret: String): Result { + override suspend fun execute(distributor: Distributor, clientSecret: String, sessionId: SessionId): Result { UnifiedPush.saveDistributor(context, distributor.value) // This will trigger the callback // VectorUnifiedPushMessagingReceiver.onNewEndpoint - UnifiedPush.register(context = context, instance = clientSecret) + // The session id is passed as the message for the distributor, so that its UI can tell one + // registration from another when several accounts are registered. + // See https://github.com/element-hq/element-x-android/issues/6426 + UnifiedPush.register(context = context, instance = clientSecret, messageForDistributor = sessionId.value) // Wait for VectorUnifiedPushMessagingReceiver.onNewEndpoint to proceed @Suppress("RunCatchingNotAllowed") return runCatching { diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt index 7288fccab2..f72a591faa 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt @@ -36,7 +36,7 @@ class UnifiedPushProvider( override suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor): Result { val clientSecret = pushClientSecret.getSecretForUser(matrixClient.sessionId) - return registerUnifiedPushUseCase.execute(distributor, clientSecret) + return registerUnifiedPushUseCase.execute(distributor, clientSecret, matrixClient.sessionId) .onSuccess { unifiedPushStore.setDistributorValue(matrixClient.sessionId, distributor.value) } diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt index fe7da75951..5460005cef 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt @@ -12,6 +12,7 @@ import androidx.test.platform.app.InstrumentationRegistry import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_SECRET +import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler import io.element.android.libraries.pushproviders.unifiedpush.registration.RegistrationResult @@ -41,7 +42,7 @@ class DefaultRegisterUnifiedPushUseCaseTest : RobolectricTest() { delay(100) endpointRegistrationHandler.registrationDone(RegistrationResult(A_SECRET, Result.success(Unit))) } - val result = useCase.execute(aDistributor, A_SECRET) + val result = useCase.execute(aDistributor, A_SECRET, A_SESSION_ID) assertThat(result.isSuccess).isTrue() } @@ -56,7 +57,7 @@ class DefaultRegisterUnifiedPushUseCaseTest : RobolectricTest() { delay(100) endpointRegistrationHandler.registrationDone(RegistrationResult(A_SECRET, Result.failure(AN_EXCEPTION))) } - val result = useCase.execute(aDistributor, A_SECRET) + val result = useCase.execute(aDistributor, A_SECRET, A_SESSION_ID) assertThat(result.isSuccess).isFalse() } @@ -67,7 +68,7 @@ class DefaultRegisterUnifiedPushUseCaseTest : RobolectricTest() { endpointRegistrationHandler = endpointRegistrationHandler ) val aDistributor = Distributor("aValue", "aName") - val result = useCase.execute(aDistributor, A_SECRET) + val result = useCase.execute(aDistributor, A_SECRET, A_SESSION_ID) assertThat(result.isSuccess).isFalse() } diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt index 3055dad6b7..55729f29f8 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt @@ -8,13 +8,14 @@ package io.element.android.libraries.pushproviders.unifiedpush +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.tests.testutils.lambda.lambdaError class FakeRegisterUnifiedPushUseCase( - private val result: (Distributor, String) -> Result = { _, _ -> lambdaError() } + private val result: (Distributor, String, SessionId) -> Result = { _, _, _ -> lambdaError() } ) : RegisterUnifiedPushUseCase { - override suspend fun execute(distributor: Distributor, clientSecret: String): Result { - return result(distributor, clientSecret) + override suspend fun execute(distributor: Distributor, clientSecret: String, sessionId: SessionId): Result { + return result(distributor, clientSecret, sessionId) } } diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt index e22c1c3bf4..e766068783 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt @@ -62,7 +62,7 @@ class UnifiedPushProviderTest { @Test fun `register ok`() = runTest { val getSecretForUserResultLambda = lambdaRecorder { A_SECRET } - val executeLambda = lambdaRecorder> { _, _ -> Result.success(Unit) } + val executeLambda = lambdaRecorder> { _, _, _ -> Result.success(Unit) } val setDistributorValueResultLambda = lambdaRecorder { _, _ -> } val unifiedPushProvider = createUnifiedPushProvider( pushClientSecret = FakePushClientSecret( @@ -82,7 +82,7 @@ class UnifiedPushProviderTest { .with(value(A_SESSION_ID)) executeLambda.assertions() .isCalledOnce() - .with(value(Distributor("value", "Name")), value(A_SECRET)) + .with(value(Distributor("value", "Name")), value(A_SECRET), value(A_SESSION_ID)) setDistributorValueResultLambda.assertions() .isCalledOnce() .with(value(A_SESSION_ID), value("value")) @@ -91,7 +91,7 @@ class UnifiedPushProviderTest { @Test fun `register ko`() = runTest { val getSecretForUserResultLambda = lambdaRecorder { A_SECRET } - val executeLambda = lambdaRecorder> { _, _ -> Result.failure(AN_EXCEPTION) } + val executeLambda = lambdaRecorder> { _, _, _ -> Result.failure(AN_EXCEPTION) } val setDistributorValueResultLambda = lambdaRecorder(ensureNeverCalled = true) { _, _ -> } val unifiedPushProvider = createUnifiedPushProvider( pushClientSecret = FakePushClientSecret( @@ -111,7 +111,7 @@ class UnifiedPushProviderTest { .with(value(A_SESSION_ID)) executeLambda.assertions() .isCalledOnce() - .with(value(Distributor("value", "Name")), value(A_SECRET)) + .with(value(Distributor("value", "Name")), value(A_SECRET), value(A_SESSION_ID)) } @Test From e63c897ec274a5421fb22c9ed8b29bcae4f0fa9e Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Sun, 9 Aug 2026 03:51:20 +0300 Subject: [PATCH 037/338] Ignore a second voice recording start while one is already running The composer emits Start whenever the voice message state is Idle, and the state only leaves Idle once the recorder has produced its first audio buffer. A second tap inside that window started a second recording, which overwrote the output file, re-initialised the encoder while the first one was still encoding, and orphaned the first job together with its AudioReader. The orphan kept the microphone open and kept emitting Recording for the rest of the session, so Stop and Delete appeared to do nothing until the room was left and re-entered, and re-initialising the encoder under an active encode could crash natively. DefaultVoiceRecorder.startRecord now runs under the mutex it already owns and returns early when a recording job exists, so the second start is a no-op: no file, no encoder re-init and no second AudioReader. Relates to https://github.com/element-hq/element-x-android/issues/5780 --- .../impl/DefaultVoiceRecorder.kt | 16 ++++++--- .../impl/DefaultVoiceRecorderTest.kt | 33 +++++++++++++++++-- .../test/FakeAudioReaderFactory.kt | 5 +++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorder.kt b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorder.kt index 5e4c9ae132..4dd68d25e7 100644 --- a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorder.kt +++ b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorder.kt @@ -71,14 +71,22 @@ class DefaultVoiceRecorder( override val state: StateFlow = _state @RequiresPermission(Manifest.permission.RECORD_AUDIO) - override suspend fun startRecord() { + override suspend fun startRecord() = lock.withLock { + // The composer can emit a second Start before the first recording has produced its first + // buffer, i.e. before the state flips away from Idle. Without this guard the second call + // orphans the first job and its AudioReader, which keeps the microphone open and keeps + // emitting Recording forever, so Stop and Delete appear to do nothing. + // See https://github.com/element-hq/element-x-android/issues/5780 + if (recordingJob != null) { + Timber.w("Voice recorder is already recording, ignoring this start") + return@withLock + } + Timber.i("Voice recorder started recording") outputFile = fileManager.createFile() .also(encoder::init) - lock.withLock { - levels.clear() - } + levels.clear() val audioRecorder = audioReaderFactory.create(config, dispatchers).also { audioReader = it } diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorderTest.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorderTest.kt index e3ff6f9198..3a65d612bb 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorderTest.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorderTest.kt @@ -39,6 +39,7 @@ import kotlin.time.TestTimeSource class DefaultVoiceRecorderTest { private val fakeFileSystem = FakeFileSystem() private val timeSource = TestTimeSource() + private val audioReaderFactory = FakeAudioReaderFactory(audio = AUDIO) @Test fun `it emits the initial state`() = runTest { @@ -109,6 +110,34 @@ class DefaultVoiceRecorderTest { } } + @Test + fun `when startRecord is called twice, the second call is ignored`() = runTest { + val voiceRecorder = createDefaultVoiceRecorder() + voiceRecorder.state.test { + assertThat(awaitItem()).isEqualTo(VoiceRecorderState.Idle) + + // Two taps landing before the state has flipped away from Idle. + voiceRecorder.startRecord() + voiceRecorder.startRecord() + assertThat(audioReaderFactory.createdCount).isEqualTo(1) + + skipItems(1) + timeSource += 5.seconds + skipItems(2) + voiceRecorder.stopRecord() + assertThat(awaitItem()).isEqualTo( + VoiceRecorderState.Finished( + file = File(FILE_PATH), + mimeType = MimeTypes.Ogg, + waveform = List(100) { 1f }, + duration = 5.seconds, + ) + ) + // An orphaned recording job would keep emitting Recording after the stop. + expectNoEvents() + } + } + @Test fun `when cancelled, it deletes the file`() = runTest { val voiceRecorder = createDefaultVoiceRecorder() @@ -128,9 +157,7 @@ class DefaultVoiceRecorderTest { return DefaultVoiceRecorder( dispatchers = testCoroutineDispatchers(), timeSource = timeSource, - audioReaderFactory = FakeAudioReaderFactory( - audio = AUDIO, - ), + audioReaderFactory = audioReaderFactory, encoder = FakeEncoder(fakeFileSystem), config = AudioConfig( format = audioFormat, diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReaderFactory.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReaderFactory.kt index d4bd241aaa..2c29b2635e 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReaderFactory.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReaderFactory.kt @@ -16,7 +16,12 @@ import io.element.android.libraries.voicerecorder.impl.audio.AudioReader class FakeAudioReaderFactory( private val audio: List "%1$s Android" "Veast teatamiseks raputa nutiseadet ägedalt" "Ekraanitõmmis" + "Logi sisse" + "Loo kasutajakonto" + "Kui sinu kasutajakonto paikneb äriühingu või kogukonna serveris, siis muuda kontoteenuse pakkujat (näiteks: minufirmanimi.ee)." + "Loo tasuta kasutajakonto matrix.org serverisse. Kui soovid luua kasutajakonto mõne teise teenusepakkuja juures, siis muuda seda vastavalt (näiteks: minukogukond.ee)." + "Teenusepakkuja" + "domeeninäidis.ee" + "Sisesta oma kasutajakonto teenusepakkuja" + "Vali kasutajakonto teenusepakkuja" "%1$s: %2$s" "Valikud" "Kustuta: %1$s" diff --git a/libraries/ui-strings/src/main/res/values-hr/translations.xml b/libraries/ui-strings/src/main/res/values-hr/translations.xml index 3c02673a22..c0879d3a2e 100644 --- a/libraries/ui-strings/src/main/res/values-hr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hr/translations.xml @@ -8,6 +8,7 @@ "Minimiziraj tekstno polje poruke" "Smanjenje dopuštenih glasova po osobi" "Povećanje broja dopuštenih glasova po osobi" + "Prilagođeni Emoji" "Izbriši" "%1$d unesena znamenka" @@ -65,6 +66,7 @@ "Započni poziv" "Započni videopoziv" "Započni glasovni poziv" + "Stanje" "Nit u %1$s" "Teme u %1$s" "Soba označena za uklanjanje" diff --git a/libraries/ui-strings/src/main/res/values-hu/translations.xml b/libraries/ui-strings/src/main/res/values-hu/translations.xml index 0a37baf199..f198cc0510 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -8,6 +8,7 @@ "Üzenet szövegmezőjének minimalizálása" "Személyenként engedélyezett szavazatok számának csökkentése" "Személyenként engedélyezett szavazatok számának növelése" + "Egyéni emoji" "Törlés" "%1$d megadott számjegy" @@ -23,6 +24,7 @@ "Csatlakozás a híváshoz" "Ugrás az aljára" "Ugrás az olvasatlanhoz" + "Ugrás az első olvasatlan üzenetre" "Térkép áthelyezése a jelenlegi helyre" "Csak megemlítések" "Némítva" @@ -63,6 +65,7 @@ "Hanghívás indítása" "Videohívás indítása" "Hanghívás indítása" + "Állapot" "Üzenetszál itt: %1$s" "Üzenetszálak itt: %1$s" "Elévült szoba" @@ -143,6 +146,7 @@ "Fiók és eszközök kezelése" "Eszközök kezelése" "Szobák kezelése" + "Megjelölés olvasottként" "Üzenet" "Minimalizálás" "Következő" @@ -267,6 +271,14 @@ Ok: %1$s." "Üzenet továbbítása" "Gyakran használt" "Galéria" + + "%1$d melléklet…" + "%1$d melléklet…" + + + "%1$d média…" + "%1$d média…" + "GIF" "Csoportos hívás folyamatban" "Kép" @@ -304,6 +316,7 @@ Ok: %1$s." "Nincs térnév" "Nincs titkosítva" "Kapcsolat nélkül" + "Hívásban" "Nyílt forráskódú licencek" "vagy" "További lehetőségek" @@ -505,6 +518,8 @@ Biztos, hogy folytatja?" "%1$d személy" "A térképen" + "A Matrix-kiszolgáló helyi hálózaton található. A csatlakozáshoz engedélyre van szüksége ennek: %1$s , hogy elérje a hálózaton lévő eszközöket." + "Hozzáférés engedélyezése a helyi hálózathoz" "Nem sikerült kiválasztani a médiát, próbálja újra." "Nyomjon hosszan az üzenetre, és válassza a „%1$s” lehetőséget, hogy itt szerepeljen." "Tűzze ki a fontos üzeneteket, hogy könnyen felfedezhetők legyenek" diff --git a/libraries/ui-strings/src/main/res/values-pl/translations.xml b/libraries/ui-strings/src/main/res/values-pl/translations.xml index 0234af883a..1ee4275703 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -20,6 +20,16 @@ "Podgląd pełnego adresu %1$s" "Szczegóły szyfrowania" "Powiększ pole tekstowe wiadomości" + "Galeria, załącznik %1$d" + "Galeria, nieudane skanowanie %1$d" + "Galeria, obraz %1$d" + + "Galeria, %1$d dodatkowe media" + "Galeria, %1$d dodatkowe media" + "Galeria, %1$d dodatkowych mediów" + + "Galeria, skanowanie %1$d" + "Galeria, wideo %1$d" "Ukryj hasło" "Informacje" "Dołącz do połączenia" @@ -184,8 +194,8 @@ "Wyślij edytowaną wiadomość" "Wyślij wiadomość" "Wyślij wiadomość głosową" - "Wyślij" - "Wyślij link" + "Udostępnij" + "Udostępnij link" "Udostępnij lokalizację na żywo" "Pokaż" "Zaloguj się ponownie" @@ -518,6 +528,14 @@ Czy na pewno chcesz kontynuować?" "%1$s Android" "Wstrząśnij gniewnie, aby zgłosić błąd" "Zrzut ekranu" + "Zaloguj" + "Utwórz konto" + "Jeśli uzyskałeś konto od firmy lub społeczności, wprowadź dostawcę konta (na przykład: nazwafirmy.com)" + "Załóż darmowe konto na matrix.org. Jeśli chcesz założyć konto u innego dostawcy wprowadź własny adres (na przykład: nazwaspołeczności.org)." + "Dostawca konta" + "przykład.com" + "Wprowadź dostawcę swojego konta" + "Wybierz dostawcę konta" "%1$s: %2$s" "Opcje" "Usuń %1$s" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 4eba83312b..71b5ab7041 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -1,23 +1,41 @@ + "Adăugați un avatar" "Adăugați o reacție: %1$s" "Adresă" "Imagine de profil" + "Ștergeți câmpul de căutare" "Micșorați câmpul mesajului" + "Reduceți numărul de voturi permise per persoană" + "Măriți numărul de voturi permise per persoană" + "Emoji personalizat" "Ștergere" "%1$d cifră introdusă" "%1$d cifre introduse" "%1$d cifre introduse" + "Durată: %1$s" "Editați avatarul" "Adresa completă va fi %1$s" "Detalii privind criptarea" "Extindeți câmpul mesajului" + "Galerie, atașament %1$d" + "Galerie, scanare eșuată %1$d" + "Galerie, imagine %1$d" + + "Galerie, încă %1$d fișier media" + "Galerie, încă %1$d fișiere media" + "Galerie, încă %1$d fișiere media" + + "Galerie, scanare %1$d" + "Galerie, videoclip %1$d" "Ascundeți parola" "Informații" "Alăturați-vă apelului" "Mergeți în jos" + "Săriți la necitite" + "Săriți la primul mesaj necitit" "Mutați harta la locația mea" "Doar mențiuni" "Notificări dezactivate" @@ -28,12 +46,14 @@ "Pagina %1$d" "Pauză" "Mesaj vocal, durată:%1$s, poziție curentă: %2$s" + "Previzualizare foto" "Câmp PIN" "Locație fixată" "Redați" "Viteză de redare" "Sondaj" "Sondaj încheiat" + "Poziție: %1$s" "Cod QR" "Reacționați cu %1$s" "Reacționați cu alte emoji-uri" @@ -44,20 +64,26 @@ "Citit de %1$s și incă %2$d" "Citit de %1$s" + "afișați toate confirmările de citire" "Îndepărtați reacția cu %1$s" "Îndepărtați reacția %1$s" "Avatarul camerei" "Trimiteți fișiere" "Locația expeditorului" + "Trimis de %1$s la %2$s" "Acțiune limitată în timp necesară, aveți un minut pentru a verifica" "Setări, acțiune necesară" "Afișați parola" "Începeți un apel" "Inițiați un apel video" "Inițiați un apel vocal" + "Statuts" + "Fir în: %1$s" + "Fire în %1$s" "Cameră terminată" "Avatar utilizator" "Meniu utilizator" + "Previzualizare video" "Vizualizați avatarul" "Vizualizați detalii" "Mesaj vocal, durată: %1$s" @@ -94,6 +120,7 @@ "Refuzați și blocați" "Ștergere" "Ștergeți contul" + "Ștergeți fișierul" "Ștergeți sondajul" "Deselectați tot" "Dezactivați" @@ -131,6 +158,7 @@ "Gestionați contul și dispozitivele" "Gestionați dispozitivele" "Gestionați camerele" + "Marcați ca citit" "Contactați" "Minimizați" "Următorul" @@ -185,9 +213,11 @@ "Traduceți" "Încercați din nou" "Defixați" + "Apel video" "Vizualizați" "Vedeți în lista de mesaje" "Vedeți sursă" + "Apel vocal" "Da" "Da, încercați din nou" "Serverul dvs. acceptă acum un protocol nou, mai rapid. Deconectați-vă și conectați-vă din nou pentru a face upgrade acum. Dacă faceți acest lucru acum, vă va ajuta să evitați o deconectare forțată atunci când vechiul protocol este eliminat ulterior." @@ -208,6 +238,7 @@ "Utilizatori blocați" "Baloane" "Apel respins" + "Apel în curs" "Apel inițiat" "Ați respins un apel" "Backup conversații" @@ -251,7 +282,19 @@ Motiv:%1$s." "Fișier salvat în Descărcări" "Redirecționați mesajul" "Utilizate frecvent" + "Galerie" + + "%1$d atașament…" + "%1$d atașamente…" + "%1$d atașamente…" + + + "%1$d element media…" + "%1$d elemente media…" + "%1$d elemente media…" + "GIF" + "Apel de grup în curs" "Imagine" "Ca răspuns la %1$s" "Instalați APK" @@ -289,6 +332,7 @@ Motiv:%1$s." "Fără nume de spațiu" "Necriptat" "Deconectat" + "Într-un apel" "Licențe open source" "sau" "Alte opțiuni" @@ -301,6 +345,7 @@ Motiv:%1$s." "Va rugam asteptati…" "Sunteți sigur că doriți să încheiați acest sondaj?" "Sondajul %1$s" + "Sondaj" "Total voturi: %1$s" "Rezultatele vor fi afișate după încheierea sondajului" @@ -400,6 +445,7 @@ Motiv:%1$s." "Activare sunet" "Apel incompatibil" "Eveniment neacceptat" + "%1$s a inițiat un apel" "Utilizator" "Verificare anulată" "Verificare completă" @@ -421,6 +467,11 @@ Motiv:%1$s." "Se așteptă localizarea în timp real…" "Oricine poate vedea istoricul" "Dumneavoastră" + "Descărcarea nu este disponibilă." + "Ceva nu a mers bine" + "Se scanează…" + "Previzualizarea și descărcarea au fost dezactivate." + "Fișierul nu este sigur" "%1$s (%2$s) a distribuit acest mesaj deoarece nu erați în cameră când a fost trimis." "%1$s a distribuit acest mesaj deoarece nu erați în cameră când a fost trimis." "Această cameră a fost configurată astfel încât noii membri să poată citi istoricul. %1$s" @@ -477,10 +528,19 @@ Sunteți sigur că doriți să continuați?" "%1$s Android" "Rageshake pentru a raporta erori" "Captură de ecran" + "Autentificare" + "Creați un cont" + "Dacă contul este furnizat de compania dumneavoastră sau de o comunitate, schimbați furnizorul contului (exemplu: numecompanie.com)." + "Creați un cont gratuit pe matrix.org. Dacă doriți să creați un cont la un alt furnizor, schimbați-l în consecință (exemplu: numecomunitate.org)." + "Furnizor de cont" + "exemplu.ro" + "Introduceți furnizorul contului dumneavoastră" + "Alegeți un furnizor de cont" "%1$s: %2$s" "Opțiuni" "Ștergeți %1$s" "Setări" + "Voturi permise per persoană" "Nimeni nu își partajează locația" "Se partajează locația în timp real" @@ -489,6 +549,8 @@ Sunteți sigur că doriți să continuați?" "%1$d persoane" "Pe hartă" + "Homeserverul dumneavoastră se află în rețeaua locală. Pentru a se conecta, %1$s are nevoie de permisiunea de a accesa dispozitive din această rețea." + "Permiteți accesul la rețeaua locală" "Selectarea fișierelor media a eșuat, încercați din nou." "Apăsați pe un mesaj și alegeți \"%1$s\" pentru a-l include aici." "Fixați mesajele importante, astfel încât să poată fi descoperite cu ușurință" @@ -535,6 +597,11 @@ Sunteți sigur că doriți să continuați?" "Spații" "Partajat %1$s" "Pe hartă" + + "%1$d s-a alăturat" + "%1$d s-au alăturat" + "%1$d s-au alăturat" + "Mesajul nu a fost trimis deoarece identitatea digitala verificată a lui %1$s s-a schimbat." "Mesajul nu a fost trimis deoarece %1$s nu a verificat toate dispozitivele." "Mesajul nu a fost trimis deoarece nu ați verificat unul sau mai multe dispozitive." @@ -546,4 +613,6 @@ Sunteți sigur că doriți să continuați?" "Nu aveți acces la acest mesaj" "Nu s-a putut decripta mesajul" "Acest mesaj a fost blocat fie pentru că nu ați verificat dispozitivul, fie pentru că expeditorul trebuie să vă verifice identitatea digitală." + "Dispozitivul dumneavoastră este prea vechi, este necesar un dispozitiv cu Android 8 sau o versiune mai nouă." + "Afișarea hărților nu este suportată" 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 4e46cd4653..cdaa72f5d3 100644 --- a/libraries/ui-strings/src/main/res/values-sv/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sv/translations.xml @@ -440,7 +440,7 @@ Anledning:%1$s." "Öppna i Apple Maps" "Öppna i Google Maps" "Öppna i OpenStreetMap" - "Dela fäst plats" + "Dela vald plats" "Utrymmen som du har skapat eller gått med i." "%1$s • %2$s" "Utrymmen" diff --git a/libraries/ui-strings/src/main/res/values-uk/translations.xml b/libraries/ui-strings/src/main/res/values-uk/translations.xml index 9e93cdb891..8545325603 100644 --- a/libraries/ui-strings/src/main/res/values-uk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uk/translations.xml @@ -20,6 +20,16 @@ "Повна адреса буде %1$s" "Подробиці шифрування" "Розгорнути текстове поле повідомлення" + "Галерея, вкладення %1$d" + "Галерея, невдалося сканувати %1$d" + "Галерея, зображення %1$d" + + "Галерея, ще %1$d медіа" + "Галерея, ще %1$d медіа" + "Галерея, ще %1$d медіа" + + "Галерея, сканування %1$d" + "Галерея, відео %1$d" "Cховати пароль" "Інформація" "Приєднатися до виклику" @@ -518,6 +528,14 @@ "%1$s Android" "Повідомляти про помилку потрусивши телефон" "Знімок екрана" + "Увійти" + "Створити обліковий запис" + "Якщо ваш обліковий запис надано вашою компанією або спільнотою, змініть постачальника облікового запису (наприклад: companyname.com)." + "Створіть обліковий запис на matrix.org безплатно. Якщо ви хочете створити обліковий запис у іншого постачальника, змініть його відповідно (наприклад: communityname.org)." + "Постачальник облікового запису" + "example.com" + "Вкажіть постачальника вашого облікового запису" + "Оберіть постачальника облікового запису" "%1$s: %2$s" "Варіанти" "Вилучити %1$s" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index c2ff955eac..171551d71e 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -1,20 +1,29 @@ + "新增大頭照" "新增反應:%1$s" "地址" "大頭貼" + "清除搜尋欄位" "最小化訊息文字欄位" + "減少每人可投的票數" + "增加每人可投的票數上限" + "自訂表情符號" "刪除" "已輸入 %1$d 個位數" + "持續時間:%1$s" "編輯大頭照" "完整地址為 %1$s" "加密詳細資訊" "展開訊息文字欄位" "隱藏密碼" + "資訊" "加入通話" "跳至底部" + "跳至未讀訊息" + "跳到第一則未讀訊息" "將地圖移動到我的位置" "僅限提及" "已關閉通知" @@ -25,33 +34,42 @@ "第 %1$d 頁" "暫停" "語音訊息,時長:%1$s,目前位置:%2$s" + "照片預覽" "PIN 碼欄位" "固定位置" "播放" "播放速度" "投票" "投票已結束" + "位置:%1$s" "QR Code" "使用 %1$s 回應" "用其他表情符號回應" - "%1$s 和 %2$s 已讀" + "上一則訊息已被 %1$s 和 %2$s 閱讀" - "%1$s 與其他 %2$d 個人已讀" + "前一則訊息已被 %1$s 及另外 %2$d 人閱讀" - "%1$s 已讀" + "上一則訊息由 %1$s 閱讀" + "顯示所有讀取回條" "移除 %1$s 的反應" "移除反應 %1$s" "聊天室大頭照" "傳送檔案" "傳送者位置" + "由 %1$s 於 %2$s 傳送" "需要限時動作,您有一分鐘可以驗證" + "設定,需採取行動" "顯示密碼" "開始通話" "開始視訊通話" "開始語音通話" + "狀態" + "%1$s 中的討論串" + "%1$s 中的討論串" "墓碑聊天室" "使用者大頭照" "使用者選單" + "影片預覽" "檢視大頭照" "檢視詳細資訊" "語音訊息,時長:%1$s" @@ -66,6 +84,7 @@ "通話" "取消" "暫時取消" + "選擇檔案" "選擇照片" "清除" "關閉" @@ -85,12 +104,16 @@ "停用帳號" "拒絕" "拒絕並封鎖" + "刪除" + "刪除帳號" + "刪除檔案" "刪除投票" "取消全選" "停用" "捨棄" "關閉" "完成" + "下載" "編輯" "編輯標題" "編輯投票" @@ -121,6 +144,7 @@ "管理帳號與裝置" "管理裝置" "管理聊天室" + "標記為已讀" "聊天" "最小化" "下一步" @@ -175,9 +199,11 @@ "翻譯" "再試一次" "取消釘選" + "視訊通話" "檢視" "在時間軸中檢視" "檢視來源" + "語音通話" "是" "是的,再試一次" "您的伺服器現在支援新的、更快的協定。立即登出並重新登入以進行升級。現在這樣做將協助您避免在稍後移除舊協定時被強制登出。" @@ -197,7 +223,10 @@ "測試版" "封鎖的使用者" "泡泡" + "通話被拒" + "通話中" "開始通話" + "您拒絕了通話" "聊天室備份" "已複製到剪貼簿" "著作權" @@ -239,7 +268,15 @@ "檔案已儲存至 Downloads" "轉寄訊息" "經常使用" + "圖庫" + + "%1$d 個附件⋯⋯" + + + "%1$d 個媒體項目⋯⋯" + "GIF" + "群組通話進行中" "圖片" "回覆 %1$s" "安裝 APK" @@ -273,6 +310,7 @@ "沒有空間名稱" "未加密" "離線" + "正在通話" "開放原始碼授權條款" "或" "其他選項" @@ -285,6 +323,7 @@ "請稍等…" "您確定要結束這項投票嗎?" "投票:%1$s" + "投票" "總票數:%1$s" "結果將在投票結束後公佈" @@ -374,6 +413,7 @@ "開啟通知" "不支援的通話" "不支援的事件" + "%1$s 已發起通話" "使用者名稱" "驗證已取消" "驗證完成" @@ -395,6 +435,11 @@ "正在等待即時位置……" "任何人都可以檢視歷史" "您" + "無法下載。" + "發生錯誤" + "正在掃描⋯⋯" + "預覽與下載功能已停用。" + "檔案不安全" "因為您當時不在聊天室內,所以 %1$s (%2$s) 分享了此訊息。" "因為您當時不在聊天室裡面,因此 %1$s 分享了此訊息。" "此聊天室被設定為方便新成員閱讀歷史紀錄。%1$s" @@ -445,6 +490,9 @@ "抱歉,發生錯誤" "🔐️ 在 %1$s 上加入我" "嘿,來 %1$s 和我聊天:%2$s" + "即時位置分享" + "正在分享位置" + "%1$s 即時位置" "%1$s Android" "憤怒搖晃以回報臭蟲" "螢幕截圖" @@ -452,6 +500,15 @@ "選項" "移除 %1$s" "設定" + "每人可投票數" + "沒有人在分享位置" + "分享即時位置" + + "%1$d 人" + + "在地圖上" + "您的家伺服器位於區域網路。要連線,%1$s 需要取得存取此網路上裝置的權限。" + "允許存取區域網路" "選取媒體失敗,請再試一次。" "按一下訊息,然後選擇「%1$s」以加入至此。" "釘選重要訊息,如此才能輕鬆發現" @@ -496,6 +553,9 @@ "空間" "已分享 %1$s" "在地圖上" + + "%1$d 人已加入" + "因為 %1$s 的驗證數位身份已重設,因此未傳送訊息。" "訊息未傳送,因為 %1$s 尚未驗證所有裝置。" "因為您尚未驗證一個或多個裝置,因此未傳送訊息" @@ -507,4 +567,6 @@ "您無法存取此則訊息" "無法解密訊息" "此訊息被封鎖,原因可能是您尚未驗證裝置,或是寄件者需要驗證您的數位身分。" + "您的裝置版本過舊,需要執行 Android 8 或更新版本的裝置。" + "不支援顯示地圖" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index 0ecd99f6c8..b363ba9498 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -18,6 +18,14 @@ "完整地址为 %1$s" "加密详情" "展开消息文本框" + "媒体组,第 %1$d 个附件" + "媒体组,第 %1$d 个扫描失败" + "媒体组,第 %1$d 个图像" + + "媒体组,其余 %1$d 个媒体" + + "媒体组,正在扫描第 %1$d 个" + "媒体组,第 %1$d 个视频" "隐藏密码" "信息" "加入通话" @@ -268,7 +276,7 @@ "文件已保存到“下载”" "转发消息" "常用" - "图库" + "媒体组" "%1$d 个附件…" @@ -496,6 +504,14 @@ "%1$s Android" "摇一摇以报告 bug" "屏幕截图" + "登录" + "创建账户" + "如果你的账户由你的公司或社区提供,则应更改账户提供者(例如:companyname.com)。" + "在 matrix.org 创建免费账户。如果你想使用其它服务提供者创建账户,请相应地更改域名(例如:communityname.org)。" + "账户提供者" + "example.com" + "输入你的账户提供者" + "选择账户提供者" "%1$s:%2$s" "选项" "移除 %1$s" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 49dedb49e1..0d27ff5a9f 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -19,6 +19,15 @@ "The full address will be %1$s" "Encryption details" "Expand message text field" + "Gallery, attachment %1$d" + "Gallery, failed scan %1$d" + "Gallery, image %1$d" + + "Gallery, %1$d more media" + "Gallery, %1$d more medias" + + "Gallery, scanning %1$d" + "Gallery, video %1$d" "Hide password" "Info" "Join call" @@ -507,6 +516,14 @@ Are you sure you want to continue?" "%1$s Android" "Rageshake to report bug" "Screenshot" + "Sign in" + "Create account" + "If your account is provided by your company or a community, then change the account provider (example: companyname.com)." + "Create an account on matrix.org for free. If you want to create an account with a different provider, then change it accordingly (example: communityname.org)." + "Account provider" + "example.com" + "Enter your account provider" + "Choose an account provider" "%1$s: %2$s" "Options" "Remove %1$s" diff --git a/screenshots/de/appnav.loggedin_LoggedInView_Day_4_de.png b/screenshots/de/appnav.loggedin_LoggedInView_Day_4_de.png index d0563d236e..b9398c2706 100644 --- a/screenshots/de/appnav.loggedin_LoggedInView_Day_4_de.png +++ b/screenshots/de/appnav.loggedin_LoggedInView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d5342cd7d81f6394ec127163668864dc9e14e05cc26999c8a9381e787067f70 -size 33738 +oid sha256:2c9bd18c9b847515945f2036ccfa9a19a5641364a0d9d8f72dbd501fbc6ef1f2 +size 33670 diff --git a/screenshots/de/appnav.loggedin_LoggedInView_Day_5_de.png b/screenshots/de/appnav.loggedin_LoggedInView_Day_5_de.png index dd36ce4cc0..622e68f33b 100644 --- a/screenshots/de/appnav.loggedin_LoggedInView_Day_5_de.png +++ b/screenshots/de/appnav.loggedin_LoggedInView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c3b46bd15055a52ef9c92b49e75a042345d83826574e4bea82f6c981c8903df -size 34726 +oid sha256:8a7f7c7c37fade606ac7bb5d9fb81750b9c3c0cfd72ffeb5fe4d1b480bb565f5 +size 34728 diff --git a/screenshots/de/features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_de.png b/screenshots/de/features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_de.png index 278fe5e567..a6285e884e 100644 --- a/screenshots/de/features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_de.png +++ b/screenshots/de/features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f0f2bcaa28da9d42a04192d689351a78d473aeb235ed7502a78fe7da79c15ae -size 25185 +oid sha256:a80691ed3ff57565eabd378354019e9700a25941a32349f342e82bba1467a192 +size 25088 diff --git a/screenshots/de/features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_de.png b/screenshots/de/features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_de.png index 2eb6ca5224..1b0a9a4eff 100644 --- a/screenshots/de/features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_de.png +++ b/screenshots/de/features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04b91c56da84172db707181b6f930780d7fd3a161e37e03dbcb59268f5fea963 -size 21116 +oid sha256:9b121fd1d8226c855b90270f3ba62f37960d5b045fe775adb4b12fc442576023 +size 20814 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_0_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_0_de.png index f41f849449..9ac001a34b 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_0_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:25753de5e78f127478e94ca29354a0ef057a20b795b302a911748b724b206c17 -size 33753 +oid sha256:2c58f8296161ff4699a28acf0b4409b6eb7fa1e505bc251f7c082a493e90d2bc +size 33426 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_1_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_1_de.png index 94b18f3f19..a26ea2745d 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_1_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4cac82fe268df0631f766e69030e1dc1bdb09d869db18e62d15c563e9ff81260 -size 36237 +oid sha256:0c7a002588018df26c32908aa3fa6ad000176f02c06e2c9a311d241074b4eae6 +size 36216 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_2_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_2_de.png index a8937dbed9..21fbc44c5c 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_2_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88eab769270bf7492cfbafbd537392348b76780bc93c2b4b858f1fecbc8e8af2 -size 45264 +oid sha256:331290f18bec6b21d9201da590be72620cef472d53ec7b7f36affec7b487a823 +size 45054 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_3_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_3_de.png index f7d745f184..61f5521cf0 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_3_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d94c8697c49e36f4275860e6a1825815738f1c8263ae7e129cd4139b2544b3e7 -size 46286 +oid sha256:f4d4cf90366a2a4567c90749b5c472ef961d88def03961c80ce70d8af3c9118b +size 46078 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_4_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_4_de.png index 41b1a4e9cf..0269967832 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_4_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:919e66822097fe71920c70c4923bcb61b332fb0766dd0bfd4e662081854151c3 -size 47732 +oid sha256:6ce6ae126656b0d52df817981b01a7bc2a79406cd6772b9370087ceae4fcec7b +size 47535 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_5_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_5_de.png index a8937dbed9..21fbc44c5c 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_5_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88eab769270bf7492cfbafbd537392348b76780bc93c2b4b858f1fecbc8e8af2 -size 45264 +oid sha256:331290f18bec6b21d9201da590be72620cef472d53ec7b7f36affec7b487a823 +size 45054 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_6_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_6_de.png index d8196828b8..91d6cd4ccd 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_6_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce9fbcbf082cf07edbb271870818e74782cbc5ed6c71624e1297b9af10a3c130 -size 46273 +oid sha256:e71aa05c73ce1a9ef184775600d6b395ebbfb8492536204395bca86b1ede3204 +size 45966 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_7_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_7_de.png index f98071e4d3..e6029f88e9 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_7_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42d004e1086f2224bb9ab8b41568d4e7f60a9f4c5b7a3e75394ef0f623f8bebf -size 41247 +oid sha256:ae25d75c2b4df98cef09460ef6575193b90b78e8d3d9b201a2341937afb3957a +size 40923 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_8_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_8_de.png index 866b224b68..44a1b1d2c2 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_8_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df105a3030abbf1760d5c1ee580664349f745d08023ad0f5da784ff7a6a3a1db -size 40619 +oid sha256:5bc48ce2eae062d624f0ed6c9d53b37ab1366ec9317ccff473a95ebd6c02f998 +size 40775 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_0_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_0_de.png index b8bf1e9d18..486d94521f 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_0_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc1d720d39e352d815fe20759dae937f87a50e89cf9de4aa998d20e2408417dc -size 34846 +oid sha256:c77d9c1fa53bf4bbb401a9abce45a9649e5f0aa9357852db84be3ab5ee88b3b2 +size 34581 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_1_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_1_de.png index 99923dc3af..5edac70588 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_1_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff909aab1190f8d9eff5b3c100df6d2a5cad0513955a1ab09351b2ac6fd9a021 -size 37618 +oid sha256:9bfbfb92dadb996c13eb33d0f37de4efe55df8e0f4011a0cb731dc4c3b6d095d +size 37674 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_2_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_2_de.png index d5ea39c88e..d991974c02 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_2_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d10e5c062dee14e0775ac72884ebbed0fe41aae4d1bb9a0892d8fa456d8c296 -size 47067 +oid sha256:ed5d241ee370a5443bbe3509b129d4d9c1d281c1bfc91a77d5ef630541a44c7c +size 46945 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_3_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_3_de.png index 5c2eb9e1e6..fbcddc1cc0 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_3_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:44fd0dd695a9e059ab54b2e6c628a6ac24625ec7c9c92c651c6626a228bd541c -size 48101 +oid sha256:a1b78daaa5a799b201f7104f99bbc667af2c936f3609e43fbb4d2212340b9fd9 +size 47987 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_4_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_4_de.png index 0863a13d18..94ae4546df 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_4_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe8b585c1bca2036160cac128233b0899c091bbc30fd087a72e4b1191ee5eef9 -size 49666 +oid sha256:e79218b5419c3369e205b760b716d1d2e75b86a8d1e0c096515786985c420e30 +size 49543 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_5_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_5_de.png index d5ea39c88e..d991974c02 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_5_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d10e5c062dee14e0775ac72884ebbed0fe41aae4d1bb9a0892d8fa456d8c296 -size 47067 +oid sha256:ed5d241ee370a5443bbe3509b129d4d9c1d281c1bfc91a77d5ef630541a44c7c +size 46945 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_6_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_6_de.png index 5b794372ed..d3db0cb4e8 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_6_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d56402d64dc752d4cccc3a1e0642a69ca244225cce346aa842fdf3cc492f8d1 -size 48121 +oid sha256:7fc4d8a1e103ae18082cfa8b0ca91d9c41e04f31bdff2c26435462b150920528 +size 47931 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_7_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_7_de.png index 9495d79405..8dd5a437f5 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_7_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55e147811154db2ce5de206693057fedf11b38ce6fcf23ef7ef4ac9ee00d4f7d -size 42888 +oid sha256:5a4769839422b683f76620b33f35faacb26874638d602632803b577786757d5a +size 42690 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_8_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_8_de.png index c393b6bc0e..fc734c6d20 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_8_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:807efc10c7d97dcfcbbaa2c696996c9ffe3c69f3f21d992fee0635f5c1dd623b -size 42289 +oid sha256:d66cb07e8dae1e9abbe1fdd8edacaa7fdbb08c510f8be71d75b3d38e9fb77589 +size 42495 diff --git a/screenshots/de/features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_de.png b/screenshots/de/features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_de.png index 1cc92bddf4..248c8d243e 100644 --- a/screenshots/de/features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac79f05488afb65d5675a8068ac1c70c7cddc81b1db5445f1314ce53a1da8ba1 -size 27143 +oid sha256:8ddee4c6ceb4adf8e349e3b672c4b25ceba38bcb34bf29b7b675687e3d81406a +size 27203 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png index 1db3060b52..8f3a836ac6 100644 --- a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png +++ b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7763b052961efdb42fbae39e078ffb00c0f62b084b477862d07f0ec5be5d2f2d -size 23815 +oid sha256:f7cb9b43a0a77152c0f60b8b43a14432a00b4223a28986b72c5d47628d9a98bd +size 23862 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png index aa7855c162..4c8daf31e4 100644 --- a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png +++ b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20528ffe999003d019f3d1484f12cbb1c37b3d0fea363c423cec0033ebeaf732 -size 25622 +oid sha256:0529da9be83a2c70b047546c24cced97414085962e94d196b40e375af6bdf66f +size 25581 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png index 2270819756..5dcde5087f 100644 --- a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png +++ b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c307ff5d16e3f24467d068711fdbeb39b0623201cfa2562ae47b0c954133ae63 -size 27662 +oid sha256:be937bddaba2650337a9acec496ff2654c59d1f6e493100126779b31265e9eb5 +size 27764 diff --git a/screenshots/de/features.home.impl_HomeView_Day_6_de.png b/screenshots/de/features.home.impl_HomeView_Day_6_de.png index 1aa91d8732..15a1f1784c 100644 --- a/screenshots/de/features.home.impl_HomeView_Day_6_de.png +++ b/screenshots/de/features.home.impl_HomeView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da970271201eb9d665400d1ef5791d5e3ae5f770c8bec9c07bd25275ac5fe942 -size 58273 +oid sha256:a9602bb99e322103e2683071d177f8aa892d06d97b877674ed436f8149ab54f2 +size 58396 diff --git a/screenshots/de/features.home.impl_HomeView_Day_7_de.png b/screenshots/de/features.home.impl_HomeView_Day_7_de.png index cae7125417..4ba188a84d 100644 --- a/screenshots/de/features.home.impl_HomeView_Day_7_de.png +++ b/screenshots/de/features.home.impl_HomeView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d11e27bd7446de6523449ce5f718df71f89a7d68ddb17f84ae7cc055273db655 -size 57593 +oid sha256:401cdff29a3252f9d195f945556671924a685afedaf2efb4959a43c41be764b9 +size 57630 diff --git a/screenshots/de/features.home.impl_HomeView_Day_8_de.png b/screenshots/de/features.home.impl_HomeView_Day_8_de.png index ee515055c9..94e960f0d7 100644 --- a/screenshots/de/features.home.impl_HomeView_Day_8_de.png +++ b/screenshots/de/features.home.impl_HomeView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2adf95b28da91bdc7bee8a34d035dea6fd7980970e16143727677b72cd238ec4 +oid sha256:4e65413390340fbc4a38d19c4e1dee7197ed7274b40df911d8b9842d8d077d61 size 56339 diff --git a/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_de.png b/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_de.png index aaf77e7d61..8f843e19f7 100644 --- a/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_de.png +++ b/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7ffc21ad57f165ae011122d20f7be43560430ea219b4b65571340f9f360ad8d -size 37757 +oid sha256:4488f516559fbbe0a4ddee9702e33429b68e5aaed2be38fc5ded310ba7974287 +size 37517 diff --git a/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_de.png b/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_de.png index 865fa85506..2027873104 100644 --- a/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_de.png +++ b/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:313a6ab1cd341cb59c8cfad8436bbd29066d0665c3b3dd9657485e7dfa527883 -size 42574 +oid sha256:895117e26de7f333adaffa364a613c908dfa6b82857006dae97f7e35d3f9089e +size 42191 diff --git a/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_de.png b/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_de.png index f1e0c453a4..9b9f3c1eb0 100644 --- a/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_de.png +++ b/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:074ba8bd973d11e0f349ab46168a0fc1589a9307ef81f7c1e6cb57d912cfc500 -size 37962 +oid sha256:9743b612280b496a466a2ce6c691e57c9a02fecd6775311e3a91f584dad45d2f +size 37709 diff --git a/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_de.png b/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_de.png index b8d86b11f0..543d032e3b 100644 --- a/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_de.png +++ b/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:979dd109a7a494d2e0c29d2686fd62cdcff2b41a82a151c0ca9baa34f9309ac8 -size 33557 +oid sha256:53d543a9065afbd496d16b08f2b1102d67156a8cdd0e64bc9001275c863be182 +size 33343 diff --git a/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_de.png b/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_de.png index 2095324034..0c9392b1cf 100644 --- a/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_de.png +++ b/screenshots/de/features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:996bc7e3530709ac887dcf01d559e3a37902d57f216cb14662f68b99bdfe280c -size 38229 +oid sha256:4707c13a4138eda087301ed08a07165ec341ba509c05be05210a0dc4fd339e74 +size 38155 diff --git a/screenshots/de/features.invitepeople.impl_InvitePeopleView_Day_10_de.png b/screenshots/de/features.invitepeople.impl_InvitePeopleView_Day_10_de.png index 4bf78594db..ab9c8dea1f 100644 --- a/screenshots/de/features.invitepeople.impl_InvitePeopleView_Day_10_de.png +++ b/screenshots/de/features.invitepeople.impl_InvitePeopleView_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27a5bb5482628162bc6da9caf59fd0bf62f8bec661f30896f11c3442ced3e471 -size 54950 +oid sha256:65438115e3ccb2814f6cf1b3e81b5c531cebcf4c9b80db2b35988d6ab996d323 +size 62458 diff --git a/screenshots/de/features.invitepeople.impl_InvitePeopleView_Day_11_de.png b/screenshots/de/features.invitepeople.impl_InvitePeopleView_Day_11_de.png new file mode 100644 index 0000000000..e7c12b71f5 --- /dev/null +++ b/screenshots/de/features.invitepeople.impl_InvitePeopleView_Day_11_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f305eec9b6940a138c88f132e78ea5c39f1403f1a1e8fca1a782dbf46b3ddd41 +size 62553 diff --git a/screenshots/de/features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_de.png b/screenshots/de/features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_de.png index 3b08016d6e..e456e54762 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:223554d39d768b58789951c63a7cc0fe644f618f493e5d71ae5f8e3b21fb8431 -size 61267 +oid sha256:c96ad3d344d4a2c1c2afa1913e668cd7e159a5f610cee44b75c029987c0ebfec +size 65962 diff --git a/screenshots/de/features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_de.png b/screenshots/de/features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_de.png index c4bacde16d..ac514c49d7 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f317b8fb7f924ed6b3f583f316663cac038887720038c560e5d4ba8db45d80d1 -size 53414 +oid sha256:a54d7f3a3091731e6131984823589ce81a10e791895f86f5e50b0bb04b7aafdd +size 57045 diff --git a/screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_de.png b/screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_de.png index 77ec4397f0..2146543310 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa525b97163b401c7540d36b5d0ea0b353e3b61c15c1a264a70b402c9ef1478f -size 34151 +oid sha256:cc83df0bf49880754d2f401c1a629355455bb01770ac0cee48e2affafe93da20 +size 34038 diff --git a/screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_de.png b/screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_de.png index f79c47c87a..a69ccc2e05 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8dcfd0a6965522ddc3d1a0571176eb6889bbf15a390ea7d4348f002ec9696d5 -size 33189 +oid sha256:424a7bb62e6b42c397eff36bbb79a4278c0fcc9f4fcc7f447d10fe5854a1fed2 +size 33079 diff --git a/screenshots/de/features.location.api_LiveLocationSharingBanner_Day_0_de.png b/screenshots/de/features.location.api_LiveLocationSharingBanner_Day_0_de.png index f889a65156..990b398452 100644 --- a/screenshots/de/features.location.api_LiveLocationSharingBanner_Day_0_de.png +++ b/screenshots/de/features.location.api_LiveLocationSharingBanner_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:efd6cb8498b29f79769b3287a062ee1ddad3c34195aa5d653b8e28e4e6f973bc -size 10767 +oid sha256:047f6ea7e1594151b79aac2d2bdb58986134296fcfebdbe920e1caed9a173c18 +size 10545 diff --git a/screenshots/de/features.location.impl.common.ui_LocationShareRow_Day_0_de.png b/screenshots/de/features.location.impl.common.ui_LocationShareRow_Day_0_de.png new file mode 100644 index 0000000000..50cb33a2cb --- /dev/null +++ b/screenshots/de/features.location.impl.common.ui_LocationShareRow_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1998316d12b0e0c4d1bae1e5bdae6748131354deb6311a03050560781998212 +size 18439 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_0_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_0_de.png index 26a2d11771..c8c0a0f692 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_0_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e80b99b89c442b8126cf80c8497b524b03012075763dd34f646fa1f7850a5a25 -size 19956 +oid sha256:2dcc50b57d030b5beb0a266a03e7efe121c3da33a68efebd7be6674420bedbb9 +size 19908 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_1_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_1_de.png index e1f4b27424..95536242e7 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_1_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94bc010e69f70262da8f693185bbb4e5ab65c1443ffb04409aad4b4ac6d6977d -size 38635 +oid sha256:1ef6bdcb5bb2439f22ddf3e505375143096f336a99329e9de8cfe431bcc49201 +size 38605 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_2_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_2_de.png index 5b33246329..5863a06c8f 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_2_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9fcdaa31d82e5e677b39ccea11767febe8d51789d410e0934b07ce27ab4033f -size 35151 +oid sha256:5d04d6aa43eb40d74abf8348e7889be3752d2534eeca0dc4f6d06d26dc1e99de +size 35119 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_3_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_3_de.png index 8381a894f4..126c236ad4 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_3_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ad541415245d6e6ca95514db2aa053691d8d2c8193cf0268be67cc74b1af4ea -size 32928 +oid sha256:ce0a04830f97490915d16b397b09382bdf2869886f702ae3f0f46c57536296c4 +size 32897 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_4_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_4_de.png index 26a2d11771..c8c0a0f692 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_4_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e80b99b89c442b8126cf80c8497b524b03012075763dd34f646fa1f7850a5a25 -size 19956 +oid sha256:2dcc50b57d030b5beb0a266a03e7efe121c3da33a68efebd7be6674420bedbb9 +size 19908 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_5_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_5_de.png index 18dc2c74b7..50b8d90d42 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_5_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6edd90a847c7fc84c25545313f90ec41b8769d4a9b758476725f7ec5255fc167 -size 21838 +oid sha256:9ff9d0497bae0f7ebd9b9ae90c76ced25e665879ebd24b85ababfe64c7fd158b +size 21794 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_6_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_6_de.png index bb93cfc575..39ae54e1ff 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_6_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1342bc5c63e6fa38797d1867b5006ebc91cfa926d189e4ff9060f84efd8146ca -size 26114 +oid sha256:2157c731d8fe8665db915489a1abdc6412d69b807e880e9e140078babfe812f5 +size 26199 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_7_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_7_de.png index 0663eceb1d..ae4dc0f6e1 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_7_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b08870ebcd6da6372363b0af0334524a4288db7242cdb5a377512ef9aa728a66 -size 38883 +oid sha256:20b5b796c88ba1da4c73215272918fcaff8f881afc0a2f5722e87f0dabe5844a +size 44645 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_8_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_8_de.png index bf8a621fe1..2f295689bb 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_8_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a5606179cc7dacc1d0bc86ff0f38a7654cebeb7c428575e2da51d4af0ba12b9 -size 43384 +oid sha256:94cff48b65bbd6887db66e55c665ccbf62b2ca4a27f8f441dbd245de2e5a8e76 +size 43376 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_9_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_9_de.png index d8b464213f..1cda7998bb 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_9_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1ab182dfc2b45c80972722b1963a75c29175c1d09369fafb96c7b0ea04bf08a -size 21171 +oid sha256:0e34089fccaf1b3dd2c9e7f613919d6500f5fcf1e088213dd0cf520e4e7d0053 +size 21126 diff --git a/screenshots/de/features.location.impl.show_ShowLocationView_Day_1_de.png b/screenshots/de/features.location.impl.show_ShowLocationView_Day_1_de.png index 7793479410..a4eea97439 100644 --- a/screenshots/de/features.location.impl.show_ShowLocationView_Day_1_de.png +++ b/screenshots/de/features.location.impl.show_ShowLocationView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f66385d2afa0a3a4c68418c58b65bb9f073512842d6b601ac9ba7170a70f3d7 -size 20266 +oid sha256:4b3858c6ba9a4a2432c0b614af79187fdd2e9c661dc71038c3e656b37232fb6b +size 20034 diff --git a/screenshots/de/features.location.impl.show_ShowLocationView_Day_2_de.png b/screenshots/de/features.location.impl.show_ShowLocationView_Day_2_de.png index cf15f3c0ff..d5487309d6 100644 --- a/screenshots/de/features.location.impl.show_ShowLocationView_Day_2_de.png +++ b/screenshots/de/features.location.impl.show_ShowLocationView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a93c8f459e118b5e97802055c076a31c260a8d1b9b863dec4464d79b5ece62db -size 16650 +oid sha256:ff8a7ece0df808c834e15d9288149a097f9ad21a7b32f343d54f154e397156b0 +size 16140 diff --git a/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_de.png b/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_de.png index 9ea51fc8de..0aa9077531 100644 --- a/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_de.png +++ b/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73eff278a1d56a54cc068a145b73d792e0e40c7bc6db926df2db1c23229ef5a7 -size 19145 +oid sha256:16a63aa2761540e6d5aff903443b0b912ee38f3cd901e6950de5f1aaf7c7b93c +size 19127 diff --git a/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_de.png b/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_de.png index 25429f5f83..c428edffd3 100644 --- a/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_de.png +++ b/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e9079d17f328a1b1bc24c3d85f9c0db5d76a1a8a5937bcbadaa092cb0b71f6e -size 21325 +oid sha256:91bfc6455cd2dcd344514f034e413d0b4cfa3376a82ce0f1421117a1ff610c1f +size 21238 diff --git a/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_de.png b/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_de.png index 69eb1f4aeb..e6ce67de68 100644 --- a/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_de.png +++ b/screenshots/de/features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ff4b516f4282d6a78164c011332a4dd9dc1d0fe5f21ef7b5d574503d271eb90 -size 32624 +oid sha256:e513c81d404236618aa8026320553fd1514e9759d5a34ad140fd7b7b47f55e51 +size 32643 diff --git a/screenshots/de/features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_de.png b/screenshots/de/features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_de.png new file mode 100644 index 0000000000..d9d2f3b499 --- /dev/null +++ b/screenshots/de/features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d96d09c6ea5427bccb08dd89397286c1eedc8a085b6e43774b5ffcb66620cffc +size 64110 diff --git a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_0_de.png b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_0_de.png index c19097e89a..ab3640b924 100644 --- a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_0_de.png +++ b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:392e090f14a98249b1cf3cf4acf180a817c41dbc8b4320ad5b8a2b4636ba0f14 -size 86883 +oid sha256:f8b9dd9cd64e0c2a3350ba46678af61b367d8dc99d580bcadbc84b6c1c1fd8c9 +size 86856 diff --git a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_1_de.png b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_1_de.png index c84f36df47..ad4873db3c 100644 --- a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_1_de.png +++ b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2488a2d453b8a8cd086e531e1c49ac2d41186713b85c1aba3df73cf6c7a5f1e8 -size 86605 +oid sha256:c93865956a26ec38b90687ebf37c54f51019745fa67077030b3785fd6e54a25e +size 86594 diff --git a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_2_de.png b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_2_de.png index 8dd9a8233d..d088e1d0cb 100644 --- a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_2_de.png +++ b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0870cc34ef448fc914f7f4a688781bacaca59edad593509ee5cb333fdcf4acd -size 73713 +oid sha256:03bba1b4cad4eef153981990e20baa9cc88161d8506e4b4346c82933edc0861e +size 73714 diff --git a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_3_de.png b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_3_de.png index 50752acb56..1162e24c5a 100644 --- a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_3_de.png +++ b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:844f24409611ccc30b514427c72cd057ed838a1b4ba058a4c6dd941858908e65 -size 66529 +oid sha256:a424e83fd088385bd821f10bf69ae20f042c7c022205df0bf9b1487a8b2cdc34 +size 66523 diff --git a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_4_de.png b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_4_de.png index e20e0c63c8..89572808f3 100644 --- a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_4_de.png +++ b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf40d60799deb62643ca650deb1a5b5e18f5d26ebca7927064c51a0f3a8b771a -size 57890 +oid sha256:679c59722fd0f9bda69c7fe54c32b068f7c9804f1f90a699aa3b34383d87b0ec +size 57888 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_10_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_10_de.png index b45f9735e9..179c549517 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_10_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec2f3efaf97bcc35ec40f04a2196ff6c194a9481847e10354aab7a84ffe44cb6 -size 35651 +oid sha256:62b3b2b8aa333d25499036aa633b20c426d583a2610712b2f9848f209633a087 +size 35642 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png index 9c4b79d103..3ad3b34536 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2958036448d7d58899dbfbda756a9d426bce315ef5b435d79396a9a80784940a -size 55178 +oid sha256:841ab6bbee9b1c45686f03795cab778dc2d46afd94970f33d51a8bc55762f3ba +size 55126 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png index 7844abdf8c..4f93c878d2 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d217bc76203d0dbbda69a567502a0ef2966408422ddea258b1171a72d32c382 -size 55460 +oid sha256:54f7192fc2ff3db4043774a3afaed469bb75350ac162bac1dda056573eb508ad +size 55090 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png index 4c23a8e276..f4968e1595 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca13fadfa2beebca49b47539556a7b65f9ad1e79b73af5d1682c743899c7563b -size 47402 +oid sha256:e9720b70b50345c0953e159ba884941771b6bdead9d4414dc7c379f787873348 +size 47541 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png index 8a481ed3cc..8abfcb9328 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:606c1a9e29d01dd48463dbd2002f4778c1343e06ddf61c6587ac2212d292fc17 -size 51296 +oid sha256:d74dcfacd248e1eccb0bfb82cf2c56de093cb94dc0897ac62866f54d9f5c0185 +size 51433 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png index db7d125d2e..dacd84c40e 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:553c3b8caf077981aeabe99175f5626737347f5ddf8eb983f6453f18d52f28f2 -size 50050 +oid sha256:effa56ccb316f3c64243cbbcd2c5f5027bc7b7a74bf7d80771ad75c92a65e26d +size 50200 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png index 8f8b6a4d85..c391a277ad 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:efb40271c1a53965bc57d1a43517d1043df44bb3df97042b0255f66e96a01a28 -size 45325 +oid sha256:0e44588831139679f11117b75e37675e53087145e2697ee92590ffb41824627d +size 45439 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png index 7ca56941ca..0978c82b8f 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:918feb5ac03bee1e4b7fd833d1d6c5614aea5356cccd6655fb43152a5d9629fe -size 50302 +oid sha256:4d4bfc867d9d4b5eb40e8577be5e85883ae6273aa26f6ba740039e8b28e8c501 +size 50439 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png index 3f72616816..edeef6eaf0 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:323c2ce83c77f3a68c6543f6c9882639ee33004fb9fd5717f2e1e2b7db68dc81 -size 46073 +oid sha256:2afa5876fe1e67e4d72351d20c0afc07d8c2d580ba53d1fa131c5d64f175dae5 +size 46243 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png index e69c8dc756..949602fae0 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:438227f1c69e12a7f4e661820e1bd8ae49b2849ff6fc4fcab3850f3c17b67239 -size 49412 +oid sha256:d420aa06b93ff7774f7cf3b2963fb43f11cf44f49c9fe8bd0f88a4a65f9ea441 +size 49550 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_9_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_9_de.png index 439f7d1e9e..79669ba97b 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_9_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bce484eb6c854e78da2d354728de6b4f4f74d5f75ea452174ac9f5343be448bf -size 38679 +oid sha256:82cbddc876b376d1c00c3c4c7f42fbf5ac96821b9de4aaeb7364dafe826bd87f +size 38608 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_de.png index 50674e05be..ff390f6987 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a927ba77b12465d718d8b09ed12c4c04b18c2d9474d242a2c1a8bbea5cf3b226 -size 90934 +oid sha256:6bc4d6814c5bfe1d1c4a0943a2f2a0ba56594aab722e12ab66644d2d660776c1 +size 90990 diff --git a/screenshots/de/features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_de.png b/screenshots/de/features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_de.png index 8ef14e6286..c94fd5f252 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7afec3a4721debdb6c14b807e5f654f94edae8ef758a939f40444ab8abc6600c -size 70850 +oid sha256:1eb3e3d002f44b03dd7eaace91eb6c4c3186ca431c5313c602e96806a01a7168 +size 70208 diff --git a/screenshots/de/features.messages.impl.messagecomposer_AttachmentsBottomSheet_Day_0_de.png b/screenshots/de/features.messages.impl.messagecomposer_AttachmentsBottomSheet_Day_0_de.png index ee70305497..038c2901ae 100644 --- a/screenshots/de/features.messages.impl.messagecomposer_AttachmentsBottomSheet_Day_0_de.png +++ b/screenshots/de/features.messages.impl.messagecomposer_AttachmentsBottomSheet_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a4f6b58fa98326481361ddf06120893f1ed6e10c21263370a75c3df40c3989d -size 28885 +oid sha256:17ecb0f88eb0d8b425f9cd30b2a9273ea2ebf11ee9f464b5588d144dace6de40 +size 28815 diff --git a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_de.png b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_de.png index 7ddac02717..cdecf067fe 100644 --- a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_de.png +++ b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4ac79f3b8fb4e231244bd96b244a4f2526d6ed28d558f9d50734d83bbb74992 -size 363909 +oid sha256:0892a187517a5d7d897c1bb39e3b6d90a4fbd4fc317ac0a450704dcbf8d3d50a +size 370764 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png index 5f24ba6d45..04feef2ccd 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c100900785c0c11cd69b60da9ed3909f135b876d0546722f707febd24e796256 -size 42510 +oid sha256:5fd8c9e4b23532f347ba4829b2fdd65327dc0c6159a5790b14b0c9299a24f0e4 +size 42454 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png index b3f0f2fea4..9d16f5e0e8 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22e5597cab58568048c5be1d8a0efc7f9efce5b304d0b043614f37ef63a7e2b9 -size 57161 +oid sha256:2fa93c15e811fd802e96f5563b6e1070519383bf0ae3df3020ac5d08993bf263 +size 56974 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_0_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_0_de.png index aa6f300abb..b874537a90 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_0_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed6d994b119d688999501ebd52c97003b4501f0b4013ab51a75b0f20b24075c6 -size 38914 +oid sha256:1718385f89dfcf0d25959d6276a80c7b060bcfb6aaa4a129289f8e9d24be9d4c +size 39127 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_1_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_1_de.png index 4e493c9379..a595226a80 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_1_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c874ff8f546368fd02b97675058578c0915739a8d0419df4d9f7c57fde325acf -size 41638 +oid sha256:52edad578eb163bb017c90f74ebea37c67baaf40f5dc55d19aed9a871967069d +size 41911 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_2_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_2_de.png index 9bc140b2bc..f6cd569c15 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_2_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:317e4ac99bafec3256906621758d7f2122e3fa9f751f60aa825d4a14641f3b5f -size 42146 +oid sha256:70b638e21fd2ed3c46b73d64b9b2a659676562c6ef4272afb57475fca9ef256d +size 45323 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_3_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_3_de.png index d015e20463..99223c780a 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_3_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26e47150797605d5233f2d9043b325a3e674b62f68f0aac172588c3f24385f52 -size 49345 +oid sha256:a6686734904d6d4437d8abf5a0e7fb6dac0ec72d32e03f1ac321ad13e8973973 +size 49461 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_4_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_4_de.png index 4e589ad6c9..237461810a 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_4_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c75eeda50c14d95266168e9e020e2c86ef5973a974c7f7468e93351b871f4820 -size 27597 +oid sha256:6d9511eca3594d876b686813bcbc3275b745f4f7cf0b6e68b7a4c13a884e1e8a +size 27078 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_5_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_5_de.png index 788533938e..f2cc5e740b 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_5_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bdf19c2c13de4fb7b6eafd0a973f141054cbe45c624c887ec5c34c5482a9630 -size 116876 +oid sha256:23bd10dca084a98d02d7945f78a9b7b83d8db2405d90c6e4ab781e0c569a2d91 +size 117032 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_6_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_6_de.png index cfa660cfca..0a4aaabe49 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_6_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3f339d9122bc91e2838c7cbf8bee8f7cc50ff75540d5337834969531e6f8584 -size 42507 +oid sha256:012f6a80882cc973f9ced653b2e569769cc75bcc8d9317a9d8c8cfad71f16787 +size 42801 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_7_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_7_de.png index 45eccdfdcb..5af2bf4baa 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_7_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6075dc5472b55e47a0156ff75649f8360b791006dc3313d4a067749037c39db -size 40318 +oid sha256:23cb62621f22cf2ee0f2c0811141efa2ae45fbca8ac11ef55a0f9ff86ac64874 +size 42030 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_de.png index 9f2744b09a..6b3a52c5c7 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:024657931956e21eb6decfa8a39a213fc621d0f4c694bcbbd56d013d49bda838 -size 61364 +oid sha256:5a20e8cb958f57646dcf248d26f4c5615e42f533e1b2caea79e11308618fa0e6 +size 61967 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_de.png index c3522b0cb7..0c953507e1 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ec9e46a3360cd996a219151fda05f38248af4bfe3f2a39d0becf74d840f5478 -size 61231 +oid sha256:fd88aeda625381fd2c5df2f2d559f2740cb0feaa2d5c60d54184bc5bf0aa5f3f +size 61819 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_de.png index 269c6bff1e..5ef05e2919 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:038b8dfdb5996d6da4b460a5ebe3b2f4aad5549a894babc583bcf15ffc549264 -size 61225 +oid sha256:9df1cc31f2af90ec34b3c8a0b5f83c93ba58ede00ac3744f82c0687f33123191 +size 61825 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_de.png index ab55a3a8c3..f61f1701b4 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49b2f7bc8675304be3d71d144584b7e7ace735c187093749cc2b94a7af091bef -size 61229 +oid sha256:cb2c03231f9c33f2336e1b6555b6e30df202a2fc7a276db56c5fed3cc3ac0b99 +size 61820 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_de.png index b874052a4c..1f3835ec43 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23d2a782811171fa55a0861450a667edcd2d22784ee3711bd00674f178144a4c -size 61120 +oid sha256:f6a27e52b0acaecba72e77ff940bf9921337a30aecbd2212309b342ce33cdf99 +size 61742 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_de.png index ceed65f824..d79c5c60d8 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16644a82af1efbf83460df01b14d85d1c2d16fa2e1a9bc4d1a591082adfb6167 -size 61366 +oid sha256:4cb09d5eafeda3f2534fb0643d2abdcd6827b325914b4787eb85bcf5a9503629 +size 61976 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_de.png index a7498d2de5..722a2c78ca 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b6c34ba3d6d833137c1faccbaab4ab32b844af05a6ffbd136b6582c16aebec4 -size 60891 +oid sha256:7fa6c2f4183a6a541fdf0bfc40e71d766faa75b20cbeb4f1fd3febc9a9def457 +size 61482 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_de.png index 6d227268fa..d7f542b204 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:114543bb1fb1985f0dbc2d78044e4f606eab3e30a6d99d83c5c0985fdc6bcaba -size 60458 +oid sha256:56e588e4b85d80524b9d14cf8ac87504c79493240c713c4c1aaeae94d4feee4d +size 61053 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_de.png index 32702f443b..346c7f92b7 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74261b09ccbe64e4a3902e907e22391bab3264f43f1f449d43955cb4326d5912 -size 63797 +oid sha256:0f3a8df55e94e80166bd967eeb88055d9f3941de6c164075547a0bd236c49952 +size 64701 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_de.png index 740cd2486b..aa857edfa6 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81240edb02f00691d55f343e53d3af922bbfedb32e60f757a62ae8b2f43230c9 -size 58356 +oid sha256:10dd2fdb3d2f7a10b77e26c57946491c73aa7ebfc446875b4ef4b79e869b0c39 +size 58807 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_de.png index e67d492f0e..d7db8fbfe4 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:822b20a349d75de5c1f7000d394f3e32b62dc419bf157dec025ef56ecf3d7f80 -size 58218 +oid sha256:dd93c4f402433e167d56cc0edbb247fe42eb8f707122f46876f3da6f87fe2e65 +size 58688 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_de.png index f4f9d96dd6..e45aa2cf93 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:03d5a02197f9ad9157752533aa9ed1c2659a0b2967c3007b6f2b2f2d2b7a98d8 -size 58210 +oid sha256:481ea026240e24b929c46d8cb7488636af8598f20cb150ae950ed4351a0680c4 +size 58691 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_de.png index 87337d55fe..621873bce7 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49569410e3b6e1d009e87d7d652f9bcc2c7d4c77e1e5ca86be7fe9dfed9c3c1b -size 58222 +oid sha256:1a16cb75c06fd44b4ca8442e83e8bd859f64ab74065b2aedf784590b2aabc739 +size 58669 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_de.png index 0cec2ab3a9..eedaa89535 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ab6c693d3cd149dea1f9e8ba95c09710aa52c8869a866412651862680c9bd4b -size 58083 +oid sha256:5b744a39e92500125729aeef314ef07aa2c46bccac740f3cf22207fb4c0654d1 +size 58568 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_de.png index 3252e63243..76a571c9eb 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f30a0a38f20c9ead24ec669fbf7d25713d1e41b1de99d4358bfbc36f34146d5b -size 58356 +oid sha256:c494f40ddaaeb1af4463191d1e22729ef71bbe996b0ff746f50190a0dec51256 +size 58814 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_de.png index 37272bf3ef..d502516b58 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4f03d366950465820c07b24eb401614d122d244c1c782993f679d61d234418f -size 57924 +oid sha256:fc3760069e627264ea63a42202d3fc6863ef4eb02c7c2579bbe37b1a71c432cf +size 58423 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_de.png index 70b5f0ccc9..bdb6b68b61 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b787733f83c3c7d7f3ee8d144f4aa3f0c5a9f7985854d5b427e8ac4fe26781ab -size 57654 +oid sha256:6e0aa0f9100ca3b433bca30c63063974aecee319b3c37549f1deb41c8805e84e +size 58109 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_de.png index 856754abf5..3d296aaf23 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:43dae2fb98ed0f6fc1f4d67d1c5bf2a8d0e45e5c3ba217af0dd31c7d2ea48528 -size 60496 +oid sha256:90d6d76d42c9f433c026f798977bf9908758c1c4a41da5885b62041cce33f2ef +size 61233 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_de.png index cfb15dff1a..22561db032 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0335d534e1ee34920b9f5adf79ba614d03ac62fe352a1c9da57c79f2f74b87d8 -size 60698 +oid sha256:99f140ce7dffc7b2c1cc9b3c34bb9bcbef04b94ad739be79e6a63dd8641f6ce8 +size 61233 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_de.png index f14eb96ef4..3726730ae8 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3d773c0c42aa2070fb201fc25f4b456692543ff85085693f9003b839b31df26 -size 60584 +oid sha256:81f808913d719fb64ccb4d6532c3590e9c854615e3770e37442630fd488c6ad7 +size 61089 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_de.png index 6da04e5146..4a89df7275 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0cccc5f8efaf4ec7dcd2c0f5f21cf3141ee0a5d6884aee64b4396ac0da883d0e -size 60596 +oid sha256:c0f6af7885d5ebb97be1b626bd3f2bbca63070503c0a7cefc73937d91d1555b4 +size 61105 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_de.png index 119cdecbbb..2f509e7479 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb801d3af2f9343662380f6df93c9dedfe513c6c842edc91608b6b7c0f9b6b27 -size 60594 +oid sha256:17a2aa055b8435f0502540dc32d0e1ed454b27ca48d5d028fe0c8bded167f36d +size 61104 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_de.png index 8c3bd9e91e..1c916683fc 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a428f83ae44b904269e7f1a8dd38854c3d99142bca7b14309c8de2484ba5269b -size 60524 +oid sha256:619fdb3918970d86cbdbfe09809bdc5f79406edff4c1c30fd82658be24c54f31 +size 61053 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_de.png index 6588f7c6fe..b90168f660 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11abff30b2a9035a3c619ed46ef056133e90af2e67e2febe82969f5d8f4d21e5 -size 60699 +oid sha256:6bb91aceccb223fc37fbff0b479585186278b3666e48a1f7ba0d9b90564286f1 +size 61232 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_de.png index 1e610fb222..fee1c64994 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:beac41fbdfcdcb78f0693e0a4f247ae43fd29ee11f6fd8e1854c93beb2152d5b -size 60295 +oid sha256:343281ef6e66ccc7f8bfdfb590dfe0237cb96bc8113ca205894b5b7e1a90867e +size 60803 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_de.png index 68442d924a..0db88371da 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05215baa5484072411d171d01d34def469a96740764782b2625f36b22c77daef -size 59999 +oid sha256:79fb880efb60041c1a1cd4188ab8c8535ced195756ce7c808c986ffd8c729660 +size 60490 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_de.png index 763d414677..10333589af 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:851e30212320b7398100b5bdf712467538f9d58bd50724239d3ff013c596e6b8 -size 62968 +oid sha256:3df9f69bc3f032de02907f2ab74c1ca655d5dc14282bef788a79526acf7e892c +size 63801 diff --git a/screenshots/de/features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_de.png b/screenshots/de/features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_de.png index db850ce640..1493408483 100644 --- a/screenshots/de/features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c65e5be2900f7e56e3a71f0a2625b0b54cbb7c1a1d5efbad1127d22b7a2af75a -size 61628 +oid sha256:830d202770f03b4301d1badad153782e46e5e1bc0f8d939d79f76dfccc7dc742 +size 60712 diff --git a/screenshots/de/features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_de.png b/screenshots/de/features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_de.png index 433611bc24..79ea8cba7c 100644 --- a/screenshots/de/features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13bc2115a6615c9f467d4041c430e7a64785f731ae6e773f3e32b0b198a42514 -size 28188 +oid sha256:21b510e1f2fcc18fb1da91b4758b610fa06cd9d9427bc41d23be5dd891772c9b +size 28150 diff --git a/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_de.png b/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_de.png index daea1e22ae..ba73f75bc5 100644 --- a/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:efd05a781d752f681f06ec085b7a352eda4d6a4db9107fc633f5aa187262c304 -size 59147 +oid sha256:538ccc4762120a273d6a27432978dffefa00c1a8aefbbe1b3a3d5ea98a62c7b6 +size 58934 diff --git a/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_de.png b/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_de.png index da2ad18fe4..7592627dad 100644 --- a/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7ccab9e3dfa3913fa9fb0ba2b2d234c136f9a06fa9daf212919e2bf417f1465 -size 56274 +oid sha256:52d664b2a5c80a186703955e7367264d0a478359dfcf6fa346f6cbcc609c8ea6 +size 56289 diff --git a/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_de.png b/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_de.png index fec5646925..2f5f9ab71d 100644 --- a/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_de.png +++ b/screenshots/de/features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6f9541fcb93dabd9e48738da4a4ce12d5fb64c58cfb40c800567f8c59c8a1c9 -size 53689 +oid sha256:54d9afcb117fee4700a236bc08a2194b7e30072ca656e905a52414635794dd0c +size 53874 diff --git a/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_0_de.png b/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_0_de.png index 3a0ebcaa2f..4abc7d13bf 100644 --- a/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a85070e2115cf92ea89f969ca6aa726a2e209bf9c0e8c131f54e261442313ee -size 56390 +oid sha256:80edb8ac19fea5b0e0adfa05178e10faccfba5a6efe26670246e9ac90d928d14 +size 56644 diff --git a/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_1_de.png b/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_1_de.png index 0c9bcdc2d6..6598da3eb6 100644 --- a/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_1_de.png +++ b/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04b4c9f15f880e5b82741ddadb2cded1f9d51c5acb96157042b711c152087713 -size 47422 +oid sha256:c6800acdb6fb269787f3349773da1ea3e56b0f6d423de250c3947edf5d072720 +size 47099 diff --git a/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_2_de.png b/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_2_de.png index 3a0ebcaa2f..4abc7d13bf 100644 --- a/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_2_de.png +++ b/screenshots/de/features.preferences.impl.developer_DeveloperSettingsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a85070e2115cf92ea89f969ca6aa726a2e209bf9c0e8c131f54e261442313ee -size 56390 +oid sha256:80edb8ac19fea5b0e0adfa05178e10faccfba5a6efe26670246e9ac90d928d14 +size 56644 diff --git a/screenshots/de/features.preferences.impl.labs_LabsView_Day_0_de.png b/screenshots/de/features.preferences.impl.labs_LabsView_Day_0_de.png index 574add3e1e..f470e40fc7 100644 --- a/screenshots/de/features.preferences.impl.labs_LabsView_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.labs_LabsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2465b0bd9ed158d8e3a8bdcd3579999d617a6fc0203b02846929db52b342c3dd -size 47828 +oid sha256:15671af4223aeb147e9b942c709f0814855226b5f8084e3f2b65485075d865b3 +size 47811 diff --git a/screenshots/de/features.preferences.impl.labs_LabsView_Day_1_de.png b/screenshots/de/features.preferences.impl.labs_LabsView_Day_1_de.png index 933ca207dc..c2ac9324ad 100644 --- a/screenshots/de/features.preferences.impl.labs_LabsView_Day_1_de.png +++ b/screenshots/de/features.preferences.impl.labs_LabsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d6182e68b425e8cd4a103d59a0d8cd9d488badf33719e71184b8985f52df7c3 -size 39327 +oid sha256:9b7f703b770ec3442e3ff31d7d36e6abf3c4860d10b1ec2d31ea14162d7821aa +size 39228 diff --git a/screenshots/de/features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_de.png b/screenshots/de/features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_de.png index f926410a8d..ca859882de 100644 --- a/screenshots/de/features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa256a49a59f067911dd5da6934fd1c75330c62430769a5914108926e8e7a90e -size 43720 +oid sha256:005d16221643cc3da7d2eb35cc4b3a9c08c3817c9cd397bbc7a628aa95e2bc69 +size 41295 diff --git a/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_de.png b/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_de.png index 64c764a8aa..729dc42673 100644 --- a/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:469e3ce9d6ad2997c4611ee981f104207a2f492d727877383923a8d80c2824f8 -size 50096 +oid sha256:a40374488f7ed266359193caf6f6bee3c8c45923322c14a83dbaae8994dbe12a +size 49794 diff --git a/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_de.png b/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_de.png index b1207d5499..deaed33d3f 100644 --- a/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_de.png +++ b/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5fc7f93f7c419f911b3d5b6b306b4eaf0cd64b84f21696dfa30373b0393d69b3 -size 50859 +oid sha256:429c69e344cfc0ea55db83211a1f4df935d1e4ec4bca2a1bcfe9060cb0ef96f9 +size 50444 diff --git a/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_de.png b/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_de.png index 77d5dad1cb..f9cd012379 100644 --- a/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_de.png +++ b/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abcc97de678f492673a85941f66e1ad1fd98cc5faa2457105c6cecdf41ae6341 -size 41942 +oid sha256:bc1c683c9d5efe361c087e35f7a00b2989933bfab8ed157233bf13d1e76f6cae +size 41719 diff --git a/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_de.png b/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_de.png index 8f6beaa251..67481575ff 100644 --- a/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_de.png +++ b/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f7127ce6ea3bc311e5385cdc6aecfd5cd48d60bb1d1562e7167326f87dcf38f -size 41153 +oid sha256:c9874c8c033b1005389afe8e7c58166a02d7401dd2893f45a0ee3beb3897ba34 +size 38868 diff --git a/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_de.png b/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_de.png index f6955a4d3c..5a39cd248f 100644 --- a/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_de.png +++ b/screenshots/de/features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2500b1577de3fec5fe17f441a92aaa8a52137d08a85548f03bde41e273d5aaed -size 69248 +oid sha256:4f6146621ec0e29ea99da2a648bfec43698eefa1194ebeda2e9177088655171d +size 68145 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_0_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_0_de.png index 078e7d0f20..28e10cd29f 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f1404b8d9cf2a4074117a0dd5f7c600bde096f1b3061e9ba86f76d316b56a07 -size 64370 +oid sha256:ebcfc4fa6ef768abb664902dad0724eaf73158b4b64369213d4db78f2f704b39 +size 64287 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_12_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_12_de.png index e6af8d3a74..6decdb5c2c 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_12_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fbbeedd76ea3453a747c860561d9c5951026ad49f44e67d56d6ad8ddf5ec208 -size 71632 +oid sha256:cec947e51e186db75c49d3a3adc47982ed905e807bcf9f16c8a82540647d2a2e +size 72679 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_13_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_13_de.png index e4e52bbd28..4dedf916b5 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_13_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c224dbbfef24812c9f1fdfc5cd91cca8a615586c13e1cccf99af8bcf2017d75a -size 19447 +oid sha256:e80b9cd741982133f17e49cace98ac0f882fae9f3a133bec4fe6a09c79bfa89c +size 19417 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_14_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_14_de.png index ed49db01f0..b74ab5753e 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_14_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:989614879ae7f0104235f3547394e4491581b081f1bf091645376487a09d00ee -size 53148 +oid sha256:9a7fe93346bf094176a1569b936b2dc71fb5900996d8781ff6f93205d746da60 +size 54740 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_15_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_15_de.png index 55c801e0d2..2ffd576496 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_15_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc6663f6bb621600ffdfe327317f80e859216fa3071914ea2210af3803c747bf -size 51729 +oid sha256:10ef1294629ac095d908aa90cbb9405ab3a36d9a9aff29ac58e59e4d18958782 +size 53352 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_16_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_16_de.png index 3cc9288d94..c895b9126c 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_16_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd73a2f4882661074626c99dc53bc3fc7cf52550ad2817a60dc366a078dede4a -size 52964 +oid sha256:a2d14e0d32b87800ed3e37abf27034759343cbcf2cc83175c1af58cf83c7dbb3 +size 53624 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_17_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_17_de.png index 81ca5e31d8..cdaa1079da 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_17_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1de536f73fde823cf3ba37a12ae778f5ddfd2c0ba408823c92c6b197a51a5969 -size 52880 +oid sha256:a8ab85c60cb9bc10c56cef4acbe4cd79b5992fe966e96bf9ce8305eaa814d359 +size 54477 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_18_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_18_de.png index b2643af43a..6577a47a2c 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_18_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_18_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b43ae79cdc8523d93a55a53098d7cea5d60003d954ae1f87bb30c5a2af66308 -size 48869 +oid sha256:58edba96fe40c8653f98116fa297a38b1e1029d23301e36e62f8dc179e5edbed +size 50050 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_19_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_19_de.png index fbccf3bdba..5e45f767ca 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_19_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_19_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4123b4b31046f99612585b7132f1ccea6c69a34c3bdce671b56dc8d23585493f -size 44900 +oid sha256:3f99b8b10e5cb6d0e7e844932b06453611c763494c449c9f1f1f524c5eae06b9 +size 45713 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_1_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_1_de.png index 81ca5e31d8..cdaa1079da 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_1_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1de536f73fde823cf3ba37a12ae778f5ddfd2c0ba408823c92c6b197a51a5969 -size 52880 +oid sha256:a8ab85c60cb9bc10c56cef4acbe4cd79b5992fe966e96bf9ce8305eaa814d359 +size 54477 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_20_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_20_de.png index 3a78ffad51..14ff3ed7a3 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_20_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_20_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8dfc412406efc35555095baf1b3bc408b211789e3d4ddc3e0e2f7ceb0e9176a0 -size 46489 +oid sha256:00a3099f61911b52367b8598678c24dc27f05b8806b20c86324f13cf7e26d3cc +size 47623 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_2_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_2_de.png index 884d3d5322..3c6a492fc0 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_2_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:297dfd25bb5b2cc0a1164febbb71623dd534f8ddb8614c50d50743c9ef417de1 -size 47530 +oid sha256:2cb1b274d525e59e3c93355bac24618d5d38bd5df58907b8f6b7db831876ffd6 +size 48741 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_3_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_3_de.png index a434f64c54..5f36cdb331 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_3_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc7d4a5537f9c3f87be21f2a2b48dab2568285cdc15c6ac6443b8d0696fb29e3 -size 48558 +oid sha256:9546e98003536641b258b39fa282283f6bad759eb05ce3fe9c7ab69b02b1cbc3 +size 49773 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_4_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_4_de.png index a434f64c54..5f36cdb331 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_4_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc7d4a5537f9c3f87be21f2a2b48dab2568285cdc15c6ac6443b8d0696fb29e3 -size 48558 +oid sha256:9546e98003536641b258b39fa282283f6bad759eb05ce3fe9c7ab69b02b1cbc3 +size 49773 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_5_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_5_de.png index 81ca5e31d8..cdaa1079da 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_5_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1de536f73fde823cf3ba37a12ae778f5ddfd2c0ba408823c92c6b197a51a5969 -size 52880 +oid sha256:a8ab85c60cb9bc10c56cef4acbe4cd79b5992fe966e96bf9ce8305eaa814d359 +size 54477 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_6_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_6_de.png index 0a43e1b84f..f2f5e95e76 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_6_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:936871dfc521b9b18b12cd52c97d1b67fae39226fb0b065b13f20a061c9a842d -size 45847 +oid sha256:9a260b78438fdb4f4703582dc2995f578be339974a2d4a4c00d5db7599528028 +size 47048 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_7_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_7_de.png index a20e7a834f..cb26672a0d 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_7_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a7701285cddc1f66a569d60dd4799cf35ca070e920f95a2463d4f48d81430ff -size 50861 +oid sha256:a5e4cd12be5556860aac1e80792a6fb22210e39cd3a4060e44a1664caa46dfb3 +size 52111 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_8_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_8_de.png index 81ca5e31d8..cdaa1079da 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_8_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1de536f73fde823cf3ba37a12ae778f5ddfd2c0ba408823c92c6b197a51a5969 -size 52880 +oid sha256:a8ab85c60cb9bc10c56cef4acbe4cd79b5992fe966e96bf9ce8305eaa814d359 +size 54477 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_9_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_9_de.png index 81ca5e31d8..cdaa1079da 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_9_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1de536f73fde823cf3ba37a12ae778f5ddfd2c0ba408823c92c6b197a51a5969 -size 52880 +oid sha256:a8ab85c60cb9bc10c56cef4acbe4cd79b5992fe966e96bf9ce8305eaa814d359 +size 54477 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png index 1e32baa819..2ae72d9689 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1586c25251df5634a3fcfc572b1926875213fa1af7e456490e064650b13e99cd -size 44059 +oid sha256:aaba3c19b010ffd52f6230aff6558cf540b24cf744af6fb12686af48a15143ca +size 43156 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png index 565e2fde0d..d071bf69c4 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffd7acae43a61f98605e7f8a6de3e00f8432e8ab42974e2d0e0f3d37550c8ab3 -size 29907 +oid sha256:0764b13f691ca467984fe6bcceb95cb23d71692be727b7ffcfeb3c9fb4b2d316 +size 29894 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_2_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_2_de.png index 8ba326f935..2dd8904bd5 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_2_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ada78ec8fb22e50de9f0ef54fce8f8d5f6ab23948b9cb32334dc8034bc7aad6 -size 40880 +oid sha256:5b6d1134d206d8915960408e7fcf1927128113c5f4c1e916963615610f8425cf +size 40891 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_3_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_3_de.png index 411cc0b223..04230b5373 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_3_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff0bc0d1615172e9120e42415b2b660042507234a5ee783a226f4a274c4e9b44 -size 30945 +oid sha256:f8d1c7edccbd8361168cd8dc0f14ef5222527b0d5fd4daf9e5ab5e0682bb9618 +size 29821 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_4_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_4_de.png index 82fc1769ff..d444e08c9f 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_4_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98087aa1557fcaa46245813d37424d28696d83220d1187d4b5dc4cee1727010c -size 32167 +oid sha256:4fd099ea9346c16ee9ee698fe8b87d233ed30464aeae8aa2c253d22724e60519 +size 32163 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_5_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_5_de.png index cf40424682..57fc5ea785 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_5_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93ff2a28aa016436780474d98042223d0d9e72f877003c0f476b3755ab6e92ad -size 33739 +oid sha256:a688d51827de99ee98d4839f822a532f89ac325d09c185d47bd88bd06e64ad20 +size 33741 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_6_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_6_de.png index 7d1e4fb3ba..599de495b4 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_6_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe73ab4a3aae2660068503f2fcdb5aad12c20d0ce3ff202036cf820929a649c5 -size 20954 +oid sha256:f23c09550d81793b55e787d069d92d5fea075384f5b0eb5f4c9ed5215e1e37ab +size 20961 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png index 5296434550..8e2be71c09 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8e596b8e70ff79e8dbeed82f6053e03eef3073907bd26929c34f0022f3fbd65 -size 45634 +oid sha256:c3496c387e518721f682d8d37f86d1ba44eb545c435b9021348aa32a8f285036 +size 44664 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png index 2a6d57344e..9a07ad459c 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2693253d4c1cf72c36f8f75fb823f2b1e173ffdf3c3c7ba9f1e688de6d8c26e8 -size 30987 +oid sha256:f4553d9e309ac70ddcde67918d0c0b2cf723bc0b9872ce179f72fade0c5350c1 +size 30980 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_2_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_2_de.png index fab99fdd74..3765c2b845 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_2_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9519a47ab91235acb3cfc3f361dc01ac7fc0361aedcf7b4a9aaae3419468edb7 -size 41934 +oid sha256:295453fa5dc5fe9bbe20feeaa181bd87bcfb70e61f77e0091f030395065c47f0 +size 41936 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_3_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_3_de.png index 06ad4f1cfb..2dfc50230c 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_3_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a39b5c91cb8e9b75a2935cca6561e83570eebd917873dc87b596c43f2d0716f0 -size 31932 +oid sha256:b39306e91c542a171ae00749137a8dea28f969d14b520588d8b5a62dda2e36e4 +size 30763 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_4_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_4_de.png index 1d51c3caa5..83bb2705bc 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_4_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe34b39b3bd4bd5a9d78f1cd2165de9334fde9544f6abc8b40862af14b33e470 -size 33277 +oid sha256:dfce749148c0039600a8875f7532ba25bf7d995b29d8ddf6b2c0fb19c291cc91 +size 33271 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_5_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_5_de.png index 883a5ef83f..961959abe7 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_5_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3b12da7a34e250eb3669edcf2d1fafccd059ecf133e388bfcec581437201c97 -size 34957 +oid sha256:e1c2366fa39aa41e3737b1dfde32b002a495fd65e8b2661c620c76f404fc42be +size 34928 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_6_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_6_de.png index 7c853c2348..edc4af544e 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_6_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e32c86006cae6c771bd03d7360083882cb0673370af3f8c070406718b5cee39f -size 21351 +oid sha256:10699fd9d7b2537e903ad5f3e287781e4db162e6abc931ad6724f4f2b0fb97ac +size 21347 diff --git a/screenshots/de/features.preferences.impl.userstatus_UserStatusView_Day_5_de.png b/screenshots/de/features.preferences.impl.userstatus_UserStatusView_Day_5_de.png index d31716420a..ec10c5829f 100644 --- a/screenshots/de/features.preferences.impl.userstatus_UserStatusView_Day_5_de.png +++ b/screenshots/de/features.preferences.impl.userstatus_UserStatusView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81741b929111f37c1b062e205b8d36259807a8aadb024848f03a5e4b8562f63d -size 13056 +oid sha256:f9fb27514c82855f49df97f578c889f02883d7d25970a68192e1450b4a53f6dc +size 13207 diff --git a/screenshots/de/features.preferences.impl.userstatus_UserStatusView_Day_6_de.png b/screenshots/de/features.preferences.impl.userstatus_UserStatusView_Day_6_de.png index 9c02c0eb7a..6b3d9d46f4 100644 --- a/screenshots/de/features.preferences.impl.userstatus_UserStatusView_Day_6_de.png +++ b/screenshots/de/features.preferences.impl.userstatus_UserStatusView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49bd134a83cb0c6b9b839e6145004a07bbfea3bd984f9288442a1c5cf29e0d84 -size 11846 +oid sha256:3c51e7ff476d687b8cc77f82263c02702f31758e363af59c037456c6530e6bb0 +size 11997 diff --git a/screenshots/de/features.rageshake.api.preferences_RageshakePreferencesView_Day_0_de.png b/screenshots/de/features.rageshake.api.preferences_RageshakePreferencesView_Day_0_de.png index 92e0cba9ae..f27ddc21f5 100644 --- a/screenshots/de/features.rageshake.api.preferences_RageshakePreferencesView_Day_0_de.png +++ b/screenshots/de/features.rageshake.api.preferences_RageshakePreferencesView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84dbbb41148ae5962843a1ef337ecaa0b8d5fed1f1b0c40fe9e7b1b12af80afa -size 20076 +oid sha256:4baab4b95202730327f8dcc4ddd16d3d078cd0c0f6dafdef66e38fb0d129d13c +size 20143 diff --git a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_0_de.png b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_0_de.png index 416a90558c..72d7ba8392 100644 --- a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_0_de.png +++ b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d747bd7c8ff2fe4ddbfbaf7dc58adbda51b6eeb790cf9b9886a695bacc598e5 -size 55304 +oid sha256:00a586d56752e967ed86b8b26c1217a0c590dff9381b78a4de3cab4627d47198 +size 55050 diff --git a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_1_de.png b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_1_de.png index 402f60a833..89deb6349d 100644 --- a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_1_de.png +++ b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9aa99577ce3172b8c8067efa3494c541a409c6e3d021b84209ae77f192cac31f -size 115625 +oid sha256:f61085b64fe9ffccdf7373f7cf1b636a7a1bb401ea872a109f1537bf857d01bb +size 116347 diff --git a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_2_de.png b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_2_de.png index 3e53f7bbba..ed5e981a3d 100644 --- a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_2_de.png +++ b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e89127359e73a5ae2002906732958f071c989a587a091cead3859378d02aa87 -size 52421 +oid sha256:a94df4d203d0c45186a54acaae4c66a71379b2ee3127488ae2d28e7961357229 +size 52077 diff --git a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_3_de.png b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_3_de.png index 416a90558c..72d7ba8392 100644 --- a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_3_de.png +++ b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d747bd7c8ff2fe4ddbfbaf7dc58adbda51b6eeb790cf9b9886a695bacc598e5 -size 55304 +oid sha256:00a586d56752e967ed86b8b26c1217a0c590dff9381b78a4de3cab4627d47198 +size 55050 diff --git a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_4_de.png b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_4_de.png index 70642b4911..c9cde45f9e 100644 --- a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_4_de.png +++ b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac3cf799c382dac09ec91c857cfbbf15148863cc66921bee326d76f3700ba168 -size 44154 +oid sha256:847592e06f55cb5f5919ce3446889c716bf1291caa476ec23224f694eb4e0a15 +size 43927 diff --git a/screenshots/de/features.reportroom.impl_ReportRoomView_Day_0_de.png b/screenshots/de/features.reportroom.impl_ReportRoomView_Day_0_de.png index 27b21e255b..ba288ca42c 100644 --- a/screenshots/de/features.reportroom.impl_ReportRoomView_Day_0_de.png +++ b/screenshots/de/features.reportroom.impl_ReportRoomView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2844991289ace64b04ed2b4cbda763c5ea929cdb43f44cbb28ea36a3820651ff -size 33241 +oid sha256:2784efb92c72d316fffc5e69a833c1cb378e3880dae72b373d9ed997cb730543 +size 33251 diff --git a/screenshots/de/features.reportroom.impl_ReportRoomView_Day_1_de.png b/screenshots/de/features.reportroom.impl_ReportRoomView_Day_1_de.png index 6ca2a6d8f3..a20c69b21f 100644 --- a/screenshots/de/features.reportroom.impl_ReportRoomView_Day_1_de.png +++ b/screenshots/de/features.reportroom.impl_ReportRoomView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7e85419e2f890fa43b91b3f540c0a6e90ed79d033bced833536825976acacbe -size 31786 +oid sha256:3a19adff84b73fedda5474f5bb5af99be628bcca83d9f8f2fdc046199c74d56a +size 31833 diff --git a/screenshots/de/features.reportroom.impl_ReportRoomView_Day_2_de.png b/screenshots/de/features.reportroom.impl_ReportRoomView_Day_2_de.png index 170f381765..b21060262d 100644 --- a/screenshots/de/features.reportroom.impl_ReportRoomView_Day_2_de.png +++ b/screenshots/de/features.reportroom.impl_ReportRoomView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:108685f8df86dfcf24c41d1007744ed49e3ca426afa8cd7a6601290ae2026d1b -size 32859 +oid sha256:d9b0729665ed3faf1b6a9bb63ef99a0ec96261eb5606fac25345c96aba2e9ef9 +size 32951 diff --git a/screenshots/de/features.reportroom.impl_ReportRoomView_Day_3_de.png b/screenshots/de/features.reportroom.impl_ReportRoomView_Day_3_de.png index 253b101741..df5213808e 100644 --- a/screenshots/de/features.reportroom.impl_ReportRoomView_Day_3_de.png +++ b/screenshots/de/features.reportroom.impl_ReportRoomView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:869d098e9048a1963ede2cb8840d23d56ab569b0f341d610f06c506923b1de98 -size 28839 +oid sha256:68f7fbfd756a6dfc8c282ee48a182de9a18bf48a8c3cbbecce2dc09e65e9669c +size 28708 diff --git a/screenshots/de/features.reportroom.impl_ReportRoomView_Day_4_de.png b/screenshots/de/features.reportroom.impl_ReportRoomView_Day_4_de.png index 3d1640dc8c..0b05408cc6 100644 --- a/screenshots/de/features.reportroom.impl_ReportRoomView_Day_4_de.png +++ b/screenshots/de/features.reportroom.impl_ReportRoomView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f44fade4fd5d66fe693833ef2ffe1476b0bdab8b2368e27abb488bc6e4afa2d8 -size 31528 +oid sha256:08e7a7901ed21660b620b7b376f7ec4d259a05df37ece4d0d56e3038a1468f21 +size 31544 diff --git a/screenshots/de/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_de.png b/screenshots/de/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_de.png index 05574dec84..92ee85315c 100644 --- a/screenshots/de/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_de.png +++ b/screenshots/de/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8248854c81134542e7ba7d34882fb0bc9728b10d8872e86cc0ac6599a04fba97 -size 57987 +oid sha256:92e1247b3a6705b6e06ef1ed6d2249c84339120e2e74cf3a73edd48dd947ac5a +size 57653 diff --git a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_de.png b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_de.png index be4e082882..21da3d82e9 100644 --- a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_de.png +++ b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:699a1cf4c54ab25a11dd198316403879870759c1972a98919da02cd2a982b2bb -size 31597 +oid sha256:dba677695ae434bd4d98043cdb0ffd7a490943a88f32bd89fe4a7defb74b60a1 +size 31742 diff --git a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_de.png b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_de.png index 185799c671..53868c5b23 100644 --- a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_de.png +++ b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:865f1e58cfcbf1de8efde943e16a4a3f7b8f800d013432c61fc63f17f4fb2da8 -size 33682 +oid sha256:7a2f283f6860e295f5732bb7a7cdcaa5c45d6014b6caa8680f17644418b0931c +size 33807 diff --git a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_de.png b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_de.png index af3856cef6..4e5dda87cb 100644 --- a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_de.png +++ b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:917cf1f5c4d6f5fe2d289e91dc4937902cc9bafac5c511f16aa94be8cffd8cfb -size 62983 +oid sha256:840381f73976a7725c042b12fd5448b53a753a16596077d653b9270580836204 +size 62951 diff --git a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_de.png b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_de.png index e2e4faccd3..ce831bc77e 100644 --- a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_de.png +++ b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b196ba5482799666ee1bd9000de4c40895f7405cea38f9018eb45da3afc4c455 -size 30363 +oid sha256:2a9a168c06feb6bcb586ce156b563843836cb6f84830148768765743e2a2900b +size 30378 diff --git a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_de.png b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_de.png index a15d3951ed..aac6e44f3c 100644 --- a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_de.png +++ b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb7aa2178a4aaac88562276bb85879ac05bf57321460102c7b4598d69ed6388c -size 28540 +oid sha256:48816e601439853798dde46eca0d2110a6ca19c1edaaf5988c6f693967ec9ecb +size 28475 diff --git a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_de.png b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_de.png index 4d2146e323..66133fec71 100644 --- a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_de.png +++ b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39d9e350f2fff99f89123c5899230d29fdfee85208bd611c5fc313a5d1c69098 -size 43051 +oid sha256:4bca9e4a19fcc0bc5c3ddda89b702348c588169e3e8ac64c81c6d38dc953beae +size 42981 diff --git a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_de.png b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_de.png index e2e4faccd3..ce831bc77e 100644 --- a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_de.png +++ b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b196ba5482799666ee1bd9000de4c40895f7405cea38f9018eb45da3afc4c455 -size 30363 +oid sha256:2a9a168c06feb6bcb586ce156b563843836cb6f84830148768765743e2a2900b +size 30378 diff --git a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_de.png b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_de.png index 9a8abbfd94..fbf5934309 100644 --- a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_de.png +++ b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d3c850336f4ffd9bd3e9f7da36e1b6ce85d5a308687a66623a3be79569ce45c -size 28009 +oid sha256:a173d4f2c2746bb9f0d7961bea3480f629b6f2d0064471ba806efc722a60c5cd +size 27966 diff --git a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_de.png b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_de.png index cc535a7d40..1122ac3e83 100644 --- a/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_de.png +++ b/screenshots/de/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c28d3688d90167323b819b20ccdf27a1049f51750b66cfad162baa974e0c591e -size 29710 +oid sha256:cdc1f08bc0f6991666e26a45b5da4d403f772f6ad85fa4e98c5e0e1317e9fd08 +size 29743 diff --git a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_2_de.png b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_2_de.png index 669b21f21f..9b5ae486ba 100644 --- a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_2_de.png +++ b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:faca91ece572d2b5bcf6927500c07a54044923723142c47f64494dc2ee121bad -size 54789 +oid sha256:4fff4adcf33c04978f5c5246abd7b6abd4d25533734c0c4e487467bba5b0adce +size 55087 diff --git a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_4_de.png b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_4_de.png index edc41b1333..052a3f9307 100644 --- a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_4_de.png +++ b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bbbcaffd90916413fc12b06deac9a96e8dde72e4260d8aa318a4f11d445d04b -size 56023 +oid sha256:a4ea9b9c55b173df2edc373afea9bc64e746cfcfa55eac0b8698f2569d2bbd86 +size 56333 diff --git a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_de.png b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_de.png index bc19c86598..028dcdca8c 100644 --- a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_de.png +++ b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:877ffe68f9b3f03cf0f9f046dbf537ba284ae9deb12c6857ed31b8174728fd0f -size 37605 +oid sha256:56e45625b5fbe49b523dd13b2e16719b7f894e22914b11cc4712f778d27e8cb1 +size 35927 diff --git a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_de.png b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_de.png index 36dbf6b80a..d20e8ce18b 100644 --- a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_de.png +++ b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e46f6eeaabf4f20e9baac0c7c9468548c3368ffdac418eab9ba63710fcf29ab0 -size 47203 +oid sha256:6015c6bb424ff8949b3f71ee04f553360524dc71415131c455aa3bb95ce6e527 +size 47527 diff --git a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_de.png b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_de.png index 96fb67684d..86d231a48a 100644 --- a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_de.png +++ b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a97165e24d2d7e8aa3f66e63a21a510c87b6b530484779b58de684b62fc3417d -size 49588 +oid sha256:aff7164a2395afc794ea72b349dabc30dcc4813e76e00e5c67683bd3a0de4535 +size 49064 diff --git a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_de.png b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_de.png index 1345662657..48c295e856 100644 --- a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_de.png +++ b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b013298feee179a8533e62c9c8f526dee39cee0f523310fc69d134ee83ed2be0 -size 40249 +oid sha256:2199e0eba60570bc2b0aa878e53a0cf0f841ad4fbefb169706e0744162f28e68 +size 40426 diff --git a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_de.png b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_de.png index d7b3e00503..ffccdfc805 100644 --- a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_de.png +++ b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bbf2ca23feca4f9fd0d4ab60a58ff53bea0bc6328bdf520d7e637f6fa411ebd -size 42411 +oid sha256:d076b1d965d84086a075953bc8ba74d51caa5b0d9d6aba97549c343bb3f2c862 +size 42500 diff --git a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_de.png b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_de.png index 1345662657..48c295e856 100644 --- a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_de.png +++ b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b013298feee179a8533e62c9c8f526dee39cee0f523310fc69d134ee83ed2be0 -size 40249 +oid sha256:2199e0eba60570bc2b0aa878e53a0cf0f841ad4fbefb169706e0744162f28e68 +size 40426 diff --git a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_de.png b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_de.png index d7b3e00503..ffccdfc805 100644 --- a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_de.png +++ b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bbf2ca23feca4f9fd0d4ab60a58ff53bea0bc6328bdf520d7e637f6fa411ebd -size 42411 +oid sha256:d076b1d965d84086a075953bc8ba74d51caa5b0d9d6aba97549c343bb3f2c862 +size 42500 diff --git a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_de.png b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_de.png index 36dbf6b80a..d20e8ce18b 100644 --- a/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_de.png +++ b/screenshots/de/features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e46f6eeaabf4f20e9baac0c7c9468548c3368ffdac418eab9ba63710fcf29ab0 -size 47203 +oid sha256:6015c6bb424ff8949b3f71ee04f553360524dc71415131c455aa3bb95ce6e527 +size 47527 diff --git a/screenshots/de/features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_de.png b/screenshots/de/features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_de.png index d3beda541f..318e19f243 100644 --- a/screenshots/de/features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_de.png +++ b/screenshots/de/features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:922b3c93b9c7714fa0e1d174ac4cb609c1fb6ffa8c5302577639ceb4d043f588 -size 25919 +oid sha256:bf99d5c97f6e3bff5d477799cecbec34a5f7605044aaea58ffc2b144ea627362 +size 25338 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png index 99644efa4e..069b6328c4 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:363d934e082e4480345a2e1838870ae4ab92dec1d2bb0b903f4ab29e5a18d22a -size 46089 +oid sha256:ba514c4dfbd8021e99bd0460d79e81fe77e721f9965915f3a7bdd2606d408d21 +size 46036 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png index ad5ae8c3a1..4c53fd1469 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a09efd65da8dce165f383ee3c571d53ad15dece7028eb9e8eb094345795e6bf9 -size 44201 +oid sha256:d948a3a01384d31142e84ea0ed26087f1f920d5a2e113e5915bb44fa2dcf0393 +size 44174 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png index 83a9cb117a..01a18a53b6 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:220f22926eeaa4d31a923dafa081fe3983a311bf04e15856d045b4ddbf729da0 -size 43561 +oid sha256:b7edb0d3ff2b059a47a3b637e650a8b8bc585b0c915225cbf1f1a48bdeeceaa7 +size 43533 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png index 48b674bc28..31bcde7e66 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1466afc2fed907b70544cccc00f5fb83dbaeb9d69e2b7986c3e9691628341af1 -size 44536 +oid sha256:e177f922437c2286bb1a41f3ef249de856b15cb06fad5e4c699b3b00c08e7281 +size 44513 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png index 69ebc6840f..c1f7e782ff 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b065415e13e1bdf44a9c27ba4568e6eb0d62d1b770f1b7feba2f584de937c331 -size 44468 +oid sha256:df4a376baeaf0d3256dca08f24ace8246965b0113fb82800393629b289a3607e +size 44429 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png index 269cfaa786..c3b1d5b18f 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d626fda5fdf1243976def39beaa67831011ca74f430128879b24a6e1dbd44efa -size 45907 +oid sha256:45acc3c57908717786e0aebaac34cd15573d557d307587c46b76cae84c0ecacf +size 45857 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png index 1ea68f73ff..66e4da9dc2 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1edd626d24da35091f60dcd5136a27b2c4987e03d7ee6d4045bf001c4f12bcb4 -size 46268 +oid sha256:98b10b8e1c656ce5f769c68f35207aad1a5299049bd44e498c422aa92c2a569e +size 46211 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png index 657a684d80..b5e1ab3ee3 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef2477e8b1a2b73694a22e7a67e580f91bb40572b4f41ded6c3022d246e5d35f -size 44766 +oid sha256:afbe37e264793a76816a14345b1bea11d38f82ba89262f212638ed10dd60c224 +size 44726 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png index b48969d333..9d6e6ece2e 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d67193aad54d160409572884fb43eb96eb6ce150995dd587f700a1e5f73fc63 -size 44174 +oid sha256:37ee555a717f96077f7cd387eb78c51753736a98443df180276259e8e4294836 +size 44148 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png index e8868e88f5..0d71a02fe1 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94dc77f27194f5f34ac1948226738dd5b82da88be6e75a1888f19681c81df36a -size 42836 +oid sha256:00be669e84cfadeea0771ba5f46db49f3c065892e72a5aea1d513e97e24445ec +size 43033 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png index 85b576cd06..07a79d3970 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7c3a3647df677fbe63d0923c6b740f83fc5585a904c3b8e07c47e7ce7ffe4e4 -size 42837 +oid sha256:465b3f9868de3a79938dfbdadfe9b20620962c696edfbc350043bd348a6ac6c9 +size 43034 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png index 4e90a09ff7..d00a732c92 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db268adf22086200cc8ebf46850f0239e8b33e1a79d1c120753a6bbd935dea39 -size 40583 +oid sha256:5b0d5eed903a658de52b4cc814a91e4d348a7edbc843fb5133b6387d8ffa5391 +size 40319 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png index 9cb5a90004..e7859c7329 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e86a20df92ed28a90a8b3ba4fe1aa4bdd493bade93e6737a6f6b26430a56f5c -size 44681 +oid sha256:f4edaf24f04a83277a7786b09a461c1ea03965fec3e8d83bbb6a13e3de4e2343 +size 44654 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png index cca4cae93c..c15c299b83 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7366b272601654b4f5651cb82aaad1be08aca0cffd96a504aa857e1e4dcbe1aa -size 44629 +oid sha256:2669b19252a53cceb5fed1907457290680c943cba468402b177c479e3a1f70a2 +size 44604 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png index 13b0440eb3..737b62bfb6 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3dca96ed406e04375095792789c9e657c5d1f8e98b877c6e991d0cba70d2f33 -size 44273 +oid sha256:fc60fad97d616e0404f5c170240ecfabe372c489500b4812f389f5ba6627da70 +size 44240 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png index bb76416135..0969d6f86d 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:103056e52d308e044579a0e26e074f69fdadff1239ed10fbcde3088f39d26afd -size 38417 +oid sha256:c1299af5efc2fa7daee5e4915ba28042634957f8f6526634cf4806298446be53 +size 38391 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png index 7e2d491a37..b3cd2b3e80 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:678517d61e34de5732c3409563415fc1bc2ea8bc8571435d5adfd8cb4dd798d2 -size 34196 +oid sha256:4eb83c136bdae2dbf3af9e78648afb518635d08ad604b82dfc09f08550d5614e +size 34099 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png index 05dbd24049..778ffbdc32 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45cbf2e3e00fca851864b324f349f28e63a91c01d912cc2406fba054bc325f35 -size 43053 +oid sha256:f0966d42c8fc0b8d4d63064697e93a73fbfec85730601bd1241204e78e8eafa2 +size 42842 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png index 4c21775d11..79fea3dfda 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca1edb157db2e40136ccc898c55e88d26144f2d183030f485143d760ec1663bb -size 42543 +oid sha256:7a59d6ef1c96887a3ffe8b56cea792537f9c33cbd597cd3d457d1c9ef182ec7a +size 42741 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png index 5ecc77ae3d..0a5bb4b79f 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a93a0c0243537989282dd01b26246efc5fa73cc44096364a7429f962f9c83cb -size 35001 +oid sha256:545b403e1ee1465888a5cbd866c98c18ae93e51902a1b5ec0bc9729335c7d047 +size 34860 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png index cdf3a551aa..a674681994 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3a7897bdef6291441de8e06969f5c2781a52b5df1dcb7a0a78ca154183eca67 -size 45153 +oid sha256:a86639422de221671bd0be5b1031f5c1ec85970ad02c8d2c0e2cbdc9c39b4490 +size 45124 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png index 3f8a788b0e..62c1af9a02 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a4e562634358362eb15165f6c3b00a66e35e187e433442a06b8f5bfbf997a34 -size 44726 +oid sha256:cad0f8e62f227d2a032ed227221de95c99c26bf1c8a32e3a82741a8112492b82 +size 44664 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png index 4c432f0485..508a6e75df 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e171f751b3ac646550ed0d03c30af5c1a87c40bd61966109961d45e3560ea0c8 -size 43448 +oid sha256:0f15bf9f40cb44ee464a1d0060312c5ecd6bb3898ce5a60b6a13f2aeb5adb534 +size 43345 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png index bc26f4154f..d2468e41c1 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9a73947904a394eeef3844d70fb954d6afe99b475070e2ffd3f4d1871350fff -size 47776 +oid sha256:08b2f6d71831c493c74e66865322baefd50d1791882db120918e555a639232cc +size 47737 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png index fcead70d3d..247e0f9cb0 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9863915e647a1e379bd36a26118a27b008c91a8be834e0654c8f07f2397154b2 -size 45670 +oid sha256:a8279b6a22cc9d935c30234c1a6fefff606b87cb6ab7d01037e761781843e14f +size 45648 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png index ce77255472..e5aa91077f 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:390e616bd74652cd799f44c306979cabf8e597dfa345a12ef70b734d360301e4 -size 45110 +oid sha256:3c61a95a19778e2c5e254a0a9d1ad3dc2be4fab5d79103a0efc84dfc1d1ac490 +size 45090 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png index ac1f951a2b..36c9fbc7f5 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72f99b9e99f58972a241871c96b88b5a6b7c63e7936f312cc82a96e83b9df927 -size 46051 +oid sha256:0ea16f40539e10079db6451a9d47cce0182f4da8bc6017930c58b9889597ae4c +size 46033 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png index 04725afc93..267fd4aced 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aaf5e26cfae6c4151d88535e3306f763cb236662a348a93486dc8d27b3844db5 -size 45997 +oid sha256:a83a9de79d0fa761fee15dba77bb7b7ab40dab0bc113828df94ffb065ea76e81 +size 45968 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png index de3387c174..d7df1473b8 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc26686b6aa746ced27166c9a78c757351898a91440690dce39219338cd08387 -size 47558 +oid sha256:aac313fdc559f815fb529b93d367cba9e4c1e58cea78bec4e3c75656b0e841a5 +size 47508 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png index 611ac1df4c..3a0fca06f6 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3a7a3b94a0113f857083b257f5212080bea2ca6a8c45d81fa766d9be08159d4 -size 47988 +oid sha256:6b794b3aa1e1fee432477402d0ff2b2dd916a8a7d827322e5149e7adb7a4c52e +size 47938 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png index 6e9d79062c..86ed4378f2 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a2a4f87070ff794d5d9b2801b00a953a2293b8ac53306f600c8fce034bccf93 -size 46309 +oid sha256:cfb0eb1850b099fe3b491c27997a4e98f0555d0c3ede73e11d6ac566104c4471 +size 46272 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png index ebbeac3f02..732ea65e66 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:079136ab2f084134091d7b80e6fae5f3b4cc0bf7f15f77fba37ca2bf02f5368d -size 45831 +oid sha256:1a6fd9d1cafd1530a26db9788bacee2bcbd567e9bbe9137dbad21ba495f83fa4 +size 45809 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png index cadbd5e21b..24810a7596 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:728d64a173548cf520b424c58cf0782efa1c7059e232d01442cdeae872bc31fd -size 44529 +oid sha256:8c226ab8b02e6dea1ed76441c3df7c79f0b68e4adcf388821cb3932f6cdcf50f +size 44678 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png index 295a526d1c..719fb091e0 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a34deaf4c5e1fd72f16157623bba5a6196b198d7af8f4279810a2a530f249e73 -size 44486 +oid sha256:012433ee5c82ee0b7b3ae8840e5f19cd9b4ca0d0deebfee3aba2bc471ddf0133 +size 44637 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png index 28b72b85c4..efae5e5edc 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d1e0669e1bc2e8bf3ef3ad78bc5ad0d115c5ed190c324c97c50fb57c441b1c4 -size 42034 +oid sha256:a237709afc56123c56dffb25a105bc6794b967f1034e495be8740911f3ffba7b +size 41925 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png index b2b4a832b3..a64725d16d 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:670017dec6822b218b58b7ff1e9a77820bdbcb83ef8d016fdc86dc6d8424dcfb -size 46236 +oid sha256:983b2d0c9a8f6e850ec3121a8e28006fe46dde69a908a0d7af3753d94df14fd6 +size 46217 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png index b3f8165b4f..2582cc203f 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b01fe8e863b1291d80276cbc69e90942531120ed4a55e75200537974b9c10222 -size 46175 +oid sha256:858bec39d28122361e0d82e0438c86ab86e4c5adb3ac78c156239d4b7323b7be +size 46150 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png index 4ef7cdce2d..3d21f4c0ce 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da295c263d2e5c5ad03114a1187f2a3839ea094a4669d81798dc44b2a9d8c639 -size 45770 +oid sha256:fba77536ba3fe17c66970b7681b05fe707a3663a2dca645d6739c834b0e8e852 +size 45749 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png index 74ac704623..ec2899e3ce 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:287e77c80a50f52c8f6379cb307ec87cf0ae85814229ce0794ec4485260c8b12 -size 39846 +oid sha256:f57afc7719f7a08d9f088fcd316d2e41de02c808376fd83c08883a31449ebbd3 +size 39808 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png index f960fcc707..0f2dfc0cc0 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:44272f5f1893abacabb50b6094580b3897ca8612f06ef48064f1da455b1a1359 -size 35524 +oid sha256:3c68ac502f7b81ec309cee330899659fd7389eb9f7adfbc9705302967dd009b2 +size 35378 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png index 8587822a8a..3edaf18352 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65816f3925fecc5b5f5eac06bc706c3270347103d06ad16a3c9a61cbf6a55b1b -size 44616 +oid sha256:9c52c69c6b1cab90d39207cfd157fb4d6e47d2897617a8f2d92e761c232fd142 +size 44486 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png index 0169436da7..d8afefd19a 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b9f8b1f0b98267b2dba7918487ee7bfc432c93bb8cfab45280a014857529fcf -size 44188 +oid sha256:5dca8eb8ae6ba99e0d0d82277b61185d3e3ead401a45f85acd57a7670587b33c +size 44329 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png index fbbe4280ea..384429fdda 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a482b0a589b8e5fd0474e76e1292c607b3fd9232caa69ddfc4602f4eb29ba730 -size 36629 +oid sha256:823d49a0d3fe06617d0b6d59997b677d7075eda10de12020b47495089a46a3a0 +size 36486 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png index baae5fb1a5..2a63131ba9 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9db70150e020948a92cab255b0c1b923c11f0d7f6de99d8ddd262128e9b78f6f -size 46812 +oid sha256:d3da69b9890183adeee8d6ecb5e3a5d4b1e9a1efb0d067f74cc1f98f4aa033d7 +size 46790 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png index f0085f5849..37cba9a09d 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:116e23d25566ec356b2c4bae654217a1e5a38150f424f45fce91a5b4b9cc61c4 -size 46294 +oid sha256:e1ccc93391215a22c337573f42d506fbd6c626370247de41d7e10c3255b2a889 +size 46248 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png index a7bb6cef72..d6b68a2487 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0cffffdfe22cb238c6a667e4092450e27c53597f4296e35b732764e6f5b484b5 -size 44833 +oid sha256:8325e3adc4e146aec0ec2146bfe5552e500c7bf21055a5a1a024e0e00a3d9e20 +size 44787 diff --git a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_de.png b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_de.png index 9a73c54042..ae715f4343 100644 --- a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_de.png +++ b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dfe598362f7bd9dd246c73cc98ac5ed34fe77e94467de94509fa0cbea9ab571b -size 21313 +oid sha256:a9314b5a780197910c1c2d4b72962e3828c4c1077fb43661d8d6e02dfaa983c3 +size 21284 diff --git a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_de.png b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_de.png index 87471ed32e..fba34860b1 100644 --- a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_de.png +++ b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86dc7f757c0f38fee531f5cc857626f5ffd993b1ff7c8830a4cdbfba4c37e501 -size 24945 +oid sha256:e47768b705266b38727e1da24adb657ae59bc83763d5c5d079493eaece01336e +size 24984 diff --git a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_de.png b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_de.png index a2ac18ab71..4548e7e5b5 100644 --- a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_de.png +++ b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58a0fbc9a1cbd6bfdb5d53d06d971fb36c666e48c14920ac10403223d87b1f4a -size 30185 +oid sha256:21ced256aec88473254bbffb84ae0755635d47e098b91c3eea98f82c2579a6b4 +size 30272 diff --git a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_de.png b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_de.png index 1fbc980d1a..ad34606d90 100644 --- a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_de.png +++ b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ea689d5607d180e37a9acdccaf3653ce20002ae1108fb6742a0624b3eb05af5 -size 30666 +oid sha256:f31dbbcca3744e89c50544f111075db6d4eddee9fd98c07a7402bd71ae2c1838 +size 30613 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_0_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_0_de.png index f437eb6ae7..26faac1547 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_0_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9a36e73dae1d3829c3575f0490df31c502e9535faefba3c0f1f9d0ce71a3d22 -size 40449 +oid sha256:8350d0190731987501331f129f8b1b02b07f2f63470d0f1a453c6364b8501b56 +size 40502 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_10_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_10_de.png index f437eb6ae7..26faac1547 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_10_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9a36e73dae1d3829c3575f0490df31c502e9535faefba3c0f1f9d0ce71a3d22 -size 40449 +oid sha256:8350d0190731987501331f129f8b1b02b07f2f63470d0f1a453c6364b8501b56 +size 40502 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_11_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_11_de.png index 8ac3eaa191..93a222c930 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_11_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1851a2e559c42ec1508e3020d1b56c467b3098eeac4e47d22daae3591a711fff -size 41332 +oid sha256:34a3ae93e63eefc6fd5ababd7e8a25efe8b9e828e016bc8aedb693370332659a +size 41435 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_12_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_12_de.png index 8ac3eaa191..93a222c930 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_12_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1851a2e559c42ec1508e3020d1b56c467b3098eeac4e47d22daae3591a711fff -size 41332 +oid sha256:34a3ae93e63eefc6fd5ababd7e8a25efe8b9e828e016bc8aedb693370332659a +size 41435 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_13_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_13_de.png index 71b79e0b69..cf1faf2a64 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_13_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af8b41d7e76c06bce4ad9420c6f2876ed61fc3e4d558e3b3e089f3ca5603b459 -size 67837 +oid sha256:78af886eba5d06e4ba65763eb85ce220c52b9195350350709d36ef53fedfed5c +size 81003 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_14_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_14_de.png index fb8b09020a..0e8b5245a5 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_14_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:923386ffe1a9737066c590a1d666439942cdc4177831a22e615633334503811d -size 73363 +oid sha256:d5bbbe7c705f5e5bb7a945ecabdd1a5bc4e2761e3b3936d0256bdd78b092b8cf +size 82016 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_15_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_15_de.png index 804b76f295..a1e745962d 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_15_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80c069fb357ca634c17251356dfed57884f7b50490a027d6f6254fb1c5b68330 -size 54504 +oid sha256:2fb93478a18d91579cac40e644653a01aa6bf41b19f0f91ee325965dfe1cf6d5 +size 54450 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_16_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_16_de.png index 7b474bc71d..273193b4ce 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_16_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd3eee57a5934460aa6cb061a32bda34b96146b230e874122e17e473e6a49ebc -size 65886 +oid sha256:b9abf285e97cdceec4e7d72313a2ef36ead0c2d9d89063a34cda103757088b89 +size 77878 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_17_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_17_de.png index acc9ca2eae..cbadaffb7a 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_17_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d00a9f39c51db7e3bcb3230b29d6b4b86cd2c538057f4a2d471e463eae7ecd8a -size 51204 +oid sha256:a5f5222b1a8c8caf1517699b6bd77ecd50247041c4d360779165367d70dc4153 +size 51508 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_1_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_1_de.png index 8ac3eaa191..93a222c930 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_1_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1851a2e559c42ec1508e3020d1b56c467b3098eeac4e47d22daae3591a711fff -size 41332 +oid sha256:34a3ae93e63eefc6fd5ababd7e8a25efe8b9e828e016bc8aedb693370332659a +size 41435 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_2_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_2_de.png index 5b59fc55b3..4ca821b0e5 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_2_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4538e4994ff46b67cfc376469f152b2c8725131ba555328c8fd2b8233018fbc -size 41721 +oid sha256:253dc79e72869e75e0cf128ef4a8858b7c5a168bc7f987a9c375ac69d05bd327 +size 41751 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png index 6c964087de..42182f953b 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b9bab52cbb4226177052e55217ba518706af204f4550ae9d1986d05d0cc401b -size 42318 +oid sha256:f5e50c340cb4255ff7975682cb63dbd03d39b2fe353c683d5d3d068e66e643e8 +size 42279 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_4_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_4_de.png index f437eb6ae7..26faac1547 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_4_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9a36e73dae1d3829c3575f0490df31c502e9535faefba3c0f1f9d0ce71a3d22 -size 40449 +oid sha256:8350d0190731987501331f129f8b1b02b07f2f63470d0f1a453c6364b8501b56 +size 40502 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_5_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_5_de.png index 8ac3eaa191..93a222c930 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_5_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1851a2e559c42ec1508e3020d1b56c467b3098eeac4e47d22daae3591a711fff -size 41332 +oid sha256:34a3ae93e63eefc6fd5ababd7e8a25efe8b9e828e016bc8aedb693370332659a +size 41435 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_6_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_6_de.png index ef70e07494..2b56ea9058 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_6_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f8a1e425850742521751919d5f0df63f27035c9a7d7bf7d442cda12dcfe451f7 -size 39619 +oid sha256:792c80db8a790454364d0246eb2b75098db26f585d8b149880fcd867233695e3 +size 39614 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_7_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_7_de.png index 8ac3eaa191..93a222c930 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_7_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1851a2e559c42ec1508e3020d1b56c467b3098eeac4e47d22daae3591a711fff -size 41332 +oid sha256:34a3ae93e63eefc6fd5ababd7e8a25efe8b9e828e016bc8aedb693370332659a +size 41435 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_8_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_8_de.png index 8ac3eaa191..93a222c930 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_8_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1851a2e559c42ec1508e3020d1b56c467b3098eeac4e47d22daae3591a711fff -size 41332 +oid sha256:34a3ae93e63eefc6fd5ababd7e8a25efe8b9e828e016bc8aedb693370332659a +size 41435 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_9_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_9_de.png index 8ac3eaa191..93a222c930 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_9_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1851a2e559c42ec1508e3020d1b56c467b3098eeac4e47d22daae3591a711fff -size 41332 +oid sha256:34a3ae93e63eefc6fd5ababd7e8a25efe8b9e828e016bc8aedb693370332659a +size 41435 diff --git a/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_de.png b/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_de.png index e1222adfe8..a8860247d4 100644 --- a/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_de.png +++ b/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7302bf9b807352443aa7a329eae777cbb1379fd25be381190be8a95c5f6237a2 -size 52223 +oid sha256:2f4cddd8b96bf7fb58058d22be32243af36875769332f6d3761748830131e306 +size 51925 diff --git a/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_de.png b/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_de.png index e1222adfe8..670657e064 100644 --- a/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_de.png +++ b/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7302bf9b807352443aa7a329eae777cbb1379fd25be381190be8a95c5f6237a2 -size 52223 +oid sha256:18eeb2fe8afe50b79c9cb25258a7824889a9d571f2da9b0df539d6bca0ed63f9 +size 51944 diff --git a/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_de.png b/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_de.png index d47f160de8..90c9723377 100644 --- a/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_de.png +++ b/screenshots/de/features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab7060ba6c4b0e6b216ea4c7bf3d70634470569b6628a39785973bda3d07a190 -size 52800 +oid sha256:11baf26ecc76edb1e79e951bb47f6f6a0109c1519144b4280d9738eeb20df16a +size 52381 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_de.png index 451767477e..6da6258d67 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f801b53ef63e094205d2a2bd14ffb0d34713de8423e154bd637134b30f224ba0 -size 48168 +oid sha256:637d9560af680d4ae4f268224a43349f8f78ccd2e4d6495b795553c1248d0447 +size 47917 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_de.png index a79c612d61..1095d4ca91 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f49b5dc0549880fbc87aa820452e6d87957eea0ea7126b23ae223106c277921e -size 21238 +oid sha256:5df518958ed6cf464b2d1b118a92f75257af9ab6f449e92f0a76d6a765f0fb91 +size 21183 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_de.png index e21b128e61..ea7a323213 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d66c6807cf3caa32a72031890338e7cb0a7a6701bf82b9ea6416b9e1b042389 -size 45620 +oid sha256:6eb10412b05ad5a5accdf5a623d963a86284379940b6de342f9125d568b6831b +size 45546 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_de.png index 009fdfdd0e..d7542ee147 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:612b72d9a7a659bb56cf46a0f5b71c735b4c3b37d96e6d8efedb26bd7a2db6e4 -size 45394 +oid sha256:d8c149bec4e158206d6d1f1027bc338c892d999c09b4e152b8770c847620d3ff +size 45331 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_de.png index df481e48ef..0081e61a7e 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c67ea324950af630af0177f01dc380a6c3cb58cb7d99b520ade9a6cb1880797 -size 45618 +oid sha256:9b003b7a6e39471f84fd3658c73539ab715829cf6e55dc9556cf3d6bb7806d65 +size 45536 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_de.png index 58d6c1f136..2b24ad2624 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ba6580e7b5cf07c6dbd067ec863dcb485a3fbc7d03d93dbffd8a6c2d337b058 -size 29129 +oid sha256:b069fe95cba038946af8e652a679791691412146b580c0c58a9f263d6069c65d +size 29072 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_de.png index 2dd817a0d9..85ec4b1781 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:054a9b89b6c4007b839e53190b10938a7dc5947badad88876be243e1386628a3 -size 61787 +oid sha256:5dc96db23cdf3b061ade2d79545bc3e76fb6784edaa7c4d1e42aec47b36f72fc +size 61412 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_de.png index 2dd817a0d9..85ec4b1781 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:054a9b89b6c4007b839e53190b10938a7dc5947badad88876be243e1386628a3 -size 61787 +oid sha256:5dc96db23cdf3b061ade2d79545bc3e76fb6784edaa7c4d1e42aec47b36f72fc +size 61412 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_de.png index 02381a52d6..b9b912e988 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:433526f6087135f74598a2dda52402f40e7c702d649a69c4ac1d50b5789821cf -size 45316 +oid sha256:6a06540cceb38f4a2156e57ffa12ac7cfb56266713271cce8ac10663a9cccdae +size 45273 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_de.png index df481e48ef..0081e61a7e 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c67ea324950af630af0177f01dc380a6c3cb58cb7d99b520ade9a6cb1880797 -size 45618 +oid sha256:9b003b7a6e39471f84fd3658c73539ab715829cf6e55dc9556cf3d6bb7806d65 +size 45536 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_de.png index 5c3ced3a8b..0dfb02fd69 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d87ccdfb3a61c27f2ac6d32edef2dccd69ce7e1278146bcded4a465ac54d555 -size 46058 +oid sha256:20d746201009e620e1fbe2497378dff95019756e626db0a65633ed7af4de5c5d +size 46218 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_de.png index 27a3cf0906..889ac3e0b5 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e4d92643de93845369958c0e907f695bd7c455a54bd190a709327c2dfdee46c -size 62166 +oid sha256:bc3fc11302783cd6d6f1728bd9ef4c631366b3341c7855c90db9929e597ca5a3 +size 62596 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_de.png index 83fc56d27d..b002491c77 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:653f931a8199e77404400c5702aa376e1c7c5ff90412462c4c96094a4f2c2639 -size 42129 +oid sha256:38bda8f91446f052727f7379f229835e9101635efbf375e70423814785052423 +size 42123 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_de.png index 382cb02e84..2d271b76ad 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b00b7596cdea420309fa5339ff6e13cea872d726600c528afdac404b333a20c4 -size 35172 +oid sha256:8337c500f365103ef972c75d7a610b7a01ceb0ae3a562693f4928ba3ff8cd6a9 +size 35076 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_de.png index 7e52cca8f7..f18b61f1a9 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:def3e89ef597a3ea18892509d52474cabdd0cb6c5ee68f5a6e7b511f7c7a7f51 -size 42470 +oid sha256:3e0973da1013a20d3460fcea48d01759c38e8bfd932df2cb56346b3cf35b8af3 +size 42566 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_de.png index ec57a137e2..3adc9c225e 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ab9a8241a87a44062627bfdbd64dd9cc717dbe3bc26e31b4e6d0073a33d6a53 -size 46115 +oid sha256:68f8a51f2d342e2d01eff3038da83e02b5bb87774a6a78c0db2e35cf61cc6183 +size 45480 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_de.png index 44dda9bafe..43c7ddbf0b 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81a128b88a2358fcc226c1bed7b0930abf2ca5bb1dea374863c369650af81a9d -size 61950 +oid sha256:23a613764c13a4a2cb2eb41d0644482706e2dd30d0661e763cbc8adc3bdaf6a3 +size 62385 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_de.png index d27dfb803f..2f1098c35f 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:966f35fdf7e9a397b7bd511eb96c8bf0a53ea0d3f3b27d525b46e3a8a3115827 -size 62289 +oid sha256:4b0c79982009d7a795871568c0834dec205a52c0de9235beb8711117c4bb73a4 +size 62714 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_de.png index cc0df3adc9..ff0b9d19a9 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ffea6362670777720ad844d424ca9fa8ca9a3a79f26adbdaad1b28adbfe18d6 -size 56453 +oid sha256:ec674e4091d988c3f1b64b9a771481442e93672821804bf051c12c0ce4e1e777 +size 56181 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_de.png index 84155f40e1..8154cf180f 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:323f616c91310b48da0170fddb7c7ad68edff7ffda48e933cf20e9728b8057b1 -size 63607 +oid sha256:0defb9c6158b54cf1ec5c369e3c005b8da50a314e12294dfeaa303bfd51bd5cf +size 66133 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_de.png index 84155f40e1..8154cf180f 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:323f616c91310b48da0170fddb7c7ad68edff7ffda48e933cf20e9728b8057b1 -size 63607 +oid sha256:0defb9c6158b54cf1ec5c369e3c005b8da50a314e12294dfeaa303bfd51bd5cf +size 66133 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_de.png index 3fb7989c82..c1ff0f2843 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54237c37a64a8dd3e5abf055eab60564b925f37f14865e5c2275e4ba320e50fd -size 63948 +oid sha256:49426bbfd1b53af2e578c2806f9ef5ac54313fff28f341d906c39d551a074c70 +size 63889 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_de.png index 9e25ce145f..da54bf15b2 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48a06b067de4eac0ac8974548ac080d82665c60de2d47e9632b9ecf458ad47ea -size 62164 +oid sha256:8f7cdc3803d196f1d381f051a0d5760cb128d82ab6364b20156d8af685c110ff +size 62588 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_de.png index 3edc57dada..e163e4dfea 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f418c822d8c6ef7849ea741baa616a9adc20d89aa1a1fa0495625ed201ef10b4 -size 59663 +oid sha256:b46847466b2a9737509ec3545026afe67534ef09d394e80605e6198986dd898c +size 61850 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_de.png index 2ce9257340..4c36fabda6 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c25d7c8ca055f154900a450d5d59e9dfb95f8db08a05898b1fd81e1f4732dbf3 -size 49984 +oid sha256:65cbe6b8edda04bc81eda34d1ee6359e4167e1de5710986d8a6ea75d4891a242 +size 49869 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_de.png index 1bfae9870a..24f4cd543c 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf7859f4e20dadd55a116a05c50eade5bf98f56a1970a2eb42189f9367897716 -size 21954 +oid sha256:e4d6712068b5733a0eca9bbd8abf8e3961b5b39172eef221d70f03677764d9c0 +size 21937 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_de.png index 82a39d8cf9..576238a1ad 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:91bc0a4cfc1b1fb1b22ecb7309f13b25221b15935927c32d8fcd5fb42c2baa1c -size 47292 +oid sha256:53c4fbe93fb932fca54e8b31d3a7e6e6495f95c5374e061ae26440200bebd176 +size 47274 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_de.png index 5c293e8407..998cfbcb26 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:649525f5b719f7a4c575acec4fab99f8419368df6a46d798727d43f39c32e221 -size 47059 +oid sha256:4eb9eb0fec860eb5600513c629d340633c70e8dc57d35e6c39ee9940c9991f9d +size 47040 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_de.png index 9094e4ad40..31d49b5e3a 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11fcce969632e7f58bcd549cc54444484839298fb3c41b97d44e5b28531e5e16 -size 47291 +oid sha256:99473fc08bea3c859b4035d6783f2e8e189b3e0c037dcdaa11cc09c401201801 +size 47265 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_de.png index fbd2710432..45ee7600c6 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6e6da70984ea50474cf4f12c78ca8a4df196e92bdb77bb9ccebb4e00ff986cc -size 30460 +oid sha256:591b5155a9821f1aa7704be0e35955804a2eb74a765b3abea8cb7b566afef128 +size 30429 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_de.png index d016666d26..94d1ea6152 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3c2270c8ff1643956b0ab4fcd066dd4509b0fa175ae8652e83bde5c1c3aaa33 -size 64089 +oid sha256:04849a1c2ae4556865651f873cff2301d3bad14230a75260c2bf08f52efa258b +size 63751 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_de.png index d016666d26..94d1ea6152 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3c2270c8ff1643956b0ab4fcd066dd4509b0fa175ae8652e83bde5c1c3aaa33 -size 64089 +oid sha256:04849a1c2ae4556865651f873cff2301d3bad14230a75260c2bf08f52efa258b +size 63751 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_de.png index 25d9838f7d..63d824849a 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4eaa6cbe95b9ae032f7bdb232e10144461d2c2e1bacfb9286a3bf6df04467f17 -size 46919 +oid sha256:ead0136244274323f98b6bb3718126386fdc85e23d88d05a1040264f95b09670 +size 46931 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_de.png index 9094e4ad40..31d49b5e3a 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11fcce969632e7f58bcd549cc54444484839298fb3c41b97d44e5b28531e5e16 -size 47291 +oid sha256:99473fc08bea3c859b4035d6783f2e8e189b3e0c037dcdaa11cc09c401201801 +size 47265 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_de.png index 62f29c7298..a7aebaf3ae 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6e6dcbc8987d635fcd467f2342ab18e20c941399003e331b935c5b0cc929cc8 -size 47840 +oid sha256:e8bbd62146eaedcc9308efaeea85190faa96af1e54e858f7bf2997d7ee87b92c +size 48022 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_de.png index be51b6cc84..181a38d116 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c6d8b62dbcb54e6daa0e1884b0679dfe792e2a5be7e15e243cfee218a647f18 -size 64675 +oid sha256:9b3e6b4bb3a2d4fdb1bc80340fc887de10983cfd1b4532452b827346b2db2756 +size 65174 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_de.png index 9e8cccdf16..6ef4dd11eb 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07b165ca1e0b14bf28df019883ee0a7270c2afef5d1f3ef1fc998bf00aac806d -size 43438 +oid sha256:120097dee41e655efd90f989b7979db1b5fba7f3884f442236e6c4c67c23c1be +size 43447 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_de.png index 50d32aae2d..5c624fbdae 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3e59af810c53a46013d1c3ef18a5f4bbe6724d71a187534fbfa62ac50606e95 -size 36771 +oid sha256:f0d9dd44411525c32bcba2646f8dfadaffab24a6a38c054d122228c33405e7f1 +size 36625 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_de.png index 42921bf2cf..cccb47dd1c 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3eab325ed25440652684c615d7e2915224440cb19d83e1ee10d3c22c907130cc -size 44403 +oid sha256:86b15f27f85210dd4efb076a98c92fe4cd1beec920afa6cbbaaf34c4ae3dd0b2 +size 44497 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_de.png index 53d6a86bc3..78cf2ad447 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e2d982d32e4003849770d0870bf3ade6cc749b6f1f43c9b9aa4239c3aaa1cfb -size 48126 +oid sha256:4967187e4e8d74a66e0eda3c5b5b2bc32be4277893eb0641ad969f2c66794dd6 +size 47464 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_de.png index 9c16b3f138..9b1f8f46de 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ee817d1a2e7cfaf23f5822236df144a96d88499bb575332a02488d040b537e7 -size 64437 +oid sha256:6230606f11baea84afb1df2b3f94a2c57070bca925fbf198a9ac1954b5cf3707 +size 64948 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_de.png index e8f9e0bd07..04a70d4419 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b22d6149dc85db5ef1a23f057f2730e774c877d88c63f1a6f6543e2bf2d23824 -size 64900 +oid sha256:b622c14112cacbbb185a49422001794f4291f542e5eb16c8a64a3313bb395d24 +size 65361 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_de.png index f434d2afca..8fea8a2bf9 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16423874fda81c259bc18949f550ccd0cd2f56aff89304fda784d51d02e175b4 -size 59036 +oid sha256:14311e79e30782c643fcf3ac1f998ec25b4fc91503d37dd21ee462530b08fa67 +size 58889 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_de.png index eab0ec0568..03ce159627 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7fb8ec382a876de47b2c1fb89de638bd1b6fd44e088e7fed4eef09c59f8eade -size 66082 +oid sha256:3e846329a72ab30ed91bcfe8e8b5b6344382b345a2b2389f0f52664ae48df126 +size 68901 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_de.png index eab0ec0568..03ce159627 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7fb8ec382a876de47b2c1fb89de638bd1b6fd44e088e7fed4eef09c59f8eade -size 66082 +oid sha256:3e846329a72ab30ed91bcfe8e8b5b6344382b345a2b2389f0f52664ae48df126 +size 68901 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_de.png index 9f66cfd421..18cf10fc2b 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7be3f4446a9f517d44fd79ebd66e6e3b4120fb8a4d49a53bad50fb572ee95dc5 -size 66399 +oid sha256:9c52f0f9723bf4e149de296f0b3865424652771cc2ecbb4f836df0cf0e8be867 +size 66432 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_de.png index 4aa5fb688a..d6f40adad4 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:118c3107868e72a2f2516d1d5cbd2caa54be32b1e7bfcad89005ad4df0e358d2 -size 64673 +oid sha256:ca2978d58ebf70aa85c5dbe9e3844d288d15f1e4b1922eb729f67c1bcb8e6c27 +size 65166 diff --git a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_de.png b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_de.png index 0664368733..e01c9afa54 100644 --- a/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_de.png +++ b/screenshots/de/features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ffb2c51bf84b4b25e9008b21f66d7af2f1a691054d34e7bf601c6b8fc7adac1 -size 62165 +oid sha256:2d62bc658f3001474cd03828b768737bb6ab3ca0f2a90655c477cf5fadfbfd0e +size 64444 diff --git a/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_0_de.png b/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_0_de.png index 1ffc68233a..9c9e634317 100644 --- a/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_0_de.png +++ b/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6b99e3e1da0674aa6aaac2133cd473ed3b8db9b6ba69bff1e64956d83d68515 -size 43418 +oid sha256:9d32df10d3172e8febaaf99687d59744288adaf553fed442b4ebf49ad640bb8a +size 43349 diff --git a/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_2_de.png b/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_2_de.png index cebf2793bb..3144ffcc2d 100644 --- a/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_2_de.png +++ b/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f5127cb965a410cc16bdbde24888ccbb1a7e3de74bc39a76261151616697192 -size 23795 +oid sha256:531fbeeb983e0cb8dc7855dd41903352c70383a37bed48a8354c2462cf127aa4 +size 23780 diff --git a/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_4_de.png b/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_4_de.png index 0b0daa881e..0d4d600b45 100644 --- a/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_4_de.png +++ b/screenshots/de/features.space.impl.addroom_AddRoomToSpaceView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea62f1044cc3b7ceaef32b5ffec837437f1275e65a3823147427286b3728cdeb -size 47776 +oid sha256:f21bba3c135b528923f08a141f6848a832dff955bc4553ceaa4380ed6e4ad93b +size 47852 diff --git a/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_0_de.png b/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_0_de.png index e77553c02d..bf4c12626b 100644 --- a/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_0_de.png +++ b/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:867cddfa670bb04a5db6928710d438befa512a26c4cd2d3d71744fc8a6b629e6 -size 23576 +oid sha256:ce889c3b2d66de6de72f736cae49a4f91d228298dc108e04f248ee192fab1a25 +size 23572 diff --git a/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_1_de.png b/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_1_de.png index 160e7704ec..6dfa73be1e 100644 --- a/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_1_de.png +++ b/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86b689b81d1043afbf10df9a9ed9e181ea97681795ef59397d6106f429f960f7 -size 20071 +oid sha256:a2ebf56a3b46c79fb0032e9e4f2453d5de726327e7f0c3d44d3e8a82e8aa168a +size 20074 diff --git a/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_2_de.png b/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_2_de.png index 899c718a96..f34b00d36f 100644 --- a/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_2_de.png +++ b/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b6e5a3b33f21c5028c15064c031c05ae0d43da0779ca5f77a73f858e2a76370 -size 28343 +oid sha256:08d11a644181cc1988cefe6da3e75aac4fbee573771abd03a71b31b28c833872 +size 28514 diff --git a/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_3_de.png b/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_3_de.png index 705098ae3d..0a1bdf9207 100644 --- a/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_3_de.png +++ b/screenshots/de/features.space.impl.settings_SpaceSettingsView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71a2130fdf175df319ac8350ab5abb4ab019a9f6d0036c61c1eedd4cbec41e7f -size 28836 +oid sha256:a60279b894158cd7b9e7bd3337f193632313a660532c6d70a4be21965acdb7b2 +size 28825 diff --git a/screenshots/de/features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_de.png b/screenshots/de/features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_de.png index f4b4a06f1a..ba33af4593 100644 --- a/screenshots/de/features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_de.png +++ b/screenshots/de/features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71af790a7b1972930267c26e705cf657aa57e5dcf2548c973a3115aa001162de -size 18136 +oid sha256:51843c1bf3dda9c4d8f2a8ced042d86f251558eb77efd535e5134e5ef49f85e4 +size 21615 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png index de068ab838..7d2d5e34f7 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:502e23453f027c8a8e18f9fc50fb5b596cb76c7774b18dc2544fc595bd4dee44 -size 25319 +oid sha256:e619c12c0848a9bd492a6ac5e880ab3b9ef50bc78226a8b40ba41ea5636230d3 +size 25311 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png index 9ee694d882..a4eb014d8f 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc85789059bf1bbd1a5c6b2079bf306fc3929ca1f229e1caca9ab27798b0ed20 -size 23039 +oid sha256:f90280859ada6d3204a4d186bb9c821d49727f19cba0ea699b5a08249fcc1e8c +size 23052 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_2_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_2_de.png index 72efdb5c37..44f8fcad8a 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_2_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54696ddbbe7ab2f428e0d4ea0748f0ede19eb399629edfc98b0918630dadbadb -size 25383 +oid sha256:a486841ed794f0025af61c3228e4fdf93d1380c8b1d9d3f76d93b1880ebcdb5c +size 25289 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_3_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_3_de.png index c1f6f71e64..9a6b504177 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_3_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a37972f22a3a7b3bfd5ea412ee564c5cd8f748dea0e1430bddc09d74132fd08 -size 43226 +oid sha256:01f0e859afbfc0a87cb9b4d3a5a592fc81de640d0b25af79f50ae6ca19a48685 +size 43200 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_4_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_4_de.png index da387221dc..eb6f8e940e 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_4_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6dee381ca12861805ab1839d6832299283415b113af9269e4f26d7eb9343cb2b -size 38945 +oid sha256:23a9ada37a77ac4d9ebb57fdd82a3e83eed150e35a1796a32d5bb60ca7188dd9 +size 38909 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_5_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_5_de.png index 4486c3ea11..228b618c9f 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_5_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f18660504a7a2fe385f2fd261b3c61ded83a8e6afac834821aeb47c54524e7f8 -size 23356 +oid sha256:bbc6957c70959c3af610e2c30b4a2b5d632a12b4f95cd93865306ea974e55607 +size 23292 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png index 61f5ac6ca4..b44bf147b9 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b581cdf88386d4c375cb7883a09bdb8072133ed7047a73377f98f05d040443d3 -size 24954 +oid sha256:8ea5ec2c5906c1422814694c681308bb4b5fd7da1f947ce47e0979a9baa66795 +size 24926 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png index b0478fe610..e0be0c5bc4 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d0c4cc915f588e0b08dd3d51aae1d30ec97567bf44309a227cc9ff4b247e0a8 -size 28150 +oid sha256:8b4ea9667662e3d02d68b95faab737f5730a6a41ec53730c12ccd1ce96ac5cc2 +size 28138 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_9_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_9_de.png index 9182b7c53a..36199d212c 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_9_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5effc46b5c1f11c7bd490362b34e29275a9611f87cced34dd12d34c062a224b -size 34517 +oid sha256:1d4850add6183c31991ef98abe97ec26e5076a22be9b0526f4dc917e7ece3c57 +size 34465 diff --git a/screenshots/de/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_de.png b/screenshots/de/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_de.png index 6515e1941b..63e19fd906 100644 --- a/screenshots/de/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_de.png +++ b/screenshots/de/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a1b49fdd3b81f6d54a46418077c056cd800fc302b728a2990ef5d4d3d5346a8 -size 14087 +oid sha256:5e6ac16a6c43a8026a77e1ae885ad014defea7bbdb5f3abdf751e753af27d982 +size 14095 diff --git a/screenshots/de/libraries.matrix.ui.components_CheckableUnresolvedUserRow_de.png b/screenshots/de/libraries.matrix.ui.components_CheckableUnresolvedUserRow_de.png index b016c4441b..e8fb83d8d5 100644 --- a/screenshots/de/libraries.matrix.ui.components_CheckableUnresolvedUserRow_de.png +++ b/screenshots/de/libraries.matrix.ui.components_CheckableUnresolvedUserRow_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa04bafc9c372c51d65404c7ac1f46f291c2b16dc41ac57ae38984ad3d5e122d -size 108049 +oid sha256:7a2d0ed77165767c36caf9c57842fdeaa0009751b4a8427af571be606d25071e +size 108070 diff --git a/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_de.png b/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_de.png index 6d22e26217..9db67335b6 100644 --- a/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_de.png +++ b/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f313eb3ee7bee0318926978f3afc04770ace66afceb46642815639a81b56ab91 -size 38713 +oid sha256:cda15470280e1b0cf7a4be23e1bf56b6001fa99b6f6ae1a70bc87ddf93d56cc4 +size 40454 diff --git a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png index 65b515536b..c7d86829d8 100644 --- a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa0b269eebc66e7eabe746ed5ef0084d3bffad2917b68b923c115bbe8fa27ac2 -size 46267 +oid sha256:751d7e1f372531507a002d81dc1b2f19006d9554a47763081be8fda088941f5c +size 46213 diff --git a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_de.png b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_de.png index 334f0f6adf..8e9890bf1a 100644 --- a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d554025c1de34290ba025e749aa9a545b856dc82861741c5ca0376c3a30fa09a -size 44157 +oid sha256:d9872d332ef0fbf0252794c65738bf57b1cfb85bef381b1815348b693d2646ee +size 44035 diff --git a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_de.png b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_de.png index d002c262a0..ca691b23ff 100644 --- a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:387e0c1758216fe412a0cd17e452841c972e9ddc02abd460104b034c54b15780 -size 51554 +oid sha256:8911f676e98e041a2579fff0c9a3b3b8678dc54ecb16703b73ad5fed9c70a858 +size 51495 diff --git a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_de.png b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_de.png index d4340ee6a1..168340791d 100644 --- a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0174293973cb765e2ba4d2c638ada5d59c94e406b4c2994dcc693a5787b1a821 -size 45643 +oid sha256:d5d2051b195dae39090ce2e93c7d0266c5c832e99d6fb24c3363dd8d42036cf9 +size 45629 diff --git a/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png b/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png index 65b515536b..c7d86829d8 100644 --- a/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa0b269eebc66e7eabe746ed5ef0084d3bffad2917b68b923c115bbe8fa27ac2 -size 46267 +oid sha256:751d7e1f372531507a002d81dc1b2f19006d9554a47763081be8fda088941f5c +size 46213 diff --git a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png index e9ea5765e7..a6f7f1f069 100644 --- a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86eb21eb729bda52ab36c8320e78a1ec8e9732edaf285c1c978614bb74a4537d -size 45015 +oid sha256:7a48cda9f340bdf7c4c73737aa8b5f389ea481deac6cd968eaa70c29ed7863ba +size 45047 diff --git a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_de.png b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_de.png index 235329d2a6..b5a57f5167 100644 --- a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_de.png +++ b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9871fb8e1fa244d955913ee1e52d71e3e9ac2759975978d6c1eae2c1a09dc024 -size 34996 +oid sha256:eb616d791f91ad98f851ed912b36133f6d8e5b1b57a43597294683ffeb3404c9 +size 35122 diff --git a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_de.png b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_de.png index de028007db..abd8378cd1 100644 --- a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_de.png +++ b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf80faea335473ae7d5557b16c99eb890bbde9ec8c2a593fe45a9159e60713d0 -size 20089 +oid sha256:bf8ae170ddd028f6286a632ab906d9cebf24844c14ef5d883efe1f839faa7187 +size 20080 diff --git a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_de.png b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_de.png index 0dcf0a7933..3cac45382c 100644 --- a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_de.png +++ b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3b3c3305b00cc2f54295e3b31c78d8677ec937820ca4b95d74265c8a65985a1 -size 25460 +oid sha256:9305a88bf9a9c72ae989606383e84cca94aa6e1e202a7266716dce6e50f31e61 +size 25415 diff --git a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_de.png b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_de.png index 4ddd7b6689..d932a92871 100644 --- a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_de.png +++ b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0be503b03411de7f5c35364c91ed3668094cb0724032c2fd3be934f0371f879a -size 32520 +oid sha256:e56f8a6c31a63628727c34f87adfed83388ccacd85d9a6f4d6b0ee786ed739af +size 32488 diff --git a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_de.png b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_de.png index 47fe5f6c8e..efd707bcb1 100644 --- a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_de.png +++ b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc66e580215e2da95a5959b92e085c7c1164f296614f3f5eab7e8088e5721b6a -size 25269 +oid sha256:26fe5c0aa80fa771523efe79eda5aba18264a86b2e7c3324481d8a95d417050a +size 25271 diff --git a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_de.png b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_de.png index 953ab5d147..390d000ab4 100644 --- a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_de.png +++ b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:990cfdbb7a8ffbb61265bb2aa6aa16eee3820110cc30ce48a33652da4e046da4 -size 41025 +oid sha256:1ea0b6844787dbfc5c8fe990c015699c1213d8549b9bf89f61fda2a0475ff494 +size 41225 diff --git a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_de.png b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_de.png index 6ad2703eee..15283f75e9 100644 --- a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_de.png +++ b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac52b8099b919ed8305a1b00d2ef1886717feb482fed0a04bebde91630770269 -size 26243 +oid sha256:5d111f517f9a27d39eaca73523388c5a739c0e592ead232f9820be0d33871184 +size 26201 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index d9e43a5274..62cea77dcf 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,109 +1,109 @@ // Generated file, do not edit export const screenshots = [ ["en","en-dark","de",], -["features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en",20658,], -["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20658,], +["features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en",20675,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20675,], ["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_0_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20658,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20658,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20658,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20658,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20658,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20658,], -["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20658,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20658,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20658,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20658,], -["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20658,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20675,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20675,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20675,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20675,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20675,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20675,], +["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20675,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20675,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20675,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20675,], +["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20675,], ["features.login.impl.accountprovider_AccountProviderView_Day_0_en","features.login.impl.accountprovider_AccountProviderView_Night_0_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_1_en","features.login.impl.accountprovider_AccountProviderView_Night_1_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_2_en","features.login.impl.accountprovider_AccountProviderView_Night_2_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_3_en","features.login.impl.accountprovider_AccountProviderView_Night_3_en",0,], -["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20658,], -["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20658,], +["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20675,], +["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20675,], ["features.messages.impl.actionlist_ActionListViewContent_Day_0_en","features.messages.impl.actionlist_ActionListViewContent_Night_0_en",0,], -["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20658,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20658,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20658,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20675,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20675,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20675,], ["features.messages.impl.actionlist_ActionListViewContent_Day_1_en","features.messages.impl.actionlist_ActionListViewContent_Night_1_en",0,], -["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20658,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20658,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20658,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20658,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20658,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20658,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20658,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20658,], -["features.messages.impl.timeline.components_ActiveCallTimelineItemView_Day_0_en","features.messages.impl.timeline.components_ActiveCallTimelineItemView_Night_0_en",20661,], -["features.messages.impl.timeline.components_ActiveCallTimelineItemView_en","",20661,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20658,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20658,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20658,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20658,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20658,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20658,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20658,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20658,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20658,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20658,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20658,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20658,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20658,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20658,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20675,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20675,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20675,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20675,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20675,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20675,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20675,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20675,], +["features.messages.impl.timeline.components_ActiveCallTimelineItemView_Day_0_en","features.messages.impl.timeline.components_ActiveCallTimelineItemView_Night_0_en",20675,], +["features.messages.impl.timeline.components_ActiveCallTimelineItemView_en","",20675,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20675,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20675,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20675,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20675,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20675,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20675,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20675,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20675,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20675,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20675,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20675,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20675,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20675,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20675,], ["libraries.designsystem.theme.components_AllIcons_Icons_en","",0,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20658,], -["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20658,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20658,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20658,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20658,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20675,], +["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20675,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20675,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20675,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20675,], ["libraries.designsystem.components_Announcement_Day_0_en","libraries.designsystem.components_Announcement_Night_0_en",0,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20658,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20658,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20658,], -["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20658,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20675,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20675,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20675,], +["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20675,], ["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",20658,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20675,], ["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",20658,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20675,], ["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",20658,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20675,], ["libraries.designsystem.components.async_AsyncIndicatorFailure_Day_0_en","libraries.designsystem.components.async_AsyncIndicatorFailure_Night_0_en",0,], ["libraries.designsystem.components.async_AsyncIndicatorLoading_Day_0_en","libraries.designsystem.components.async_AsyncIndicatorLoading_Night_0_en",0,], ["libraries.designsystem.components.async_AsyncLoading_Day_0_en","libraries.designsystem.components.async_AsyncLoading_Night_0_en",0,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en","",20658,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en","",20658,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en","",20658,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en","",20658,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en","",20658,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en","",20658,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_en","",20658,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_en","",20658,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en","",20675,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en","",20675,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en","",20675,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en","",20675,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en","",20675,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en","",20675,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_en","",20675,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_en","",20675,], ["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,], @@ -113,22 +113,22 @@ 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.messagecomposer_AttachmentsBottomSheet_Day_0_en","features.messages.impl.messagecomposer_AttachmentsBottomSheet_Night_0_en",20658,], -["features.messages.impl.attachments.preview_AttachmentsPreviewGalleryView_en","",20658,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en","",20658,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20658,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20658,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20658,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20658,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20658,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20658,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20658,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20658,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en","",20658,], +["features.messages.impl.messagecomposer_AttachmentsBottomSheet_Day_0_en","features.messages.impl.messagecomposer_AttachmentsBottomSheet_Night_0_en",20675,], +["features.messages.impl.attachments.preview_AttachmentsPreviewGalleryView_en","",20675,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en","",20675,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20675,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20675,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20675,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20675,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20675,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20675,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20675,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20675,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en","",20675,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_1_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_2_en",0,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20658,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20675,], ["libraries.designsystem.components.avatar.internal_AvatarCluster_Avatars_en","",0,], ["libraries.matrix.ui.components_AvatarPickerSizes_Day_0_en","libraries.matrix.ui.components_AvatarPickerSizes_Night_0_en",0,], ["libraries.matrix.ui.components_AvatarPickerViewRtl_Day_0_en","libraries.matrix.ui.components_AvatarPickerViewRtl_Night_0_en",0,], @@ -158,22 +158,22 @@ export const screenshots = [ ["libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Night_0_en",0,], ["libraries.designsystem.modifiers_BackgroundVerticalGradient_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradient_Night_0_en",0,], ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], -["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20658,], +["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20675,], ["libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en","libraries.designsystem.atomic.atoms_BetaLabel_Night_0_en",0,], ["libraries.designsystem.components_BigIcon_Day_0_en","libraries.designsystem.components_BigIcon_Night_0_en",0,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20658,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20658,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20658,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20658,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20658,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20658,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20658,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20675,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20675,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20675,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20675,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20675,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20675,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20675,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20658,], -["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20658,], -["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20658,], -["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20658,], -["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20658,], +["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20675,], +["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20675,], +["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20675,], +["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20675,], +["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20675,], ["features.rageshake.impl.bugreport_BugReportViewNight_0_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_1_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_2_en","",0,], @@ -184,142 +184,142 @@ export const screenshots = [ ["features.messages.impl.timeline.components_CallMenuItem_Day_0_en","features.messages.impl.timeline.components_CallMenuItem_Night_0_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_1_en","features.messages.impl.timeline.components_CallMenuItem_Night_1_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20658,], -["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20658,], +["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20675,], +["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20675,], ["features.messages.impl.timeline.components_CallMenuItem_Day_5_en","features.messages.impl.timeline.components_CallMenuItem_Night_5_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20658,], +["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20675,], ["features.messages.impl.timeline.components_CallMenuItem_Day_7_en","features.messages.impl.timeline.components_CallMenuItem_Night_7_en",0,], ["features.call.impl.ui_CallScreenView_Day_0_en","features.call.impl.ui_CallScreenView_Night_0_en",0,], -["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20658,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20658,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20658,], -["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20658,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20658,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20658,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20675,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20675,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20675,], +["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20675,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20675,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20675,], ["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_5_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_5_en",0,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20658,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20658,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20658,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20658,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20658,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20658,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20658,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20658,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20658,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20675,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20675,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20675,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20675,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20675,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20675,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20675,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20675,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20675,], ["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",20658,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20658,], -["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20658,], -["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20658,], -["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20658,], -["features.login.impl.changeserver_ChangeServerView_Day_6_en","features.login.impl.changeserver_ChangeServerView_Night_6_en",20658,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20675,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20675,], +["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20675,], +["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20675,], +["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20675,], +["features.login.impl.changeserver_ChangeServerView_Day_6_en","features.login.impl.changeserver_ChangeServerView_Night_6_en",20675,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20658,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20675,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20658,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20658,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20658,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20658,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20658,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20658,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20658,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20658,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20675,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20675,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20675,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20675,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20675,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20675,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20675,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20675,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], -["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20658,], +["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20675,], ["libraries.designsystem.theme_ColorAliases_Day_0_en","libraries.designsystem.theme_ColorAliases_Night_0_en",0,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20658,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20658,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20658,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20658,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20658,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20658,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20658,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20675,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20675,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20675,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20675,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20675,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20675,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20675,], ["libraries.textcomposer_ComposerModeView_Day_1_en","libraries.textcomposer_ComposerModeView_Night_1_en",0,], ["libraries.textcomposer_ComposerModeView_Day_2_en","libraries.textcomposer_ComposerModeView_Night_2_en",0,], ["libraries.textcomposer_ComposerModeView_Day_3_en","libraries.textcomposer_ComposerModeView_Night_3_en",0,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20658,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20658,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20658,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20658,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20658,], -["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20658,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20675,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20675,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20675,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20675,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20675,], +["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20675,], ["libraries.designsystem.components.dialogs_ConfirmationDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ConfirmationDialog_Day_0_en","libraries.designsystem.components.dialogs_ConfirmationDialog_Night_0_en",0,], ["features.networkmonitor.api.ui_ConnectivityIndicator_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicator_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_CounterAtom_Day_0_en","libraries.designsystem.atomic.atoms_CounterAtom_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20658,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20658,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20658,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20658,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20658,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20658,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20658,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20658,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20658,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20658,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20658,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20658,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20658,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20658,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20658,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20658,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20658,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20658,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20658,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20658,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20675,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20675,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20675,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20675,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20675,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20675,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20675,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20675,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20675,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20675,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20675,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20675,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20675,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20675,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20675,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20675,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20675,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20675,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20675,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20675,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_1_en",0,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20658,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20658,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20658,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20658,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20658,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20658,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20658,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20675,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20675,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20675,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20675,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20675,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20675,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20675,], ["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",20658,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20658,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20658,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20675,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20675,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20675,], ["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",20658,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20675,], ["features.licenses.impl.details_DependenciesDetailsView_Day_0_en","features.licenses.impl.details_DependenciesDetailsView_Night_0_en",0,], -["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20658,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20658,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20658,], -["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20658,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20658,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20658,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20658,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20658,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20658,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20675,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20675,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20675,], +["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20675,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20675,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20675,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20675,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20675,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20675,], ["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,], @@ -332,20 +332,20 @@ export const screenshots = [ ["libraries.designsystem.text_DpScale_1_0f__en","",0,], ["libraries.designsystem.text_DpScale_1_5f__en","",0,], ["libraries.designsystem.theme.components_DropdownMenuItem_Menus_en","",0,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20658,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20658,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20658,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20658,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20658,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20658,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20658,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20658,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20658,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20658,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20658,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20658,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20658,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20658,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20675,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20675,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20675,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20675,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20675,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20675,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20675,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20675,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20675,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20675,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20675,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20675,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20675,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20675,], ["libraries.matrix.ui.components_EditableOrgAvatarRtl_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatarRtl_Night_0_en",0,], ["libraries.matrix.ui.components_EditableOrgAvatar_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatar_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en",0,], @@ -354,29 +354,29 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Night_0_en",0,], ["libraries.emoji.impl.picker_EmojiItemWithPopup_Day_0_en","libraries.emoji.impl.picker_EmojiItemWithPopup_Night_0_en",0,], ["libraries.emoji.impl.picker_EmojiItem_Day_0_en","libraries.emoji.impl.picker_EmojiItem_Night_0_en",0,], -["libraries.emoji.impl.picker_EmojiPickerView_Day_0_en","libraries.emoji.impl.picker_EmojiPickerView_Night_0_en",20661,], -["libraries.emoji.impl.picker_EmojiPickerView_Day_1_en","libraries.emoji.impl.picker_EmojiPickerView_Night_1_en",20661,], +["libraries.emoji.impl.picker_EmojiPickerView_Day_0_en","libraries.emoji.impl.picker_EmojiPickerView_Night_0_en",20675,], +["libraries.emoji.impl.picker_EmojiPickerView_Day_1_en","libraries.emoji.impl.picker_EmojiPickerView_Night_1_en",20675,], ["libraries.emoji.impl.picker_EmojiPickerView_Day_2_en","libraries.emoji.impl.picker_EmojiPickerView_Night_2_en",0,], ["libraries.emoji.impl.picker_EmojiPickerView_Day_3_en","libraries.emoji.impl.picker_EmojiPickerView_Night_3_en",0,], ["libraries.ui.common.nodes_EmptyView_Day_0_en","libraries.ui.common.nodes_EmptyView_Night_0_en",0,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20658,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20658,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20658,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20658,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20658,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20658,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20658,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20658,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20658,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20658,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20658,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20658,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20658,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20658,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20658,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20658,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20658,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20658,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20675,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20675,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20675,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20675,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20675,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20675,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20675,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20675,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20675,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20675,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20675,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20675,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20675,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20675,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20675,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20675,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20675,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20675,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.designsystem.components_ExpandableBottomSheetLayout_en","",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], @@ -396,49 +396,49 @@ export const screenshots = [ ["features.messages.impl.timeline.components_FloatingDateBadge_Day_0_en","features.messages.impl.timeline.components_FloatingDateBadge_Night_0_en",0,], ["libraries.designsystem.atomic.pages_FlowStepPage_Day_0_en","libraries.designsystem.atomic.pages_FlowStepPage_Night_0_en",0,], ["features.messages.impl.timeline.focus_FocusRequestStateView_Day_0_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_0_en",0,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20658,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20658,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20658,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20675,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20675,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20675,], ["features.messages.impl.timeline.components_FocusedEvent_Day_0_en","features.messages.impl.timeline.components_FocusedEvent_Night_0_en",0,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_0_en","features.forward.impl_ForwardMessagesView_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_1_en","features.forward.impl_ForwardMessagesView_Night_1_en",0,], ["features.forward.impl_ForwardMessagesView_Day_2_en","features.forward.impl_ForwardMessagesView_Night_2_en",0,], -["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20658,], -["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20658,], +["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20675,], +["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20675,], ["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_0_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_0_en",0,], -["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20658,], +["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20675,], ["libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Day_0_en","libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Night_0_en",0,], ["libraries.designsystem.components.button_GradientFloatingActionButton_Day_0_en","libraries.designsystem.components.button_GradientFloatingActionButton_Night_0_en",0,], ["features.messages.impl.timeline.components.group_GroupHeaderView_Day_0_en","features.messages.impl.timeline.components.group_GroupHeaderView_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en",0,], -["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20658,], -["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20658,], -["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20658,], -["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20658,], -["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20658,], +["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20675,], +["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20675,], +["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20675,], +["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20675,], +["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20675,], ["features.home.impl.components_HomeTopBarSpaces_Day_0_en","features.home.impl.components_HomeTopBarSpaces_Night_0_en",0,], -["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20658,], -["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20658,], +["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20675,], +["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20675,], ["features.home.impl_HomeViewA11y_en","",0,], -["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20658,], -["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20658,], +["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20675,], +["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20675,], ["features.home.impl_HomeView_Day_11_en","features.home.impl_HomeView_Night_11_en",0,], ["features.home.impl_HomeView_Day_12_en","features.home.impl_HomeView_Night_12_en",0,], -["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20658,], -["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20658,], -["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20658,], -["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20658,], -["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20658,], -["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20658,], -["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20658,], -["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20658,], -["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20658,], -["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20658,], -["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20658,], -["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20658,], -["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20658,], +["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20675,], +["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20675,], +["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20675,], +["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20675,], +["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20675,], +["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20675,], +["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20675,], +["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20675,], +["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20675,], +["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20675,], +["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20675,], +["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20675,], +["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20675,], ["libraries.designsystem.theme.components_HorizontalDivider_Dividers_en","",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en",0,], @@ -453,8 +453,8 @@ export const screenshots = [ ["appicon.enterprise_Icon_en","",0,], ["libraries.designsystem.icons_IconsOther_Day_0_en","libraries.designsystem.icons_IconsOther_Night_0_en",0,], ["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_0_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_0_en",0,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20658,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20658,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20675,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20675,], ["libraries.mediaviewer.impl.gallery.ui_ImageItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_ImageItemView_Night_0_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_0_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_0_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_10_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_10_en",0,], @@ -462,118 +462,118 @@ 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",20658,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20675,], ["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",20658,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20675,], ["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",20658,], -["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20658,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20675,], +["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20675,], ["features.verifysession.impl.incoming_IncomingVerificationViewA11y_en","",0,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20658,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20658,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20675,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20675,], ["libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Day_0_en","libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en",0,], -["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20658,], +["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20675,], ["libraries.matrix.ui.media.contentvalidation_InvalidContentView_Day_0_en","libraries.matrix.ui.media.contentvalidation_InvalidContentView_Night_0_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20658,], -["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20658,], -["features.invitepeople.impl_InvitePeopleView_Day_11_en","features.invitepeople.impl_InvitePeopleView_Night_11_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20658,], +["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20675,], +["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20675,], +["features.invitepeople.impl_InvitePeopleView_Day_11_en","features.invitepeople.impl_InvitePeopleView_Night_11_en",20675,], +["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20675,], ["features.invitepeople.impl_InvitePeopleView_Day_2_en","features.invitepeople.impl_InvitePeopleView_Night_2_en",0,], ["features.invitepeople.impl_InvitePeopleView_Day_3_en","features.invitepeople.impl_InvitePeopleView_Night_3_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20658,], -["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20658,], -["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20658,], -["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20658,], +["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20675,], +["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20675,], +["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20675,], +["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20675,], ["features.invitepeople.impl_InvitePeopleView_Day_8_en","features.invitepeople.impl_InvitePeopleView_Night_8_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20658,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20658,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20658,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20658,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20658,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20658,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20658,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20658,], +["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20675,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20675,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20675,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20675,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20675,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20675,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20675,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20675,], ["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",20658,], -["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20658,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20658,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20658,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20658,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20658,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20658,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20658,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20658,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20658,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20658,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20658,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20658,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20658,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20658,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20658,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20658,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20658,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20658,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20658,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20658,], +["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20675,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20675,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20675,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20675,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20675,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20675,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20675,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20675,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20675,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20675,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20675,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20675,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20675,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20675,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20675,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20675,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20675,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20675,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20675,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20675,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], -["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20658,], -["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20658,], +["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20675,], +["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20675,], ["features.leaveroom.impl_LeaveRoomView_Day_0_en","features.leaveroom.impl_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20658,], -["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20658,], -["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20658,], -["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20658,], -["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20658,], -["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20658,], -["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20658,], -["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20658,], -["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20658,], -["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20658,], -["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20658,], -["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20658,], -["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20658,], -["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20658,], -["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20658,], -["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20658,], -["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20658,], -["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20658,], +["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20675,], +["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20675,], +["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20675,], +["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20675,], +["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20675,], +["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20675,], +["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20675,], +["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20675,], +["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20675,], +["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20675,], +["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20675,], +["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20675,], +["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20675,], +["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20675,], +["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20675,], +["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20675,], +["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20675,], +["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20675,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en","",0,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20658,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20658,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20658,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20658,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20658,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20658,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20675,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20675,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20675,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20675,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20675,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20675,], ["features.messages.impl.link_LinkView_Day_0_en","features.messages.impl.link_LinkView_Night_0_en",0,], -["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20658,], +["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20675,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ListDialog_Day_0_en","libraries.designsystem.components.dialogs_ListDialog_Night_0_en",0,], ["libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en","",0,], @@ -626,50 +626,50 @@ export const screenshots = [ ["libraries.designsystem.theme.components_ListSupportingTextLargePadding_List_supporting_text_-_large_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextNoPadding_List_supporting_text_-_no_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en","",0,], -["features.location.api_LiveLocationSharingBanner_Day_0_en","features.location.api_LiveLocationSharingBanner_Night_0_en",20658,], +["features.location.api_LiveLocationSharingBanner_Day_0_en","features.location.api_LiveLocationSharingBanner_Night_0_en",20675,], ["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",20658,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20675,], ["libraries.designsystem.components_LocationPin_Day_0_en","libraries.designsystem.components_LocationPin_Night_0_en",0,], -["features.location.impl.common.ui_LocationShareRow_Day_0_en","features.location.impl.common.ui_LocationShareRow_Night_0_en",0,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20658,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20658,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20658,], +["features.location.impl.common.ui_LocationShareRow_Day_0_en","features.location.impl.common.ui_LocationShareRow_Night_0_en",20675,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20675,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20675,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20675,], ["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",20658,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20658,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20658,], -["appnav.loggedin_LoggedInView_Day_4_en","appnav.loggedin_LoggedInView_Night_4_en",20658,], -["appnav.loggedin_LoggedInView_Day_5_en","appnav.loggedin_LoggedInView_Night_5_en",20658,], -["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20658,], -["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20658,], -["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20658,], -["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20658,], -["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20658,], -["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20658,], -["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20658,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20658,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20658,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20658,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20658,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20658,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20658,], -["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20658,], -["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20658,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20658,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20658,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20658,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20658,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20658,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20658,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20658,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20658,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20658,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20675,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20675,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20675,], +["appnav.loggedin_LoggedInView_Day_4_en","appnav.loggedin_LoggedInView_Night_4_en",20675,], +["appnav.loggedin_LoggedInView_Day_5_en","appnav.loggedin_LoggedInView_Night_5_en",20675,], +["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20675,], +["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20675,], +["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20675,], +["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20675,], +["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20675,], +["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20675,], +["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20675,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20675,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20675,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20675,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20675,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20675,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20675,], +["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20675,], +["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20675,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20675,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20675,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20675,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20675,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20675,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20675,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20675,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20675,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20675,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20658,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20658,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20658,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20658,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20675,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20675,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20675,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20675,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], @@ -684,29 +684,29 @@ export const screenshots = [ ["libraries.matrix.ui.components_MatrixUserRow_Day_2_en","libraries.matrix.ui.components_MatrixUserRow_Night_2_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en",0,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20658,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20658,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20658,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20658,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20658,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20658,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_4_en",20658,], -["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",20658,], -["libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en",20658,], -["libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20658,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20658,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20675,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20675,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20675,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20675,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20675,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20675,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_4_en",20675,], +["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",20675,], +["libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en",20675,], +["libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20675,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20675,], ["libraries.mediaviewer.impl.local.image_MediaImageView_Day_0_en","libraries.mediaviewer.impl.local.image_MediaImageView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en",0,], @@ -714,10 +714,10 @@ export const screenshots = [ ["libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en","libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20658,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20658,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20675,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20675,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20658,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20675,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en","",0,], @@ -729,20 +729,20 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_22_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_23_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_24_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20658,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20675,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_5_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en","",20658,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en","",20658,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en","",20675,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en","",20675,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20658,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20658,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20675,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20675,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20658,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20675,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_17_en","",0,], @@ -754,12 +754,12 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerView_22_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_23_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_24_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20658,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20675,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_5_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_6_en","",20658,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_7_en","",20658,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_6_en","",20675,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_7_en","",20675,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_8_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_9_en","",0,], ["libraries.designsystem.theme.components_MediumTopAppBar_App_Bars_en","",0,], @@ -768,7 +768,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",20658,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20675,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_0_en","features.messages.impl.timeline.components_MessageEventBubble_Night_0_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_1_en","features.messages.impl.timeline.components_MessageEventBubble_Night_1_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_2_en","features.messages.impl.timeline.components_MessageEventBubble_Night_2_en",0,], @@ -777,7 +777,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_5_en","features.messages.impl.timeline.components_MessageEventBubble_Night_5_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_6_en","features.messages.impl.timeline.components_MessageEventBubble_Night_6_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20658,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20675,], ["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,], @@ -786,25 +786,25 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessagesReactionButton_Day_2_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_2_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButton_Day_3_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_3_en",0,], ["features.messages.impl_MessagesViewA11y_en","",0,], -["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20658,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20658,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20658,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20658,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20658,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20658,], -["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20658,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20658,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20658,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20658,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20658,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20658,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20658,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20658,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20658,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20658,], +["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20675,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20675,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20675,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20675,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20675,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20675,], +["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20675,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20675,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20675,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20675,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20675,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20675,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20675,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20675,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20675,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20675,], ["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",20658,], -["features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en","features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en",0,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20675,], +["features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en","features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en",20675,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en","",0,], ["appicon.element_MonochromeIcon_en","",0,], @@ -814,129 +814,129 @@ export const screenshots = [ ["libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple_selection_List_item_-_selection_in_supporting_text_List_items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItem_Multiple_selection_List_item_-_no_selection_List_items_en","",0,], ["libraries.designsystem.theme.components_NavigationBar_App_Bars_en","",0,], -["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20658,], +["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20675,], ["libraries.matrix.ui.media.contentvalidation_NotFoundContentView_Day_0_en","libraries.matrix.ui.media.contentvalidation_NotFoundContentView_Night_0_en",0,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_14_en","features.preferences.impl.notifications_NotificationSettingsView_Night_14_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_15_en","features.preferences.impl.notifications_NotificationSettingsView_Night_15_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_16_en","features.preferences.impl.notifications_NotificationSettingsView_Night_16_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_17_en","features.preferences.impl.notifications_NotificationSettingsView_Night_17_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_18_en","features.preferences.impl.notifications_NotificationSettingsView_Night_18_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_19_en","features.preferences.impl.notifications_NotificationSettingsView_Night_19_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_20_en","features.preferences.impl.notifications_NotificationSettingsView_Night_20_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20658,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20658,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20658,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_14_en","features.preferences.impl.notifications_NotificationSettingsView_Night_14_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_15_en","features.preferences.impl.notifications_NotificationSettingsView_Night_15_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_16_en","features.preferences.impl.notifications_NotificationSettingsView_Night_16_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_17_en","features.preferences.impl.notifications_NotificationSettingsView_Night_17_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_18_en","features.preferences.impl.notifications_NotificationSettingsView_Night_18_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_19_en","features.preferences.impl.notifications_NotificationSettingsView_Night_19_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_20_en","features.preferences.impl.notifications_NotificationSettingsView_Night_20_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20675,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20675,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20675,], ["features.linknewdevice.impl.screens.number.component_NumberTextField_Day_0_en","features.linknewdevice.impl.screens.number.component_NumberTextField_Night_0_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20658,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20658,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20658,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20658,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20658,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20658,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20658,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20658,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20658,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20675,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20675,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20675,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20675,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20675,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20675,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20675,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20675,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20675,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], -["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20658,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20658,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20658,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20658,], +["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20675,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20675,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20675,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20675,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_12_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_12_en",0,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_13_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_13_en",0,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20658,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20658,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20658,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20658,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20658,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20658,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20658,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20658,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20658,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20675,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20675,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20675,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20675,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20675,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20675,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20675,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20675,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20675,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonSmall_Buttons_en","",0,], -["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20658,], -["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20658,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20658,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20658,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20658,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20658,], +["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20675,], +["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20675,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20675,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20675,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20675,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20675,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_Night_0_en",0,], ["features.lockscreen.impl.unlock.keypad_PinKeypad_Day_0_en","features.lockscreen.impl.unlock.keypad_PinKeypad_Night_0_en",0,], -["features.lockscreen.impl.unlock_PinUnlockViewCompact_0_en","",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewCompact_1_en","",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_10_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_10_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_10_en","features.lockscreen.impl.unlock_PinUnlockView_Night_10_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20658,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20658,], +["features.lockscreen.impl.unlock_PinUnlockViewCompact_0_en","",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewCompact_1_en","",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_10_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_10_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_10_en","features.lockscreen.impl.unlock_PinUnlockView_Night_10_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20675,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20675,], ["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_0_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_0_en",0,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20658,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20658,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20658,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20658,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20658,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20658,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20658,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20658,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20658,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20658,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20658,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20658,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20658,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20658,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20675,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20675,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20675,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20675,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20675,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20675,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20675,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20675,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20675,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20675,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20675,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20675,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20675,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20675,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Day_0_en","libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20658,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20658,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20658,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20658,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20658,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20675,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20675,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20675,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20675,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20675,], ["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",20658,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20658,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20658,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20658,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20658,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20658,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20658,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20658,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20658,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20658,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20658,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20675,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20675,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20675,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20675,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20675,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20675,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20675,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20675,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20675,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20675,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20675,], ["features.poll.api.pollcontent_PollTitleView_Day_0_en","features.poll.api.pollcontent_PollTitleView_Night_0_en",0,], ["libraries.designsystem.components.preferences_PreferenceCategory_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceCheckbox_Preferences_en","",0,], @@ -950,231 +950,231 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceRow_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSlide_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSwitch_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewDark_6_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20658,], -["features.preferences.impl.root_PreferencesRootViewLight_6_en","",20658,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewDark_6_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20675,], +["features.preferences.impl.root_PreferencesRootViewLight_6_en","",20675,], ["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","",20658,], -["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20658,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20675,], +["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20675,], ["libraries.designsystem.components_ProgressDialogWithTextAndContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithTextAndContent_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20658,], -["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20658,], -["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20658,], -["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20658,], -["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20658,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20658,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20658,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20658,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20658,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20658,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20658,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20658,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20658,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20658,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20658,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20658,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20658,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20658,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20658,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20658,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20658,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20658,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20658,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20658,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20658,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20658,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20658,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20675,], +["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20675,], +["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20675,], +["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20675,], +["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20675,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20675,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20675,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20675,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20675,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20675,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20675,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20675,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20675,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20675,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20675,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20675,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20675,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20675,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20675,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20675,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20675,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20675,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20675,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20675,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20675,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20675,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20675,], ["libraries.qrcode_QrCodeView_en","",0,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20658,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20658,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20675,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20675,], ["features.rageshake.api.preferences_RageshakePreferencesView_Day_1_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_1_en",0,], ["features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Day_0_en","features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Night_0_en",0,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20658,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20658,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20658,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20658,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20658,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20658,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20658,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20675,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20675,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20675,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20675,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20675,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20675,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20675,], ["libraries.designsystem.atomic.atoms_RedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_RedIndicatorAtom_Night_0_en",0,], -["features.location.api_RenderingMapsNotSupportedDialog_Day_0_en","features.location.api_RenderingMapsNotSupportedDialog_Night_0_en",20658,], +["features.location.api_RenderingMapsNotSupportedDialog_Day_0_en","features.location.api_RenderingMapsNotSupportedDialog_Night_0_en",20675,], ["features.messages.impl.timeline.components_ReplySwipeIndicator_Day_0_en","features.messages.impl.timeline.components_ReplySwipeIndicator_Night_0_en",0,], ["libraries.matrix.ui.messages.reply_ReplyToInvalidContent_Day_0_en","libraries.matrix.ui.messages.reply_ReplyToInvalidContent_Night_0_en",0,], ["libraries.matrix.ui.messages.reply_ReplyToNotFoundContent_Day_0_en","libraries.matrix.ui.messages.reply_ReplyToNotFoundContent_Night_0_en",0,], -["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20658,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20658,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20658,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20658,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20658,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20658,], -["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20658,], -["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20658,], -["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20658,], -["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20658,], -["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20658,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20658,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20658,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20658,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20658,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20658,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20658,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20675,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20675,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20675,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20675,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20675,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20675,], +["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20675,], +["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20675,], +["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20675,], +["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20675,], +["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20675,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20675,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20675,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20675,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20675,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20675,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20675,], ["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_0_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_0_en",0,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20658,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20658,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20658,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20658,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20658,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20658,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20658,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20658,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20658,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20658,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20658,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20658,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20658,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20675,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20675,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20675,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20675,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20675,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20675,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20675,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20675,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20675,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20675,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20675,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20675,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20675,], ["libraries.matrix.ui.room.address_RoomAddressField_Day_0_en","libraries.matrix.ui.room.address_RoomAddressField_Night_0_en",0,], ["features.roomaliasresolver.impl_RoomAliasResolverView_Day_0_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_0_en",0,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20658,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20658,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20675,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20675,], ["features.roomdetails.impl_RoomDetailsA11y_en","",0,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_14_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_15_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_16_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_17_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_18_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_19_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_20_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_21_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_22_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20658,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20658,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20658,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20658,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20658,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20658,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20658,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20658,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20658,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20658,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20658,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20658,], -["features.roomdetails.impl_RoomDetails_0_en","",20658,], -["features.roomdetails.impl_RoomDetails_10_en","",20658,], -["features.roomdetails.impl_RoomDetails_11_en","",20658,], -["features.roomdetails.impl_RoomDetails_12_en","",20658,], -["features.roomdetails.impl_RoomDetails_13_en","",20658,], -["features.roomdetails.impl_RoomDetails_14_en","",20658,], -["features.roomdetails.impl_RoomDetails_15_en","",20658,], -["features.roomdetails.impl_RoomDetails_16_en","",20658,], -["features.roomdetails.impl_RoomDetails_17_en","",20658,], -["features.roomdetails.impl_RoomDetails_18_en","",20658,], -["features.roomdetails.impl_RoomDetails_19_en","",20658,], -["features.roomdetails.impl_RoomDetails_1_en","",20658,], -["features.roomdetails.impl_RoomDetails_20_en","",20658,], -["features.roomdetails.impl_RoomDetails_21_en","",20658,], -["features.roomdetails.impl_RoomDetails_22_en","",20658,], -["features.roomdetails.impl_RoomDetails_2_en","",20658,], -["features.roomdetails.impl_RoomDetails_3_en","",20658,], -["features.roomdetails.impl_RoomDetails_4_en","",20658,], -["features.roomdetails.impl_RoomDetails_5_en","",20658,], -["features.roomdetails.impl_RoomDetails_6_en","",20658,], -["features.roomdetails.impl_RoomDetails_7_en","",20658,], -["features.roomdetails.impl_RoomDetails_8_en","",20658,], -["features.roomdetails.impl_RoomDetails_9_en","",20658,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20658,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20658,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20658,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20658,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20658,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20658,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20658,], -["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20658,], -["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20658,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_14_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_15_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_16_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_17_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_18_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_19_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_20_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_21_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_22_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20675,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20675,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20675,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20675,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20675,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20675,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20675,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20675,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20675,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20675,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20675,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20675,], +["features.roomdetails.impl_RoomDetails_0_en","",20675,], +["features.roomdetails.impl_RoomDetails_10_en","",20675,], +["features.roomdetails.impl_RoomDetails_11_en","",20675,], +["features.roomdetails.impl_RoomDetails_12_en","",20675,], +["features.roomdetails.impl_RoomDetails_13_en","",20675,], +["features.roomdetails.impl_RoomDetails_14_en","",20675,], +["features.roomdetails.impl_RoomDetails_15_en","",20675,], +["features.roomdetails.impl_RoomDetails_16_en","",20675,], +["features.roomdetails.impl_RoomDetails_17_en","",20675,], +["features.roomdetails.impl_RoomDetails_18_en","",20675,], +["features.roomdetails.impl_RoomDetails_19_en","",20675,], +["features.roomdetails.impl_RoomDetails_1_en","",20675,], +["features.roomdetails.impl_RoomDetails_20_en","",20675,], +["features.roomdetails.impl_RoomDetails_21_en","",20675,], +["features.roomdetails.impl_RoomDetails_22_en","",20675,], +["features.roomdetails.impl_RoomDetails_2_en","",20675,], +["features.roomdetails.impl_RoomDetails_3_en","",20675,], +["features.roomdetails.impl_RoomDetails_4_en","",20675,], +["features.roomdetails.impl_RoomDetails_5_en","",20675,], +["features.roomdetails.impl_RoomDetails_6_en","",20675,], +["features.roomdetails.impl_RoomDetails_7_en","",20675,], +["features.roomdetails.impl_RoomDetails_8_en","",20675,], +["features.roomdetails.impl_RoomDetails_9_en","",20675,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20675,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20675,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20675,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20675,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20675,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20675,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20675,], +["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20675,], +["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20675,], ["features.home.impl.components_RoomListContentView_Day_2_en","features.home.impl.components_RoomListContentView_Night_2_en",0,], -["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20658,], -["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20658,], -["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20658,], -["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20658,], -["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20658,], -["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20658,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20658,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20658,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20658,], -["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20658,], -["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20658,], +["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20675,], +["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20675,], +["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20675,], +["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20675,], +["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20675,], +["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20675,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20675,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20675,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20675,], +["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20675,], +["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20675,], ["features.home.impl.search_RoomListSearchContent_Day_0_en","features.home.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20658,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20658,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20658,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20658,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20658,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20658,], -["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20658,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20658,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20658,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20658,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20658,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20658,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20658,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20658,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20658,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20658,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20658,], +["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20675,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20675,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20675,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20675,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20675,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20675,], +["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20675,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20675,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20675,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20675,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20675,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20675,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20675,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20675,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20675,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20675,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20675,], ["features.roommembermoderation.impl_RoomMemberModerationView_Day_9_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20658,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20658,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20658,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20658,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20658,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20658,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20658,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20658,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20675,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20675,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20675,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20675,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20675,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20675,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20675,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20675,], ["libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Night_0_en",0,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20658,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20658,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20658,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20658,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20658,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20658,], -["libraries.roomselect.impl_RoomSelectView_Day_6_en","libraries.roomselect.impl_RoomSelectView_Night_6_en",20658,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20675,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20675,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20675,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20675,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20675,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20675,], +["libraries.roomselect.impl_RoomSelectView_Day_6_en","libraries.roomselect.impl_RoomSelectView_Night_6_en",20675,], ["features.home.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.home.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_0_en","features.home.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_10_en","features.home.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -1197,16 +1197,16 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_26_en","features.home.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_27_en","features.home.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_28_en","features.home.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20658,], -["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20658,], -["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20658,], -["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20658,], -["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20658,], -["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20658,], -["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20658,], -["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20658,], +["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20675,], +["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20675,], +["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20675,], +["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20675,], +["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20675,], +["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20675,], +["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20675,], +["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20675,], ["features.home.impl.components_RoomSummaryRow_Day_36_en","features.home.impl.components_RoomSummaryRow_Night_36_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20658,], +["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20675,], ["features.home.impl.components_RoomSummaryRow_Day_38_en","features.home.impl.components_RoomSummaryRow_Night_38_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_3_en","features.home.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_4_en","features.home.impl.components_RoomSummaryRow_Night_4_en",0,], @@ -1215,119 +1215,119 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_7_en","features.home.impl.components_RoomSummaryRow_Night_7_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_8_en","features.home.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_9_en","features.home.impl.components_RoomSummaryRow_Night_9_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20658,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20675,], ["features.login.impl.screens.classic.root_RootView_Day_0_en","features.login.impl.screens.classic.root_RootView_Night_0_en",0,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20658,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20658,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20675,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20675,], ["appicon.enterprise_RoundIcon_en","",0,], ["appicon.element_RoundIcon_en","",0,], ["libraries.designsystem.atomic.atoms_RoundedIconAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoundedIconAtom_Night_0_en",0,], -["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20658,], -["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20658,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20658,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20658,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20658,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20658,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20658,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20658,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20675,], +["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20675,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20675,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20675,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20675,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20675,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20675,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20675,], ["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","",20658,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20675,], ["libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchBarInactive_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchFieldsDark_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchFieldsLight_Search_views_en","",0,], -["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20658,], -["features.startchat.impl.components_SearchSingleUserResultItem_en","",20658,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20658,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20658,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20658,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20658,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20658,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20658,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20658,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20658,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20658,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20658,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20658,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20658,], -["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20658,], +["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20675,], +["features.startchat.impl.components_SearchSingleUserResultItem_en","",20675,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20675,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20675,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20675,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20675,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20675,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20675,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20675,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20675,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20675,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20675,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20675,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20675,], +["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20675,], ["libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_0_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_1_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_1_en",0,], @@ -1350,39 +1350,39 @@ export const screenshots = [ ["libraries.matrix.ui.messages.sender_SenderName_Day_6_en","libraries.matrix.ui.messages.sender_SenderName_Night_6_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_7_en","libraries.matrix.ui.messages.sender_SenderName_Night_7_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_8_en","libraries.matrix.ui.messages.sender_SenderName_Night_8_en",0,], -["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20658,], -["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20658,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20658,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20658,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20658,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20658,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20658,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20658,], -["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20658,], -["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20658,], -["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20658,], -["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20658,], -["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20658,], -["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20658,], -["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20658,], -["features.location.impl.share_ShareLocationView_Day_7_en","features.location.impl.share_ShareLocationView_Night_7_en",20658,], -["features.location.impl.share_ShareLocationView_Day_8_en","features.location.impl.share_ShareLocationView_Night_8_en",20658,], -["features.location.impl.share_ShareLocationView_Day_9_en","features.location.impl.share_ShareLocationView_Night_9_en",20658,], +["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20675,], +["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20675,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20675,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20675,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20675,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20675,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20675,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20675,], +["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20675,], +["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20675,], +["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20675,], +["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20675,], +["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20675,], +["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20675,], +["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20675,], +["features.location.impl.share_ShareLocationView_Day_7_en","features.location.impl.share_ShareLocationView_Night_7_en",20675,], +["features.location.impl.share_ShareLocationView_Day_8_en","features.location.impl.share_ShareLocationView_Night_8_en",20675,], +["features.location.impl.share_ShareLocationView_Day_9_en","features.location.impl.share_ShareLocationView_Night_9_en",20675,], ["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",20658,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20658,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20658,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20658,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20658,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20658,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20658,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20658,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20658,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20658,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en",20658,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20658,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20675,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20675,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20675,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20675,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20675,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20675,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20675,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20675,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20675,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20675,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en",20675,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20675,], ["libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en","libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en",0,], ["libraries.designsystem.components.dialogs_SingleSelectionDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_SingleSelectionDialog_Day_0_en","libraries.designsystem.components.dialogs_SingleSelectionDialog_Night_0_en",0,], @@ -1393,106 +1393,106 @@ export const screenshots = [ ["libraries.designsystem.components.list_SingleSelectionListItem_Single_selection_List_item_-_no_selection_List_items_en","",0,], ["libraries.emoji.impl.picker_SkinTonePicker_Day_0_en","libraries.emoji.impl.picker_SkinTonePicker_Night_0_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",20658,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20675,], ["libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar_with_action_and_close_button_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar_with_action_and_close_button_on_new_line_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar_with_action_on_new_line_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en","",0,], ["libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en","",0,], ["libraries.designsystem.components.avatar.internal_SpaceAvatar_Avatars_en","",0,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20658,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20658,], -["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20658,], -["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20658,], -["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20658,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20675,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20675,], +["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20675,], +["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20675,], +["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20675,], ["libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Day_0_en","libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Night_0_en",0,], ["libraries.matrix.ui.components_SpaceMembersView_Day_0_en","libraries.matrix.ui.components_SpaceMembersView_Night_0_en",0,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20658,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20658,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20658,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20658,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20658,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20658,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20658,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20658,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20658,], -["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20658,], -["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20658,], -["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20658,], -["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20658,], -["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20658,], -["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20658,], -["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20658,], -["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20658,], -["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20658,], -["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20658,], -["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20658,], -["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20658,], -["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20658,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20675,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20675,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20675,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20675,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20675,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20675,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20675,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20675,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20675,], +["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20675,], +["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20675,], +["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20675,], +["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20675,], +["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20675,], +["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20675,], +["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20675,], +["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20675,], +["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20675,], +["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20675,], +["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20675,], +["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20675,], +["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20675,], ["libraries.designsystem.modifiers_SquareSizeModifierInsideSquare_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], -["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20658,], -["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20658,], -["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20658,], -["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20658,], -["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20658,], -["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20658,], +["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20675,], +["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20675,], +["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20675,], +["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20675,], +["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20675,], +["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20675,], ["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",20658,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20675,], ["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",20658,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20675,], ["libraries.designsystem.components.avatar.internal_TextAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TextButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonSmall_Buttons_en","",0,], -["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20658,], -["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20658,], -["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20658,], -["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20658,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20658,], -["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20658,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20658,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20658,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20658,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20658,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20658,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20658,], -["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20658,], -["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20658,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20658,], -["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20658,], +["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20675,], +["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20675,], +["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20675,], +["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20675,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20675,], +["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20675,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20675,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20675,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20675,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20675,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20675,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20675,], +["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20675,], +["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20675,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20675,], +["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20675,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], -["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20658,], -["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20658,], +["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20675,], +["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20675,], ["libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en","",0,], @@ -1505,17 +1505,17 @@ export const screenshots = [ ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], ["features.messages.impl.threads.list_ThreadListItemRow_Day_0_en","features.messages.impl.threads.list_ThreadListItemRow_Night_0_en",0,], -["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20658,], -["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20658,], +["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20675,], +["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20675,], ["features.messages.impl.threads.list_ThreadsListView_Day_0_en","features.messages.impl.threads.list_ThreadsListView_Night_0_en",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20658,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20658,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20658,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20675,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20675,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20675,], ["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",20658,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20658,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20675,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20675,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_5_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_6_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_6_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_7_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en",0,], @@ -1526,32 +1526,32 @@ export const screenshots = [ ["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",20658,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20675,], ["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",20658,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20658,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20675,], ["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,], -["features.messages.impl.timeline.components_TimelineItemEventRowRtlContent_en","",20658,], +["features.messages.impl.timeline.components_TimelineItemEventRowRtlContent_en","",20675,], ["features.messages.impl.timeline.components_TimelineItemEventRowShield_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowShield_Night_0_en",0,], ["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",20658,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20658,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20675,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_6_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20658,], -["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20658,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20675,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_2_en",0,], @@ -1561,29 +1561,29 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_6_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_7_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_8_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20658,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20675,], ["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",20658,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20658,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en",20658,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en",20661,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en",20658,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en",20675,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en",20658,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en",20661,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en",20675,], ["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",20658,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20658,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en",20661,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en",20675,], ["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",20658,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20658,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",20661,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20658,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",20675,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20675,], ["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","",20658,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20675,], ["features.messages.impl.timeline.components_TimelineItemFileViewScanningContent_Day_0_en","features.messages.impl.timeline.components_TimelineItemFileViewScanningContent_Night_0_en",0,], ["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,], @@ -1599,31 +1599,31 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemGalleryView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemGalleryView_Night_7_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemGalleryView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemGalleryView_Night_8_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemGalleryView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemGalleryView_Night_9_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20658,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20658,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20675,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20675,], ["features.messages.impl.timeline.components_TimelineItemImageViewScanningContent_Day_0_en","features.messages.impl.timeline.components_TimelineItemImageViewScanningContent_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_2_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemInformativeView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemInformativeView_Night_0_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20658,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20675,], ["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",20658,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20658,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20658,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20675,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20675,], ["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",20658,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20658,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20675,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20675,], ["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",20658,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20675,], ["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,], @@ -1633,8 +1633,8 @@ export const screenshots = [ ["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_TimelineItemRedactedMessagesGroup_Day_0_en","features.messages.impl.timeline.components_TimelineItemRedactedMessagesGroup_Night_0_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20658,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20658,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20675,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20675,], ["features.messages.impl.timeline.components_TimelineItemScanningContentFailed_Day_0_en","features.messages.impl.timeline.components_TimelineItemScanningContentFailed_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemScanningContentNotFound_Day_0_en","features.messages.impl.timeline.components_TimelineItemScanningContentNotFound_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemScanningContentWithInvalidReplies_Day_0_en","features.messages.impl.timeline.components_TimelineItemScanningContentWithInvalidReplies_Night_0_en",0,], @@ -1654,8 +1654,8 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_4_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_5_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20658,], -["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20658,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20675,], +["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20675,], ["features.messages.impl.timeline.components_TimelineItemVideoViewScanningContent_Day_0_en","features.messages.impl.timeline.components_TimelineItemVideoViewScanningContent_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_1_en",0,], @@ -1680,94 +1680,94 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_8_en",0,], ["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_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20658,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20675,], ["features.messages.impl.timeline_TimelineViewWithReadMarkerBothIndicators_Day_0_en","features.messages.impl.timeline_TimelineViewWithReadMarkerBothIndicators_Night_0_en",0,], ["features.messages.impl.timeline_TimelineViewWithReadMarkerJumpToUnreadIndicatorOnly_Day_0_en","features.messages.impl.timeline_TimelineViewWithReadMarkerJumpToUnreadIndicatorOnly_Night_0_en",0,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20658,], -["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20658,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20658,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20658,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20658,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20658,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20658,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20658,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20658,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20658,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20675,], +["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20675,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20675,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20675,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20675,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20675,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20675,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20675,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20675,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20675,], ["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",20658,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20675,], ["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",20658,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20675,], ["features.messages.impl.timeline_TimelineView_Day_7_en","features.messages.impl.timeline_TimelineView_Night_7_en",0,], ["features.messages.impl.timeline_TimelineView_Day_8_en","features.messages.impl.timeline_TimelineView_Night_8_en",0,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.components.avatar.internal_TombstonedRoomAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TopAppBarStr_App_Bars_en","",0,], ["libraries.designsystem.theme.components_TopAppBar_App_Bars_en","",0,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20658,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20658,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20658,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20658,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20658,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20658,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20658,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20658,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20675,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20675,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20675,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20675,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20675,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20675,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20675,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20675,], ["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",20658,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20658,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20658,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20658,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20658,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20658,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20675,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20675,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20675,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20675,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20675,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20675,], ["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","",20658,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20675,], ["libraries.designsystem.components.avatar.internal_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar.internal_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20658,], -["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20658,], -["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20658,], -["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20658,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20675,], +["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20675,], +["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20675,], +["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20675,], ["features.startchat.impl.components_UserListView_Day_3_en","features.startchat.impl.components_UserListView_Night_3_en",0,], ["features.startchat.impl.components_UserListView_Day_4_en","features.startchat.impl.components_UserListView_Night_4_en",0,], ["features.startchat.impl.components_UserListView_Day_5_en","features.startchat.impl.components_UserListView_Night_5_en",0,], ["features.startchat.impl.components_UserListView_Day_6_en","features.startchat.impl.components_UserListView_Night_6_en",0,], -["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20658,], +["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20675,], ["features.startchat.impl.components_UserListView_Day_8_en","features.startchat.impl.components_UserListView_Night_8_en",0,], -["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20658,], +["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20675,], ["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_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20658,], -["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20658,], -["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20658,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20658,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20658,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20658,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20658,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20658,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20658,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20658,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20658,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20658,], -["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20658,], +["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20675,], +["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20675,], +["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20675,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20675,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20675,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20675,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20675,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20675,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20675,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20675,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20675,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20675,], +["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20675,], ["features.preferences.impl.userstatus_UserStatusView_Day_0_en","features.preferences.impl.userstatus_UserStatusView_Night_0_en",0,], ["features.preferences.impl.userstatus_UserStatusView_Day_1_en","features.preferences.impl.userstatus_UserStatusView_Night_1_en",0,], ["features.preferences.impl.userstatus_UserStatusView_Day_2_en","features.preferences.impl.userstatus_UserStatusView_Night_2_en",0,], ["features.preferences.impl.userstatus_UserStatusView_Day_3_en","features.preferences.impl.userstatus_UserStatusView_Night_3_en",0,], ["features.preferences.impl.userstatus_UserStatusView_Day_4_en","features.preferences.impl.userstatus_UserStatusView_Night_4_en",0,], -["features.preferences.impl.userstatus_UserStatusView_Day_5_en","features.preferences.impl.userstatus_UserStatusView_Night_5_en",20658,], -["features.preferences.impl.userstatus_UserStatusView_Day_6_en","features.preferences.impl.userstatus_UserStatusView_Night_6_en",20658,], +["features.preferences.impl.userstatus_UserStatusView_Day_5_en","features.preferences.impl.userstatus_UserStatusView_Night_5_en",20675,], +["features.preferences.impl.userstatus_UserStatusView_Day_6_en","features.preferences.impl.userstatus_UserStatusView_Night_6_en",20675,], ["features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en","features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en",0,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_1_en",0,], -["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20658,], -["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20658,], +["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20675,], +["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20675,], ["features.viewfolder.impl.file_ViewFileView_Day_0_en","features.viewfolder.impl.file_ViewFileView_Night_0_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_1_en","features.viewfolder.impl.file_ViewFileView_Night_1_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_2_en","features.viewfolder.impl.file_ViewFileView_Night_2_en",0,], -["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20658,], +["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20675,], ["features.viewfolder.impl.file_ViewFileView_Day_4_en","features.viewfolder.impl.file_ViewFileView_Night_4_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_5_en","features.viewfolder.impl.file_ViewFileView_Night_5_en",0,], ["features.viewfolder.impl.folder_ViewFolderView_Day_0_en","features.viewfolder.impl.folder_ViewFolderView_Night_0_en",0,], diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png index ae365f5143..5fe8e62f96 100644 --- a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:645349979f0789dd674b9efcaee305a35955725c5c4d24599984aefc3a61b18f -size 32005 +oid sha256:b2e211a700dfcefab1927926ecd2ab1eee2ceaed6980657e1bad1e70bbc69dd5 +size 29522 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png index 93d24c8613..d002a79378 100644 --- a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3cd12ed914f8c161ef845feaaec416eec9ef1ae6d23d71fb444c93d1388738ea -size 41736 +oid sha256:ab30267a07b1ceb43c1a7f854e758c68123812d99054f6e94db4cccf3214a2b4 +size 41479 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png index 56bc529007..caa435aca2 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76c4f1674cda818e0684869050fbad2cb31c02146726d1ee5e57481940218d29 -size 30363 +oid sha256:455ad29386413801da964f034b284c3693ed2281510cfdb0e6a0e50478904098 +size 32472 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png index 3d4aa9edc5..75f447d578 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd18d8bb6e61f616451439dff8690afc7473461c87adf11bb9870319d8e06fc4 -size 38447 +oid sha256:a205abcee059aef3f902cac7716a5bb53127b98ab29d97b0b44cbaa6ba5cdabe +size 42394 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png index d9f4faabc7..21eb862eda 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:124381cc86542b2704b619e798f62f58dc7f3b95ab8a1c4eec3fa4b849462f9e -size 34867 +oid sha256:e0cd7909eb2cce4ce2ad297791527468a18b43343c4f4c7b1bb7ab5ade6cfc9d +size 36959 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png index d261494eb8..46f4412ab9 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a3d7220c216b6fee2384ba2cb263903a8cfa45f3f97302c722188a4a827c575 -size 21630 +oid sha256:299310684787f8c18e4219e69125ce48d32d229b4bb3087da7e102aa87ea1ac7 +size 21599 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png index dcf2ab131b..1cf67b2855 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cab8ea5b016f20de07dd0134836c42f9fd51562837fe4da2be8a8d38ca1f95f8 -size 19914 +oid sha256:e68d95e8c4734d26e0853c886f8253c3ff21508fdf97dac8879e67d25a683e06 +size 19899 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png index 4185f46b51..5289eaca63 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:085963fcb425147d35a9ad947a7a3e4ba4712bd3d2f32e93099ff15cffe83b88 -size 64761 +oid sha256:65f9ee083a73669a0d3f9d88141a610ea53f3c39e89d8f706d414b04555b4764 +size 59321 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png index 0cf6c10c4e..bd73e322ab 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49f0bb16d3d57b59242e704e3dd4e8885c59721a42129837ff6a4f0d7d02de11 -size 54433 +oid sha256:12020ba97720f2374f9ace172693f08be01522e4ca30d3970efa91d802581e81 +size 3657 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png index 12bd190dd1..0ab00359b7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce15ca9f89274620728715e3448ce671e2253f4fed22a44035460e9a3709ca0b -size 647404 +oid sha256:7b5e6f4c5ec97e67bb20f03f25f18d9f32d5d1aed5ff30d161b331894ad03443 +size 647384 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png index 86018764ac..195aa04fe2 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8344427a0fe87cbf0883ff53a9ee4dcf90976a7011f6618d85961df48b2c53a9 -size 370228 +oid sha256:76e5ae44362792d570cd82f3910c8bd0acde509b8418c5892e0385e7d636f042 +size 370223 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png index 40fa5d563f..ed84f87e74 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ff97eb99daf65f74b469d51b93bd77d304d0c780b9406e1c4d7893b71e3c68d -size 367163 +oid sha256:da4f118a4c669358ffb28b89005893cb6398f27c4389352a0884af1a04018fec +size 366813 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png index 68f8148879..b5dac8434c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ca8cf82aa8ab1bdad56a27584b27789402aeb7ad8fadd038280a376fb75a72c -size 345094 +oid sha256:1a92f8af94682facac66d4aacb6e49f345561c15be2f8b689041b78aeaaae831 +size 345062 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png index 133fd3903f..d7b969fd5b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:426d1c844a8c88281c5fa4c61916d93278fdfd38f8cdf2dc54bf1a821f2de3eb -size 397167 +oid sha256:2528df873713755f542f61cd794080929586c1bd5f316c0378ce9e837c21d50b +size 397145 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png index b10f9741b9..0021619c44 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ec1b0244dab22414c6719b3309426e13128d0c70fb67dc66cd8a60d42521fd0 -size 355411 +oid sha256:7b23d3a37774283067a8919b8c332b1bb5a79e90dceb7216b484b985491282e5 +size 355389 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png index b6d1f07279..47a9a6b630 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bee12a6bdb18fea5ac6997671bfcb903296da7278cf69b2456330066ca31dfb -size 342870 +oid sha256:54372845e922d047408dd960d048b9b5eae2263d589117d8e7d197741aa34f32 +size 342827 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png index 0eb106f4a9..b51a89586b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:217ac95bf476c53d2dc6d3d0143b21312bf38b8ab86c8dfd4f9360582b584f9d -size 395476 +oid sha256:e642229c58169775fe702769a306bbef22698516ae6f7ac75a761213c3f195fa +size 395446 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png index c8306263a0..c9227ebf94 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:756f0cff90ddd61adce2f467c0384d74d430f6d51c091e4980e45bcc9ee6f0fd -size 353705 +oid sha256:e614f4a8657637c584a283d69124236d00fe1251c2b8d739c5688a57a6fd152e +size 353693 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png index adabb6dde5..ee23a7111d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b071ba6e99b68dcc75f5aaed3a879f2e30614c954b88c6dc05e1dfec57f1079 -size 65771 +oid sha256:38ae233a128e887efc48da73be3d6ce50a7095601315ed49a3c9cb209ffdda26 +size 58352 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png index 479d07ae5f..f6456f0ec1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d4f03ac10bbb676d272dba906e758b25d633a1da1e249fe23ba825f8d2f4901 -size 55298 +oid sha256:22153168f6f3e84ab789faea29c94fffbc1a5e4c1b9a98deccb0460192fdde23 +size 57213 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png index 2209fcb54f..b3af9d831a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd0de7412503e93c33cf92dde0cff38bb68555ef176080d913320af5e4942e7a -size 58642 +oid sha256:ce795c4e32a1be3d57f6b1c87588d8c7ed0442996ab9d8a96f1ec5a07627e598 +size 59663 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png index b3109663f2..6e8468054f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:10bce6df962a5198f019f509b1cdb55e28195587de4b74742ddc29833f1333a4 -size 69216 +oid sha256:262e8d4b8daa2d25593834fd0ab5d7019b0a3dd13707aa96e0701bf9240c6525 +size 59809 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png index b0cd5bc6d4..3974618f39 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e900ff75d02c9db4200e2032444dd7e2f8635973cca3389dd96d7d2bb734061 -size 50757 +oid sha256:718d5c9f24c114f3bf88e000b637ea19598d7bb9b97001ba137fc12a6a1285e4 +size 52608 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png index 952751a962..7261c9b671 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed13a2778b8ced86ee41d5460d274b42b714a87f59749e3b26bf75114ef40dd9 -size 58265 +oid sha256:7cf3d727da78b39ecf24d4df7db0884af9008db67cc92274849c1b0978f87726 +size 59252 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png index a68f886f73..616c06bf46 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5a2e306087a967849f61872b56a83f194d1a76df5723f0bbae6e6e2f4a98c63 -size 55725 +oid sha256:0ce2519906f1aaf34700ca9ba4bdeaa72030f1f5aff3f7e24352a1435ac67d98 +size 53966 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png index 73b8b8e50c..5c597300e4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8a3ad99763929d2783147814e44fa9ec7e7efb7b99949e3580f23c38fb5d971 -size 9398 +oid sha256:21aee17ee9a292bb2f0d3d2ec2db565ebb8d5df753454a5a002f033ed5ed39c5 +size 3853 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png index a87d89c701..d47bf90664 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f3a0cb5d9bae6725a1c527f3d9575aabf85d3edf63a28d9faa23f4f0b65e2d0 -size 7624 +oid sha256:520cae2544153ba45f8fe4b021286c2d22da06aa1b60be7f1b13879723ff994c +size 3664 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png index fedead0fa8..52fd9b9693 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4811667dec0f32accc5e2c8a5dd5c0b34017fcff8d1e0376ee7a4708426bca0f -size 9838 +oid sha256:58b9491aaa5554b89d86c62a5c9c44975367538c6baa5562f8dc5768f76d8264 +size 8705 From 612b4a6dbe4f4d9d7720a827843ed051c565d04c Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 11 Aug 2026 07:41:59 +0000 Subject: [PATCH 099/338] Update screenshots --- ...krequests.impl.banner_KnockRequestsBannerView_Day_5_en.png | 4 ++-- ...knockrequests.impl.list_KnockRequestsListView_Day_3_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_3_en.png | 4 ++-- ...eatures.location.impl.share_ShareLocationView_Day_7_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_1_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_5_en.png | 4 ++-- ...tures.location.impl.share_ShareLocationView_Night_9_en.png | 4 ++-- ...rypto.identity_MessagesViewWithIdentityChange_Day_2_en.png | 4 ++-- ....resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png | 4 ++-- ...meline.components.event_ATimelineItemEventRow_Day_0_en.png | 4 ++-- ...ents_TimelineItemEventRowWithReplyInformative_Day_1_en.png | 4 ++-- ...components_TimelineItemEventRowWithReplyOther_Day_1_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_1_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_5_en.png | 4 ++-- ...line.components_TimelineItemEventRowWithReply_Day_9_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_1_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_5_en.png | 4 ++-- ...ne.components_TimelineItemEventRowWithReply_Night_9_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_11_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_3_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_7_en.png | 4 ++-- .../features.messages.impl_MessagesView_Night_11_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_3_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_7_en.png | 4 ++-- ...ferences.impl.blockedusers_BlockedUsersView_Night_4_en.png | 4 ++-- ...embermoderation.impl_RoomMemberModerationView_Day_5_en.png | 4 ++-- ...bermoderation.impl_RoomMemberModerationView_Night_7_en.png | 4 ++-- ...e.components_HorizontalFloatingToolbarNoFab_Night_0_en.png | 4 ++-- 28 files changed, 56 insertions(+), 56 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png index 5fe8e62f96..ae365f5143 100644 --- a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2e211a700dfcefab1927926ecd2ab1eee2ceaed6980657e1bad1e70bbc69dd5 -size 29522 +oid sha256:645349979f0789dd674b9efcaee305a35955725c5c4d24599984aefc3a61b18f +size 32005 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png index d002a79378..93d24c8613 100644 --- a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab30267a07b1ceb43c1a7f854e758c68123812d99054f6e94db4cccf3214a2b4 -size 41479 +oid sha256:3cd12ed914f8c161ef845feaaec416eec9ef1ae6d23d71fb444c93d1388738ea +size 41736 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png index caa435aca2..56bc529007 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:455ad29386413801da964f034b284c3693ed2281510cfdb0e6a0e50478904098 -size 32472 +oid sha256:76c4f1674cda818e0684869050fbad2cb31c02146726d1ee5e57481940218d29 +size 30363 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png index 75f447d578..3d4aa9edc5 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a205abcee059aef3f902cac7716a5bb53127b98ab29d97b0b44cbaa6ba5cdabe -size 42394 +oid sha256:fd18d8bb6e61f616451439dff8690afc7473461c87adf11bb9870319d8e06fc4 +size 38447 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png index 21eb862eda..d9f4faabc7 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0cd7909eb2cce4ce2ad297791527468a18b43343c4f4c7b1bb7ab5ade6cfc9d -size 36959 +oid sha256:124381cc86542b2704b619e798f62f58dc7f3b95ab8a1c4eec3fa4b849462f9e +size 34867 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png index 46f4412ab9..d261494eb8 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:299310684787f8c18e4219e69125ce48d32d229b4bb3087da7e102aa87ea1ac7 -size 21599 +oid sha256:8a3d7220c216b6fee2384ba2cb263903a8cfa45f3f97302c722188a4a827c575 +size 21630 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png index 1cf67b2855..dcf2ab131b 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e68d95e8c4734d26e0853c886f8253c3ff21508fdf97dac8879e67d25a683e06 -size 19899 +oid sha256:cab8ea5b016f20de07dd0134836c42f9fd51562837fe4da2be8a8d38ca1f95f8 +size 19914 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png index 5289eaca63..4185f46b51 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65f9ee083a73669a0d3f9d88141a610ea53f3c39e89d8f706d414b04555b4764 -size 59321 +oid sha256:085963fcb425147d35a9ad947a7a3e4ba4712bd3d2f32e93099ff15cffe83b88 +size 64761 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png index bd73e322ab..0cf6c10c4e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12020ba97720f2374f9ace172693f08be01522e4ca30d3970efa91d802581e81 -size 3657 +oid sha256:49f0bb16d3d57b59242e704e3dd4e8885c59721a42129837ff6a4f0d7d02de11 +size 54433 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png index 0ab00359b7..12bd190dd1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b5e6f4c5ec97e67bb20f03f25f18d9f32d5d1aed5ff30d161b331894ad03443 -size 647384 +oid sha256:ce15ca9f89274620728715e3448ce671e2253f4fed22a44035460e9a3709ca0b +size 647404 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png index 195aa04fe2..86018764ac 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76e5ae44362792d570cd82f3910c8bd0acde509b8418c5892e0385e7d636f042 -size 370223 +oid sha256:8344427a0fe87cbf0883ff53a9ee4dcf90976a7011f6618d85961df48b2c53a9 +size 370228 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png index ed84f87e74..40fa5d563f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da4f118a4c669358ffb28b89005893cb6398f27c4389352a0884af1a04018fec -size 366813 +oid sha256:3ff97eb99daf65f74b469d51b93bd77d304d0c780b9406e1c4d7893b71e3c68d +size 367163 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png index b5dac8434c..68f8148879 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a92f8af94682facac66d4aacb6e49f345561c15be2f8b689041b78aeaaae831 -size 345062 +oid sha256:6ca8cf82aa8ab1bdad56a27584b27789402aeb7ad8fadd038280a376fb75a72c +size 345094 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png index d7b969fd5b..133fd3903f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2528df873713755f542f61cd794080929586c1bd5f316c0378ce9e837c21d50b -size 397145 +oid sha256:426d1c844a8c88281c5fa4c61916d93278fdfd38f8cdf2dc54bf1a821f2de3eb +size 397167 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png index 0021619c44..b10f9741b9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b23d3a37774283067a8919b8c332b1bb5a79e90dceb7216b484b985491282e5 -size 355389 +oid sha256:3ec1b0244dab22414c6719b3309426e13128d0c70fb67dc66cd8a60d42521fd0 +size 355411 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png index 47a9a6b630..b6d1f07279 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54372845e922d047408dd960d048b9b5eae2263d589117d8e7d197741aa34f32 -size 342827 +oid sha256:4bee12a6bdb18fea5ac6997671bfcb903296da7278cf69b2456330066ca31dfb +size 342870 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png index b51a89586b..0eb106f4a9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e642229c58169775fe702769a306bbef22698516ae6f7ac75a761213c3f195fa -size 395446 +oid sha256:217ac95bf476c53d2dc6d3d0143b21312bf38b8ab86c8dfd4f9360582b584f9d +size 395476 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png index c9227ebf94..c8306263a0 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e614f4a8657637c584a283d69124236d00fe1251c2b8d739c5688a57a6fd152e -size 353693 +oid sha256:756f0cff90ddd61adce2f467c0384d74d430f6d51c091e4980e45bcc9ee6f0fd +size 353705 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png index ee23a7111d..adabb6dde5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38ae233a128e887efc48da73be3d6ce50a7095601315ed49a3c9cb209ffdda26 -size 58352 +oid sha256:9b071ba6e99b68dcc75f5aaed3a879f2e30614c954b88c6dc05e1dfec57f1079 +size 65771 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png index f6456f0ec1..479d07ae5f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22153168f6f3e84ab789faea29c94fffbc1a5e4c1b9a98deccb0460192fdde23 -size 57213 +oid sha256:0d4f03ac10bbb676d272dba906e758b25d633a1da1e249fe23ba825f8d2f4901 +size 55298 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png index b3af9d831a..2209fcb54f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce795c4e32a1be3d57f6b1c87588d8c7ed0442996ab9d8a96f1ec5a07627e598 -size 59663 +oid sha256:bd0de7412503e93c33cf92dde0cff38bb68555ef176080d913320af5e4942e7a +size 58642 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png index 6e8468054f..b3109663f2 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:262e8d4b8daa2d25593834fd0ab5d7019b0a3dd13707aa96e0701bf9240c6525 -size 59809 +oid sha256:10bce6df962a5198f019f509b1cdb55e28195587de4b74742ddc29833f1333a4 +size 69216 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png index 3974618f39..b0cd5bc6d4 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:718d5c9f24c114f3bf88e000b637ea19598d7bb9b97001ba137fc12a6a1285e4 -size 52608 +oid sha256:5e900ff75d02c9db4200e2032444dd7e2f8635973cca3389dd96d7d2bb734061 +size 50757 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png index 7261c9b671..952751a962 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cf3d727da78b39ecf24d4df7db0884af9008db67cc92274849c1b0978f87726 -size 59252 +oid sha256:ed13a2778b8ced86ee41d5460d274b42b714a87f59749e3b26bf75114ef40dd9 +size 58265 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png index 616c06bf46..a68f886f73 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ce2519906f1aaf34700ca9ba4bdeaa72030f1f5aff3f7e24352a1435ac67d98 -size 53966 +oid sha256:e5a2e306087a967849f61872b56a83f194d1a76df5723f0bbae6e6e2f4a98c63 +size 55725 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png index 5c597300e4..73b8b8e50c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21aee17ee9a292bb2f0d3d2ec2db565ebb8d5df753454a5a002f033ed5ed39c5 -size 3853 +oid sha256:e8a3ad99763929d2783147814e44fa9ec7e7efb7b99949e3580f23c38fb5d971 +size 9398 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png index d47bf90664..a87d89c701 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:520cae2544153ba45f8fe4b021286c2d22da06aa1b60be7f1b13879723ff994c -size 3664 +oid sha256:0f3a0cb5d9bae6725a1c527f3d9575aabf85d3edf63a28d9faa23f4f0b65e2d0 +size 7624 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png index 52fd9b9693..fedead0fa8 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58b9491aaa5554b89d86c62a5c9c44975367538c6baa5562f8dc5768f76d8264 -size 8705 +oid sha256:4811667dec0f32accc5e2c8a5dd5c0b34017fcff8d1e0376ee7a4708426bca0f +size 9838 From 77ae52513c0669b5741ab7d0ca66bbec57857f8b Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 11 Aug 2026 08:18:52 +0000 Subject: [PATCH 100/338] Update screenshots --- ...components_TimelineItemEventRowWithReplyOther_Day_1_en.png | 4 ++-- ...mponents_TimelineItemEventRowWithReplyOther_Night_1_en.png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png index 40fa5d563f..5400a98a50 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ff97eb99daf65f74b469d51b93bd77d304d0c780b9406e1c4d7893b71e3c68d -size 367163 +oid sha256:40a23ddff1fe46a486a5d34d94e0e67ec14d0b2b9c6bee2ddbadc17ce0948b7b +size 367131 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png index 2348755afd..c3b6a086fe 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e907f67ef884edb61df187f0f229ed4ff4cb31f8c276f807698f861c0e7a4c27 -size 364124 +oid sha256:0e615c14cd23f3efd217ec20ce05ed32be76dc3317091e8221f1a72a5a594d92 +size 364175 From 9a5aab2d73e67cc8d2a966fb9667395ebcba7071 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:52:02 +0200 Subject: [PATCH 101/338] Merge pull request #7384 from element-hq/renovate/gradle-actions-6.x Update gradle/actions action to v6.3.0 --- .github/workflows/build.yml | 2 +- .github/workflows/build_enterprise.yml | 2 +- .github/workflows/generate_github_pages.yml | 2 +- .github/workflows/maestro-local.yml | 2 +- .github/workflows/nightlyReports.yml | 4 ++-- .github/workflows/quality.yml | 12 ++++++------ .github/workflows/recordScreenshots.yml | 2 +- .github/workflows/release.yml | 6 +++--- .github/workflows/sonar.yml | 2 +- .github/workflows/sync-localazy.yml | 2 +- .github/workflows/tests.yml | 2 +- 11 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 252e365dce..48f67099ea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,7 +56,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug APKs diff --git a/.github/workflows/build_enterprise.yml b/.github/workflows/build_enterprise.yml index 58db7a7c84..7560c36a06 100644 --- a/.github/workflows/build_enterprise.yml +++ b/.github/workflows/build_enterprise.yml @@ -61,7 +61,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug Gplay Enterprise APK diff --git a/.github/workflows/generate_github_pages.yml b/.github/workflows/generate_github_pages.yml index 7b5271aeaf..300058ec87 100644 --- a/.github/workflows/generate_github_pages.yml +++ b/.github/workflows/generate_github_pages.yml @@ -24,7 +24,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Set up Python 3.12 diff --git a/.github/workflows/maestro-local.yml b/.github/workflows/maestro-local.yml index 6b60aaab04..e2d37aadb9 100644 --- a/.github/workflows/maestro-local.yml +++ b/.github/workflows/maestro-local.yml @@ -50,7 +50,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug APK diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml index 14fbf6a98b..929dd6680a 100644 --- a/.github/workflows/nightlyReports.yml +++ b/.github/workflows/nightlyReports.yml @@ -43,7 +43,7 @@ jobs: java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: false @@ -85,7 +85,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Dependency analysis diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 1fa9c9c46f..14e5ee7a74 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -74,7 +74,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Set up Python 3.12 @@ -113,7 +113,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run Konsist tests @@ -154,7 +154,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run compose tests @@ -188,7 +188,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Build Gplay Debug @@ -233,7 +233,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run Detekt @@ -274,7 +274,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run Ktlint check diff --git a/.github/workflows/recordScreenshots.yml b/.github/workflows/recordScreenshots.yml index bba96afc6c..44082bd347 100644 --- a/.github/workflows/recordScreenshots.yml +++ b/.github/workflows/recordScreenshots.yml @@ -60,7 +60,7 @@ jobs: java-version: '21' # Add gradle cache, this should speed up the process - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Record screenshots diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0099f52b92..b9d8be16c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 - name: Create app bundle env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} @@ -87,7 +87,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 - name: Create Enterprise app bundle env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} @@ -131,7 +131,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 - name: Create APKs env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index a18588ed69..26e24f3bd4 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -50,7 +50,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Build debug code and test fixtures diff --git a/.github/workflows/sync-localazy.yml b/.github/workflows/sync-localazy.yml index 2d4d2cb904..939354f12f 100644 --- a/.github/workflows/sync-localazy.yml +++ b/.github/workflows/sync-localazy.yml @@ -22,7 +22,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Set up Python 3.12 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9638ed03dc..d558baf73a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -68,7 +68,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} From 7438a0d3787d1c1efd496148ce385a2620544518 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:52:27 +0200 Subject: [PATCH 102/338] Update plugin dependencycheck to v13 (#7386) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d7349f35fd..9dbb2dbd5f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -270,7 +270,7 @@ metro = { id = "dev.zacsweers.metro", version.ref = "metro" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } ktlint = "org.jlleitschuh.gradle.ktlint:14.2.0" dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12" -dependencycheck = "org.owasp.dependencycheck:12.2.2" +dependencycheck = "org.owasp.dependencycheck:13.0.0" dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyAnalysis" } paparazzi = "app.cash.paparazzi:2.0.0-alpha05" roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } From 59758a20a55a603369b00ebbb4e77b90f387d5b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:12:45 +0200 Subject: [PATCH 103/338] Add `RemoteEnterpriseConfig` typealias for `ElementWellKnown` In the future, we want to move the enterprise config to the private Element Pro repo, this is a first step --- .../api/remoteconfig/RemoteEnterpriseConfig.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfig.kt diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfig.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfig.kt new file mode 100644 index 0000000000..c9adf21eec --- /dev/null +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfig.kt @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.enterprise.api.remoteconfig + +import io.element.android.libraries.wellknown.api.ElementWellKnown + +/** + * Typealias for the well-known configuration used for enterprise features. + * + * This will allow for easier refactoring in the future when we'll want to move as much of its logic as possible to this module. + */ +typealias RemoteEnterpriseConfig = ElementWellKnown From 49abaf5a5ce717f08137e82e9cf5b8524ba587fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:13:21 +0200 Subject: [PATCH 104/338] Create `EnterpriseRemoteConfigSource` enum This allows us to decide where to get the remote enterprise config from. --- .../api/EnterpriseRemoteConfigSource.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/EnterpriseRemoteConfigSource.kt diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/EnterpriseRemoteConfigSource.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/EnterpriseRemoteConfigSource.kt new file mode 100644 index 0000000000..147f31269d --- /dev/null +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/EnterpriseRemoteConfigSource.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.wellknown.api + +/** + * The source from which the enterprise remote configuration is retrieved. + */ +enum class EnterpriseRemoteConfigSource { + /** + * Retrieve the enterprise remote configuration from the Element Server Suite configuration (ESS_CONFIG) endpoint. + */ + ESS_CONFIG, + + /** + * Retrieve the enterprise remote configuration from the Element server's well-known endpoint. + */ + WELLKNOWN_ENDPOINT, +} From c8e43bdad60d26a201f99d013857ffdf4a1892ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:15:08 +0200 Subject: [PATCH 105/338] Create `GetUrlResolver` interface Implementations should just perform a GET request and return the raw body as bytes --- .../libraries/matrix/api/GetUrlResolver.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/GetUrlResolver.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/GetUrlResolver.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/GetUrlResolver.kt new file mode 100644 index 0000000000..085aeff02f --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/GetUrlResolver.kt @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api + +fun interface GetUrlResolver { + /** + * Execute generic GET requests through the SDKs internal HTTP client. + */ + suspend fun getUrl(url: String): Result +} From fd3c6713013baeeed61aab3492c61308f711ff54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:15:19 +0200 Subject: [PATCH 106/338] Create `RemoteEnterpriseConfigProvider` wrapper For Pro, this will check different remote config sources and return the result. For FOSS, this will always return a `NotFound` result --- .../RemoteEnterpriseConfigProvider.kt | 23 ++++++++++++++ .../DefaultRemoteEnterpriseConfigProvider.kt | 30 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt create mode 100644 features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt new file mode 100644 index 0000000000..724508bb49 --- /dev/null +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.enterprise.api.remoteconfig + +import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.wellknown.api.WellknownRetrieverResult + +/** + * Wrapper to fetch the remote enterprise configuration for a given [SessionId], either from a local cache or from a remote source. + */ +interface RemoteEnterpriseConfigProvider { + suspend fun get(sessionId: SessionId): WellknownRetrieverResult + + interface Factory { + fun create(getUrlResolver: GetUrlResolver): RemoteEnterpriseConfigProvider + } +} diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt new file mode 100644 index 0000000000..e585885bdd --- /dev/null +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.enterprise.impl.remoteconfig + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfig +import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider +import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.wellknown.api.WellknownRetrieverResult + +@ContributesBinding(AppScope::class) +class DefaultRemoteEnterpriseConfigProvider : RemoteEnterpriseConfigProvider { + override suspend fun get(sessionId: SessionId): WellknownRetrieverResult { + return WellknownRetrieverResult.NotFound + } + + @ContributesBinding(AppScope::class) + class Factory : RemoteEnterpriseConfigProvider.Factory { + override fun create(getUrlResolver: GetUrlResolver): RemoteEnterpriseConfigProvider { + return DefaultRemoteEnterpriseConfigProvider() + } + } +} From d48f5bd8b9c34b2bf1a58af90272203969ce342d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:16:35 +0200 Subject: [PATCH 107/338] Have `MatrixClient` implement `GetUrlResolver` --- .../element/android/libraries/matrix/api/MatrixClient.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 9ea7d7fcf1..a17859ac78 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -54,7 +54,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import java.util.Optional -interface MatrixClient { +interface MatrixClient : GetUrlResolver { val sessionId: SessionId val deviceId: DeviceId @@ -173,11 +173,6 @@ interface MatrixClient { */ fun userIdServerName(): String - /** - * Execute generic GET requests through the SDKs internal HTTP client. - */ - suspend fun getUrl(url: String): Result - /** * Get a room preview for a given room ID or alias. This is especially useful for rooms that the user is not a member of, or hasn't joined yet. */ From 3f19100427cc44fa321dded35d9afdb768acc70c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:19:41 +0200 Subject: [PATCH 108/338] Add `TemporaryMatrixClient` interface This is an abstraction over an actual unauthenticated `Client` from the SDK that implements `GetUrlResolver`, and is supposed to be short-lived. With this, we can use the SDK network stack to perform arbitrary GET url operations. The `AutoCloseable` implementation makes it easier to clean up any leftover local config after using this temporary client. --- .../matrix/api/TemporaryMatrixClient.kt | 18 ++++++++++ .../api/TemporaryMatrixClientFactory.kt | 15 ++++++++ .../matrix/impl/RustTemporaryMatrixClient.kt | 36 +++++++++++++++++++ .../impl/RustTemporaryMatrixClientFactory.kt | 36 +++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClient.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClient.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactory.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClient.kt new file mode 100644 index 0000000000..918b6237a0 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClient.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api + +import java.lang.AutoCloseable + +/** + * A Matrix client in an 'unauthenticated' state that can still be used to resolve URLs. + * + * Note: this client implements an [AutoCloseable] interface, so using [AutoCloseable.close] or [AutoCloseable.use] will clean up any resources associated + * with it, such as temporary files. + */ +interface TemporaryMatrixClient : GetUrlResolver, AutoCloseable diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt new file mode 100644 index 0000000000..605401af85 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api + +/** + * Factory to create a temporary [TemporaryMatrixClient] for a given home server URL. + */ +interface TemporaryMatrixClientFactory { + suspend fun createTemporaryMatrixClient(homeServerUrl: String): Result +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClient.kt new file mode 100644 index 0000000000..54e4f696d3 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClient.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl + +import io.element.android.libraries.core.extensions.mapFailure +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.matrix.api.TemporaryMatrixClient +import io.element.android.libraries.matrix.api.paths.SessionPaths +import io.element.android.libraries.matrix.impl.exception.mapClientException +import org.matrix.rustcomponents.sdk.Client + +class RustTemporaryMatrixClient( + private val client: Client, + private val paths: SessionPaths?, +) : TemporaryMatrixClient { + override suspend fun getUrl(url: String): Result = runCatchingExceptions { + client.getUrl(url) + }.mapFailure { it.mapClientException() } + + override fun close() { + client.close() + paths?.let { + if (it.fileDirectory.exists()) { + it.fileDirectory.deleteRecursively() + } + if (it.cacheDirectory.exists()) { + it.cacheDirectory.deleteRecursively() + } + } + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactory.kt new file mode 100644 index 0000000000..e2a55c2b8c --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactory.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory +import io.element.android.libraries.matrix.api.TemporaryMatrixClient +import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory + +@ContributesBinding(AppScope::class) +class RustTemporaryMatrixClientFactory( + private val sessionPathsFactory: SessionPathsFactory, + private val rustMatrixClientFactory: RustMatrixClientFactory, +) : TemporaryMatrixClientFactory { + override suspend fun createTemporaryMatrixClient(homeServerUrl: String): Result { + return runCatchingExceptions { + val sessionPaths = sessionPathsFactory.create() + val client = rustMatrixClientFactory.getBaseClientBuilder( + sessionPaths = sessionPaths, + clientSecret = null, + slidingSyncType = ClientBuilderSlidingSync.Native, + isMessageSearchAvailable = false, + ) + .homeserverUrl(homeServerUrl) + .build() + RustTemporaryMatrixClient(client, sessionPaths) + } + } +} From 25eca29cd2bc83656697ef6eb89f2a5bf2218f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:22:39 +0200 Subject: [PATCH 109/338] Rework `WellknownRetriever` so it uses the `GetUrlResolver` Also, allow instantiating it using a factory: with this we can always decide where to take the config from and how to do it Do the same for `ElementWellknownStore` so we can store and restore the values for different sources (endpoints) independently --- libraries/wellknown/api/build.gradle.kts | 4 + .../wellknown/api/ElementWellknownStore.kt | 4 + .../wellknown/api/WellknownRetriever.kt | 11 +- .../wellknown/impl/DefaultWellKnownStore.kt | 18 ++- .../impl/DefaultWellknownRetriever.kt | 141 +++++++++++++----- 5 files changed, 137 insertions(+), 41 deletions(-) diff --git a/libraries/wellknown/api/build.gradle.kts b/libraries/wellknown/api/build.gradle.kts index 768214d874..54da0d1445 100644 --- a/libraries/wellknown/api/build.gradle.kts +++ b/libraries/wellknown/api/build.gradle.kts @@ -12,3 +12,7 @@ plugins { android { namespace = "io.element.android.libraries.wellknown.api" } + +dependencies { + implementation(projects.libraries.matrix.api) +} diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt index 80b2ab4c5e..abb1cf53c7 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt @@ -11,4 +11,8 @@ interface ElementWellknownStore { suspend fun get(domain: String): WellknownRetrieverResult suspend fun update(domain: String, wellknown: String): Result suspend fun delete(domain: String): Result + + fun interface Factory { + fun create(prefix: String?): ElementWellknownStore + } } diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt index f7b73de8e5..830cf4bd4e 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt @@ -8,6 +8,15 @@ package io.element.android.libraries.wellknown.api +import io.element.android.libraries.matrix.api.GetUrlResolver + interface WellknownRetriever { - suspend fun getElementWellKnown(baseUrl: String): WellknownRetrieverResult + suspend fun getElementWellKnown( + host: String, + source: EnterpriseRemoteConfigSource + ): WellknownRetrieverResult + + interface Factory { + fun create(getUrlResolver: GetUrlResolver): WellknownRetriever + } } diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt index 50c8caeafc..4d58e70db4 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt @@ -8,6 +8,9 @@ package io.element.android.libraries.wellknown.impl import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.cachestore.api.CacheData import io.element.android.libraries.cachestore.api.CacheStore @@ -18,12 +21,19 @@ import io.element.android.libraries.wellknown.api.ElementWellknownStore import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.services.toolbox.api.systemclock.SystemClock -@ContributesBinding(AppScope::class) +@AssistedInject class DefaultWellKnownStore( + @Assisted + private val prefix: String?, private val cacheStore: CacheStore, private val elementWellKnownParser: ElementWellKnownParser, private val systemClock: SystemClock, ) : ElementWellknownStore { + @AssistedFactory + @ContributesBinding(AppScope::class) + fun interface Factory : ElementWellknownStore.Factory { + override fun create(prefix: String?): DefaultWellKnownStore + } override suspend fun get(domain: String): WellknownRetrieverResult { return runCatchingExceptions { val cachedData = cacheStore.getData(key(domain)) @@ -54,7 +64,11 @@ class DefaultWellKnownStore( } } - private fun key(domain: String): String = "https://$domain/.well-known/element/element.json" + private fun key(domain: String): String = if (prefix != null) { + "$prefix-$domain" + } else { + "https://$domain/.well-known/element/element.json" + } companion object { // 1 day diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt index 10f668cfb0..df72eddb25 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt @@ -9,65 +9,130 @@ package io.element.android.libraries.wellknown.impl import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.libraries.androidutils.json.JsonProvider -import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.core.uri.ensureProtocol -import io.element.android.libraries.network.RetrofitFactory +import io.element.android.libraries.di.annotations.AppCoroutineScope +import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.wellknown.api.ElementWellKnown +import io.element.android.libraries.wellknown.api.ElementWellKnownParser +import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource import io.element.android.libraries.wellknown.api.ElementWellknownStore import io.element.android.libraries.wellknown.api.WellknownRetriever import io.element.android.libraries.wellknown.api.WellknownRetrieverResult -import retrofit2.HttpException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import timber.log.Timber -import java.net.HttpURLConnection import java.net.URL -@ContributesBinding(AppScope::class) +@AssistedInject class DefaultWellknownRetriever( - private val retrofitFactory: RetrofitFactory, - private val elementWellknownStore: ElementWellknownStore, - private val jsonProvider: JsonProvider, + private val elementWellknownStoreFactory: ElementWellknownStore.Factory, private val enterpriseService: EnterpriseService, + private val elementWellKnownParser: ElementWellKnownParser, + @Assisted private val getUrlResolver: GetUrlResolver, + @AppCoroutineScope private val coroutineScope: CoroutineScope, ) : WellknownRetriever { - override suspend fun getElementWellKnown(baseUrl: String): WellknownRetrieverResult { + @ContributesBinding(AppScope::class) + @AssistedFactory + fun interface Factory : WellknownRetriever.Factory { + override fun create(getUrlResolver: GetUrlResolver): DefaultWellknownRetriever + } + + override suspend fun getElementWellKnown( + host: String, + source: EnterpriseRemoteConfigSource + ): WellknownRetrieverResult { val overriddenElementWellKnown = enterpriseService.overriddenElementWellKnown() if (overriddenElementWellKnown != null) { return WellknownRetrieverResult.Success(overriddenElementWellKnown) } - val domain = URL(baseUrl).host - return buildWellknownApi(baseUrl) - .map { wellknownApi -> - try { - val json = jsonProvider() - val result = wellknownApi.getElementWellKnown() - elementWellknownStore.update(domain, json.encodeToString(result)) - WellknownRetrieverResult.Success(result.map()) - } catch (e: Exception) { - // Is it a 404? - Timber.e(e, "Failed to retrieve Element well-known data for $baseUrl") - if ((e as? HttpException)?.code() == HttpURLConnection.HTTP_NOT_FOUND) { - WellknownRetrieverResult.NotFound - } else { - WellknownRetrieverResult.Error(e) - } - } + val checkedHost = URL(host.ensureProtocol()).host ?: host + + // We instantiate different stores for different sources, so that we can cache them separately. + // The ESS_CONFIG source is cached with a prefix to differentiate it from the WELLKNOWN_ENDPOINT. + val prefix = when (source) { + EnterpriseRemoteConfigSource.ESS_CONFIG -> "ess_config" + EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT -> null + } + val store = elementWellknownStoreFactory.create(prefix) + return when (val cacheData = store.get(checkedHost)) { + is WellknownRetrieverResult.Success -> { + Timber.d("Using cached well-known for domain $checkedHost") + fetchElementWellKnown(checkedHost, source, store) + cacheData } - .fold( - onSuccess = { it }, - onFailure = { WellknownRetrieverResult.Error(it as Exception) } - ) + is WellknownRetrieverResult.Outdated -> { + // Return the outdated data but refresh in the background + // If the cache is missing or outdated, trigger a refresh in background but still return the cached value + Timber.d("Outdated cached well-known for domain $checkedHost, returning existing value and fetching new one from network") + coroutineScope.launch { fetchElementWellKnown(checkedHost, source, store) } + cacheData + } + is WellknownRetrieverResult.NotFound -> { + // Try to fetch from the server + Timber.d("No cached well-known for domain $checkedHost, fetching from network") + fetchElementWellKnown(checkedHost, source, store) + } + is WellknownRetrieverResult.Error -> { + // Return the error + Timber.e(cacheData.exception, "Error retrieving well-known for domain $checkedHost") + cacheData.exception.toWellknownRetrieverResult() + } + } } - private fun buildWellknownApi(accountProviderUrl: String): Result { - return runCatchingExceptions { - retrofitFactory.create(accountProviderUrl.ensureProtocol()) - .create(WellknownAPI::class.java) - }.onFailure { e -> - // If the base URL is not valid, we cannot retrieve the well-known data - Timber.e(e, "Failed to create Retrofit instance for $accountProviderUrl") + private suspend fun fetchElementWellKnown( + host: String, + source: EnterpriseRemoteConfigSource, + store: ElementWellknownStore + ): WellknownRetrieverResult { + return getUrlResolver + .getUrl(url(host, source)) + .mapCatchingExceptions { + val data = String(it) + val parsed = elementWellKnownParser.parse(data).getOrThrow() + // Also store in cache, if valid + store.update(host, data) + .onFailure { exception -> + Timber.e(exception, "Failed to parse cached Element .well-known data for $host, deleting cache") + store.delete(host) + } + parsed + } + .toWellknownRetrieverResult(host) + } + + private fun Result.toWellknownRetrieverResult(host: String): WellknownRetrieverResult = fold( + onSuccess = { + WellknownRetrieverResult.Success(it) + }, + onFailure = { + Timber.e(it, "Failed to retrieve Element .well-known from $host") + // This check on message value is not ideal but this is what we got from the SDK. + it.toWellknownRetrieverResult() + } + ) + + private fun Throwable.toWellknownRetrieverResult(): WellknownRetrieverResult { + return if ((this as? ClientException.Generic)?.message?.contains("404") == true) { + WellknownRetrieverResult.NotFound + } else { + WellknownRetrieverResult.Error(this as Exception) + } + } + + private fun url(host: String, source: EnterpriseRemoteConfigSource): String { + val elementWellKnownUrl = "https://$host/.well-known/element/element.json" + return when (source) { + EnterpriseRemoteConfigSource.ESS_CONFIG -> enterpriseService.essConfigEndpointUrl(host) ?: elementWellKnownUrl + EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT -> elementWellKnownUrl } } } From 90d4fbe0bd5ba3fa68460d44d7ff6168a2947d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:26:10 +0200 Subject: [PATCH 110/338] Apply the `GetUrlResolver` pattern using unauthenticated matrix clients where possible --- .../enterprise/api/EnterpriseService.kt | 8 ++++- .../impl/DefaultEnterpriseService.kt | 5 ++- .../DefaultContentScannerUrlProvider.kt | 13 +++++-- .../DefaultAccountProviderAccessControl.kt | 29 ++++++++++------ .../WebClientUrlForAuthenticationRetriever.kt | 34 ++++++++++++------- .../api/scanner/ContentScannerUrlProvider.kt | 11 ++++-- .../matrix/impl/RustMatrixClientFactory.kt | 26 +++++++------- .../auth/RustMatrixAuthenticationService.kt | 20 +++++++---- 8 files changed, 97 insertions(+), 49 deletions(-) rename {libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix => features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise}/impl/scanner/DefaultContentScannerUrlProvider.kt (51%) diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt index 7b595966d4..f2923c2b40 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt @@ -10,6 +10,7 @@ package io.element.android.features.enterprise.api import androidx.compose.ui.graphics.Color import io.element.android.compound.colors.SemanticColorsLightDark +import io.element.android.libraries.matrix.api.GetUrlResolver import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.wellknown.api.ElementWellKnown import kotlinx.coroutines.flow.Flow @@ -17,7 +18,7 @@ import kotlinx.coroutines.flow.Flow interface EnterpriseService { val isEnterpriseBuild: Boolean suspend fun isEnterpriseUser(sessionId: SessionId): Boolean - suspend fun tweakMasUrl(url: String, homeserver: String): String + suspend fun tweakMasUrl(url: String, homeserver: String, getUrlResolver: GetUrlResolver): String fun defaultHomeserverList(): List suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean @@ -47,6 +48,11 @@ interface EnterpriseService { */ fun overriddenElementWellKnown(): ElementWellKnown? + /** + * Gets the Element Server Suite (ESS) config endpoint URL for the given domain. + */ + fun essConfigEndpointUrl(domain: String): String? + companion object { const val ANY_ACCOUNT_PROVIDER = "*" } diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt index bc2a3f1bf1..567f598c22 100644 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt @@ -14,6 +14,7 @@ import dev.zacsweers.metro.ContributesBinding import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.libraries.matrix.api.GetUrlResolver import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.wellknown.api.ElementWellKnown import kotlinx.coroutines.flow.Flow @@ -24,7 +25,7 @@ class DefaultEnterpriseService : EnterpriseService { override val isEnterpriseBuild = false override suspend fun isEnterpriseUser(sessionId: SessionId) = false - override suspend fun tweakMasUrl(url: String, homeserver: String) = url + override suspend fun tweakMasUrl(url: String, homeserver: String, getUrlResolver: GetUrlResolver) = url override fun defaultHomeserverList(): List = emptyList() override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true @@ -48,4 +49,6 @@ class DefaultEnterpriseService : EnterpriseService { override fun getNoisyNotificationChannelId(sessionId: SessionId): String? = null override fun overriddenElementWellKnown(): ElementWellKnown? = null + + override fun essConfigEndpointUrl(domain: String): String? = null } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/scanner/DefaultContentScannerUrlProvider.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/scanner/DefaultContentScannerUrlProvider.kt similarity index 51% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/scanner/DefaultContentScannerUrlProvider.kt rename to features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/scanner/DefaultContentScannerUrlProvider.kt index b43dcfb3e8..edd1514929 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/scanner/DefaultContentScannerUrlProvider.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/scanner/DefaultContentScannerUrlProvider.kt @@ -5,10 +5,12 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.impl.scanner +package io.element.android.features.enterprise.impl.scanner import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.scanner.ContentScannerUrlProvider /** @@ -16,5 +18,12 @@ import io.element.android.libraries.matrix.api.scanner.ContentScannerUrlProvider */ @ContributesBinding(AppScope::class) class DefaultContentScannerUrlProvider : ContentScannerUrlProvider { - override suspend fun getContentScannerUrl(homeserver: String): Result = Result.success(null) + override suspend fun getContentScannerUrl(sessionId: SessionId): Result = Result.success(null) + + @ContributesBinding(AppScope::class) + class Factory : ContentScannerUrlProvider.Factory { + override fun create(getUrlResolver: GetUrlResolver): ContentScannerUrlProvider { + return DefaultContentScannerUrlProvider() + } + } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt index db56047e15..854b4293f1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt @@ -13,13 +13,15 @@ import dev.zacsweers.metro.ContributesBinding import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.login.api.accesscontrol.AccountProviderAccessControl import io.element.android.features.login.impl.changeserver.AccountProviderAccessException -import io.element.android.libraries.core.uri.ensureProtocol +import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory +import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource import io.element.android.libraries.wellknown.api.WellknownRetriever @ContributesBinding(AppScope::class) class DefaultAccountProviderAccessControl( private val enterpriseService: EnterpriseService, - private val wellknownRetriever: WellknownRetriever, + private val wellknownRetrieverFactory: WellknownRetriever.Factory, + private val temporaryMatrixClientFactory: TemporaryMatrixClientFactory, ) : AccountProviderAccessControl { override suspend fun isAllowedToConnectToAccountProvider(accountProviderUrl: String) = try { assertIsAllowedToConnectToAccountProvider( @@ -38,14 +40,21 @@ class DefaultAccountProviderAccessControl( ) { if (enterpriseService.isEnterpriseBuild.not()) { // Ensure that Element Pro is not required for this account provider - val wellKnown = wellknownRetriever.getElementWellKnown( - baseUrl = accountProviderUrl.ensureProtocol(), - ).dataOrNull() - if (wellKnown?.enforceElementPro == true) { - throw AccountProviderAccessException.NeedElementProException( - unauthorisedAccountProviderTitle = title, - applicationId = ELEMENT_PRO_APPLICATION_ID, - ) + val temporaryMatrixClient = temporaryMatrixClientFactory.createTemporaryMatrixClient(accountProviderUrl).getOrThrow() + temporaryMatrixClient.use { + val wellknownRetriever = wellknownRetrieverFactory.create(temporaryMatrixClient) + val wellKnown = wellknownRetriever.getElementWellKnown( + host = accountProviderUrl, + // It's expected this is hardcoded to the well-known endpoint + source = EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT, + ).dataOrNull() + + if (wellKnown?.enforceElementPro == true) { + throw AccountProviderAccessException.NeedElementProException( + unauthorisedAccountProviderTitle = title, + applicationId = ELEMENT_PRO_APPLICATION_ID, + ) + } } } if (enterpriseService.isAllowedToConnectToHomeserver(accountProviderUrl).not()) { diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt index df08cf468b..f9dcd83c24 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt @@ -13,6 +13,8 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported +import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory +import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource import io.element.android.libraries.wellknown.api.WellknownRetriever import timber.log.Timber @@ -22,24 +24,32 @@ interface WebClientUrlForAuthenticationRetriever { @ContributesBinding(AppScope::class) class DefaultWebClientUrlForAuthenticationRetriever( - private val wellknownRetriever: WellknownRetriever, + private val wellknownRetrieverFactory: WellknownRetriever.Factory, + private val temporaryMatrixClientFactory: TemporaryMatrixClientFactory, ) : WebClientUrlForAuthenticationRetriever { override suspend fun retrieve(homeServerUrl: String): String { if (homeServerUrl != AuthenticationConfig.MATRIX_ORG_URL) { Timber.w("Temporary account creation flow is only supported on matrix.org") throw AccountCreationNotSupported() } - val wellknown = wellknownRetriever.getElementWellKnown(homeServerUrl).dataOrNull() - ?: throw AccountCreationNotSupported() - val registrationHelperUrl = wellknown.registrationHelperUrl - return if (registrationHelperUrl != null) { - registrationHelperUrl.toUri() - .buildUpon() - .appendQueryParameter("hs_url", homeServerUrl) - .build() - .toString() - } else { - throw AccountCreationNotSupported() + val temporaryMatrixClient = temporaryMatrixClientFactory.createTemporaryMatrixClient(homeServerUrl).getOrThrow() + return temporaryMatrixClient.use { + val wellknownRetriever = wellknownRetrieverFactory.create(temporaryMatrixClient) + val wellknown = wellknownRetriever.getElementWellKnown( + host = homeServerUrl, + source = EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT + ).dataOrNull() ?: throw AccountCreationNotSupported() + + val registrationHelperUrl = wellknown.registrationHelperUrl + if (registrationHelperUrl != null) { + registrationHelperUrl.toUri() + .buildUpon() + .appendQueryParameter("hs_url", homeServerUrl) + .build() + .toString() + } else { + throw AccountCreationNotSupported() + } } } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt index 600866f586..32794bff7b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt @@ -7,12 +7,19 @@ package io.element.android.libraries.matrix.api.scanner +import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.core.SessionId + /** * Provides the URL of the content scanner service for a given homeserver, if any is set up. */ fun interface ContentScannerUrlProvider { /** - * Returns the URL of the content scanner service for the given [homeserver], or `null` if no content scanner is set up. + * Returns the URL of the content scanner service for the given [sessionId], or `null` if no content scanner is set up. */ - suspend fun getContentScannerUrl(homeserver: String): Result + suspend fun getContentScannerUrl(sessionId: SessionId): Result + + fun interface Factory { + fun create(getUrlResolver: GetUrlResolver): ContentScannerUrlProvider + } } 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 d4b9063931..a0b06a2e1e 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 @@ -17,7 +17,7 @@ import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.paths.SessionPaths import io.element.android.libraries.matrix.api.scanner.ContentScannerUrlProvider import io.element.android.libraries.matrix.impl.analytics.UtdTracker @@ -70,7 +70,7 @@ class RustMatrixClientFactory( private val clientBuilderProvider: ClientBuilderProvider, private val sqliteStoreBuilderProvider: SqliteStoreBuilderProvider, private val workManagerScheduler: WorkManagerScheduler, - private val contentScannerUrlProvider: ContentScannerUrlProvider, + private val contentScannerUrlProviderFactory: ContentScannerUrlProvider.Factory, ) { private val sessionDelegate = RustClientSessionDelegate( sessionStore = sessionStore, @@ -135,21 +135,19 @@ class RustMatrixClientFactory( client.setUtdDelegate(UtdTracker(analyticsService)) - val domainName = UserId(client.userId()).domainName // If a content scanner URL is available for the homeserver, create a RustContentScanner and set it on the client. // This allows the SDK to use the content scanner for automatic media scanning. // If no content scanner URL is available, the contentScanner will be null. - val contentScanner = domainName?.let { - contentScannerUrlProvider.getContentScannerUrl(domainName) - .getOrNull() - ?.let { contentScannerUrl -> - val contentScanner = ContentScanner(contentScannerUrl) - client.setContentScanner(contentScanner) - RustContentScanner( - client = client, - rustScanner = contentScanner, - ) - } + val contentScannerUrlProvider = contentScannerUrlProviderFactory.create(RustTemporaryMatrixClient(client, null)) + val contentScanner = contentScannerUrlProvider.getContentScannerUrl(SessionId(client.userId())) + .getOrNull() + ?.let { contentScannerUrl -> + val contentScanner = ContentScanner(contentScannerUrl) + client.setContentScanner(contentScanner) + RustContentScanner( + client = client, + rustScanner = contentScanner, + ) } val syncService = client.syncService() 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 dd9f73c381..bef83c35ff 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 @@ -35,6 +35,7 @@ import io.element.android.libraries.matrix.api.paths.SessionPaths import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync import io.element.android.libraries.matrix.impl.RustMatrixClientFactory +import io.element.android.libraries.matrix.impl.RustTemporaryMatrixClient 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 @@ -133,9 +134,10 @@ class RustMatrixAuthenticationService( } currentClient = client + client.homeserverLoginDetails().map() }.onFailure { - clear() + clear(destroyClient = true) }.mapFailure { failure -> Timber.e(failure, "Failed to set homeserver to $homeserver") failure.mapAuthenticationException() @@ -168,7 +170,7 @@ class RustMatrixAuthenticationService( sessionStore.addSession(sessionData) // Clean up the strong reference held here since it's no longer necessary - currentClient = null + clear(destroyClient = false) SessionId(sessionData.userId) }.mapFailure { failure -> @@ -248,7 +250,7 @@ class RustMatrixAuthenticationService( sessionStore.addSession(sessionData) // Clean up the strong reference held here since it's no longer necessary - currentClient = null + clear(destroyClient = false) SessionId(sessionData.userId) } @@ -271,11 +273,13 @@ class RustMatrixAuthenticationService( deviceId = null, additionalScopes = emptyList(), ) + val getUrlResolver = RustTemporaryMatrixClient(client, sessionPaths) val url = oAuthAuthorizationData.loginUrl() .let { enterpriseService.tweakMasUrl( url = it, homeserver = client.server() ?: client.homeserver(), + getUrlResolver = getUrlResolver, ) } pendingOAuthAuthorizationData = oAuthAuthorizationData @@ -335,7 +339,7 @@ class RustMatrixAuthenticationService( sessionStore.addSession(sessionData) // Clean up the strong reference held here since it's no longer necessary - currentClient = null + clear(destroyClient = false) SessionId(sessionData.userId) }.mapFailure { failure -> @@ -398,7 +402,7 @@ class RustMatrixAuthenticationService( sessionStore.addSession(sessionData) // Clean up the strong reference held here since it's no longer necessary - currentClient = null + clear(destroyClient = false) SessionId(sessionData.userId) }.mapFailure { @@ -464,8 +468,10 @@ class RustMatrixAuthenticationService( .build() } - private fun clear() { - currentClient?.close() + private fun clear(destroyClient: Boolean) { + if (destroyClient) { + currentClient?.close() + } currentClient = null } From ec0819ad07c27e75446ee712c577e684cbe784d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:26:22 +0200 Subject: [PATCH 111/338] Fixup geturlresolver matrix client --- .../libraries/matrix/impl/di/SessionMatrixModule.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt index e88dc382e8..8f942e1ebf 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt @@ -13,6 +13,7 @@ import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.Provides import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.annotations.SessionCoroutineScope +import io.element.android.libraries.matrix.api.GetUrlResolver import io.element.android.libraries.matrix.api.HomeserverCapabilitiesProvider import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.DeviceId @@ -31,7 +32,7 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationS import kotlinx.coroutines.CoroutineScope @BindingContainer -@ContributesTo(SessionScope::class) +@ContributesTo(SessionScope::class, replaces = [GetUrlResolver::class]) object SessionMatrixModule { @Provides fun providesSessionId(matrixClient: MatrixClient): SessionId { @@ -108,4 +109,9 @@ object SessionMatrixModule { fun providesContentScanner(matrixClient: MatrixClient): ContentScanner? { return matrixClient.contentScanner } + + @Provides + fun providesGetUrlResolverClient(matrixClient: MatrixClient): GetUrlResolver { + return matrixClient + } } From b9afbf0692d725ca08747af3d81151127b7d8e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:27:06 +0200 Subject: [PATCH 112/338] Add a session-scoped `WellknownRetriever` provider so we don't have to build it every time there --- .../wellknown/impl/SessionWellknownModule.kt | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt new file mode 100644 index 0000000000..bfd18ac7c2 --- /dev/null +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.wellknown.impl + +import dev.zacsweers.metro.BindingContainer +import dev.zacsweers.metro.ContributesTo +import dev.zacsweers.metro.Provides +import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.di.annotations.SessionCoroutineScope +import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.wellknown.api.ElementWellKnownParser +import io.element.android.libraries.wellknown.api.ElementWellknownStore +import io.element.android.libraries.wellknown.api.WellknownRetriever +import kotlinx.coroutines.CoroutineScope + +@BindingContainer +@ContributesTo(SessionScope::class) +object SessionWellknownModule { + @Provides + fun provideWellKnownRetriever( + elementWellknownStoreFactory: ElementWellknownStore.Factory, + enterpriseService: EnterpriseService, + elementWellKnownParser: ElementWellKnownParser, + getUrlResolver: GetUrlResolver, + @SessionCoroutineScope coroutineScope: CoroutineScope, + ): WellknownRetriever { + return DefaultWellknownRetriever( + elementWellknownStoreFactory = elementWellknownStoreFactory, + enterpriseService = enterpriseService, + elementWellKnownParser = elementWellKnownParser, + getUrlResolver = getUrlResolver, + coroutineScope = coroutineScope + ) + } +} From 89908c2511c84df51909acb0c34d709d2513e9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:27:41 +0200 Subject: [PATCH 113/338] Add `WellknownRetrieverResul.upToDateDataOrNull` helper function for the enterprise module --- .../libraries/wellknown/api/WellknownRetrieverResult.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetrieverResult.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetrieverResult.kt index fcc54df0b5..98af981a66 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetrieverResult.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetrieverResult.kt @@ -35,4 +35,11 @@ sealed interface WellknownRetrieverResult { is Error -> null NotFound -> null } + + fun upToDateDataOrNull(): T? = when (this) { + is Success -> data + is Outdated -> null + is Error -> null + NotFound -> null + } } From e3290707d9c7f1baaffc47115e19a2eee59ad11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:27:59 +0200 Subject: [PATCH 114/338] Remove no longer needed `SessionWellknownRetriever` --- .../api/SessionWellknownRetriever.kt | 13 - .../impl/DefaultSessionWellknownRetriever.kt | 109 ----- .../DefaultSessionWellknownRetrieverTest.kt | 376 ------------------ 3 files changed, 498 deletions(-) delete mode 100644 libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/SessionWellknownRetriever.kt delete mode 100644 libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt delete mode 100644 libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/SessionWellknownRetriever.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/SessionWellknownRetriever.kt deleted file mode 100644 index b006508fc7..0000000000 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/SessionWellknownRetriever.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.api - -interface SessionWellknownRetriever { - suspend fun getElementWellKnown(): WellknownRetrieverResult -} diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt deleted file mode 100644 index 66654dd228..0000000000 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.impl - -import dev.zacsweers.metro.ContributesBinding -import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.libraries.core.extensions.mapCatchingExceptions -import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.di.annotations.SessionCoroutineScope -import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.exception.ClientException -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.ElementWellKnownParser -import io.element.android.libraries.wellknown.api.ElementWellknownStore -import io.element.android.libraries.wellknown.api.SessionWellknownRetriever -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import timber.log.Timber - -@ContributesBinding(SessionScope::class) -class DefaultSessionWellknownRetriever( - private val matrixClient: MatrixClient, - @SessionCoroutineScope - private val sessionCoroutineScope: CoroutineScope, - private val elementWellknownStore: ElementWellknownStore, - private val elementWellKnownParser: ElementWellKnownParser, - private val enterpriseService: EnterpriseService, -) : SessionWellknownRetriever { - private val domain by lazy { matrixClient.userIdServerName() } - - override suspend fun getElementWellKnown(): WellknownRetrieverResult { - val overriddenElementWellKnown = enterpriseService.overriddenElementWellKnown() - if (overriddenElementWellKnown != null) { - return WellknownRetrieverResult.Success(overriddenElementWellKnown) - } - - val cacheData = elementWellknownStore.get(domain) - return when (cacheData) { - is WellknownRetrieverResult.Success -> { - Timber.d("Using cached well-known for domain $domain") - cacheData - } - is WellknownRetrieverResult.Outdated -> { - // Return the outdated data but refresh in the background - // If the cache is missing or outdated, trigger a refresh in background but still return the cached value - Timber.d("Outdated cached well-known for domain $domain, returning existing value and fetching new one from network") - sessionCoroutineScope.launch { - val url = "https://$domain/.well-known/element/element.json" - fetchElementWellKnown(url) - } - cacheData - } - is WellknownRetrieverResult.NotFound -> { - // Try to fetch from the server - Timber.d("No cached well-known for domain $domain, fetching from network") - val url = "https://$domain/.well-known/element/element.json" - fetchElementWellKnown(url) - } - is WellknownRetrieverResult.Error -> { - // Return the error - Timber.e(cacheData.exception, "Error retrieving well-known for domain $domain") - cacheData.exception.toWellknownRetrieverResult() - } - } - } - - private suspend fun fetchElementWellKnown(url: String): WellknownRetrieverResult { - return matrixClient - .getUrl(url) - .mapCatchingExceptions { - val data = String(it) - val parsed = elementWellKnownParser.parse(data).getOrThrow() - // Also store in cache, if valid - elementWellknownStore.update(domain, data) - .onFailure { exception -> - Timber.e(exception, "Failed to parse cached Element .well-known data for $domain, deleting cache") - elementWellknownStore.delete(domain) - } - parsed - } - .toWellknownRetrieverResult() - } - - private fun Result.toWellknownRetrieverResult(): WellknownRetrieverResult = fold( - onSuccess = { - WellknownRetrieverResult.Success(it) - }, - onFailure = { - Timber.e(it, "Failed to retrieve Element .well-known from $domain") - // This check on message value is not ideal but this is what we got from the SDK. - it.toWellknownRetrieverResult() - } - ) - - private fun Throwable.toWellknownRetrieverResult(): WellknownRetrieverResult { - return if ((this as? ClientException.Generic)?.message?.contains("404") == true) { - WellknownRetrieverResult.NotFound - } else { - WellknownRetrieverResult.Error(this as Exception) - } - } -} diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt deleted file mode 100644 index f54cf919d9..0000000000 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -@file:OptIn(ExperimentalCoroutinesApi::class) - -package io.element.android.libraries.wellknown.impl - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.enterprise.test.FakeEnterpriseService -import io.element.android.features.wellknown.test.FakeElementWellknownStore -import io.element.android.features.wellknown.test.anElementWellKnown -import io.element.android.libraries.androidutils.json.DefaultJsonProvider -import io.element.android.libraries.matrix.api.exception.ClientException -import io.element.android.libraries.matrix.test.AN_EXCEPTION -import io.element.android.libraries.matrix.test.FakeMatrixClient -import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.ElementWellKnownParser -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult -import io.element.android.tests.testutils.lambda.lambdaError -import io.element.android.tests.testutils.lambda.lambdaRecorder -import io.element.android.tests.testutils.lambda.value -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Test - -class DefaultSessionWellknownRetrieverTest { - @Test - fun `get empty element wellknown`() = runTest { - val getUrlLambda = lambdaRecorder> { - Result.success("{}".toByteArray()) - } - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = getUrlLambda, - ) - assertThat(sut.getElementWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - ElementWellKnown( - registrationHelperUrl = null, - enforceElementPro = null, - rageshakeUrl = null, - brandColor = null, - notificationSound = null, - identityProviderAppScheme = null, - customRecoveryPassphrase = null, - contentScannerUrl = null, - forceDisableE2EE = null, - ) - ) - ) - getUrlLambda.assertions().isCalledOnce() - .with(value("https://user.domain.org/.well-known/element/element.json")) - } - - @Test - fun `get element wellknown with full content`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success( - WELLKNOWN_CONTENT.toByteArray() - ) - } - ) - assertThat(sut.getElementWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - ElementWellKnown( - registrationHelperUrl = "a_registration_url", - enforceElementPro = true, - rageshakeUrl = "a_rageshake_url", - brandColor = "#FF0000", - notificationSound = "a_notification_sound.flac", - identityProviderAppScheme = "an_app_scheme", - customRecoveryPassphrase = null, - contentScannerUrl = "https://content-scanner.example.com", - forceDisableE2EE = false, - ) - ) - ) - } - - @Test - fun `get element wellknown with unknown key`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success( - """{ - "registration_helper_url": "a_registration_url", - "enforce_element_pro": true, - "rageshake_url": "a_rageshake_url", - // Note the trailing comma, and the comment! - "other": true, - }""".trimIndent().toByteArray() - ) - }, - ) - assertThat(sut.getElementWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - ElementWellKnown( - registrationHelperUrl = "a_registration_url", - enforceElementPro = true, - rageshakeUrl = "a_rageshake_url", - brandColor = null, - notificationSound = null, - identityProviderAppScheme = null, - contentScannerUrl = null, - customRecoveryPassphrase = null, - forceDisableE2EE = null, - ) - ) - ) - } - - @Test - fun `get element wellknown with custom recovery passphrase settings`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success( - """{ - "custom_recovery_passphrase": { - "min_character_count": 8 - } - }""".trimIndent().toByteArray() - ) - }, - ) - assertThat(sut.getElementWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - anElementWellKnown( - customRecoveryPassphrase = CustomRecoveryPassphrase(minCharacterCount = 8) - ) - ) - ) - } - - @Test - fun `get element wellknown with custom recovery passphrase settings missing min character count floors to 1`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success( - """{ - "custom_recovery_passphrase": {} - }""".trimIndent().toByteArray() - ) - }, - ) - assertThat(sut.getElementWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - anElementWellKnown( - customRecoveryPassphrase = CustomRecoveryPassphrase(minCharacterCount = 1) - ) - ) - ) - } - - @Test - fun `get element wellknown with zero min character count floors to 1`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success( - """{ - "custom_recovery_passphrase": { - "min_character_count": 0 - } - }""".trimIndent().toByteArray() - ) - }, - ) - assertThat(sut.getElementWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - anElementWellKnown( - customRecoveryPassphrase = CustomRecoveryPassphrase(minCharacterCount = 1) - ) - ) - ) - } - - @Test - fun `get element wellknown with negative min character count floors to 1`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success( - """{ - "custom_recovery_passphrase": { - "min_character_count": -5 - } - }""".trimIndent().toByteArray() - ) - }, - ) - assertThat(sut.getElementWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - anElementWellKnown( - customRecoveryPassphrase = CustomRecoveryPassphrase(minCharacterCount = 1) - ) - ) - ) - } - - @Test - fun `get element wellknown json error`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success( - """{ - "registration_helper_url" = "a_registration_url", - error - }""".trimIndent().toByteArray() - ) - } - ) - assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java) - } - - @Test - fun `get element wellknown network error`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.failure(AN_EXCEPTION) - } - ) - assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java) - } - - @Test - fun `get element wellknown 404 http error counts as not found`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.failure(ClientException.Generic("Not Found: 404 status code", "The file could not be found")) - } - ) - assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.NotFound::class.java) - } - - @Test - fun `get element wellknown hitting cache`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { lambdaError() }, - cacheStore = FakeElementWellknownStore( - initialData = mapOf( - WELLKNOWN_URL to WellknownRetrieverResult.Success(parsedWellKnownContent()) - ) - ) - ) - assertThat(sut.getElementWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - ElementWellKnown( - registrationHelperUrl = "a_registration_url", - enforceElementPro = true, - rageshakeUrl = "a_rageshake_url", - brandColor = "#FF0000", - notificationSound = "a_notification_sound.flac", - identityProviderAppScheme = "an_app_scheme", - customRecoveryPassphrase = null, - contentScannerUrl = "https://content-scanner.example.com", - forceDisableE2EE = false, - ) - ) - ) - } - - @Test - fun `get element wellknown hitting cache containing invalid json`() = runTest { - val cacheStore = FakeElementWellknownStore( - initialData = mapOf( - WELLKNOWN_URL to WellknownRetrieverResult.Error(IllegalStateException("Invalid JSON")) - ) - ) - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success("{}".toByteArray()) - }, - cacheStore = cacheStore, - elementWellKnownParser = { Result.failure(IllegalStateException("Failed to parse JSON")) } - ) - assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java) - // Ensure that the cache is deleted after the failure to parse it - assertThat(cacheStore.get(WELLKNOWN_URL)).isEqualTo(WellknownRetrieverResult.NotFound) - } - - @Test - fun `get element wellknown hitting outdated cache`() = runTest { - val cacheStore = FakeElementWellknownStore( - initialData = mapOf( - WELLKNOWN_URL to WellknownRetrieverResult.Outdated(parsedWellKnownContent()), - ) - ) - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success("{}".toByteArray()) - }, - cacheStore = cacheStore, - ) - assertThat(sut.getElementWellKnown()).isEqualTo( - WellknownRetrieverResult.Outdated( - ElementWellKnown( - registrationHelperUrl = "a_registration_url", - enforceElementPro = true, - rageshakeUrl = "a_rageshake_url", - brandColor = "#FF0000", - notificationSound = "a_notification_sound.flac", - identityProviderAppScheme = "an_app_scheme", - customRecoveryPassphrase = null, - contentScannerUrl = "https://content-scanner.example.com", - forceDisableE2EE = false, - ) - ) - ) - // Next call returns the updated value - runCurrent() - assertThat(sut.getElementWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - anElementWellKnown() - ) - ) - } - - @Test - fun `get element wellknown was overridden`() = runTest { - val getLambda = lambdaRecorder> { Result.failure(IllegalStateException("BOOM")) } - val wellKnown = anElementWellKnown() - - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = getLambda, - enterpriseService = FakeEnterpriseService( - overrideWellKnownResult = { wellKnown } - ) - ) - - // The overridden value is returned - assertThat(sut.getElementWellKnown()).isEqualTo( - WellknownRetrieverResult.Success(wellKnown) - ) - - // And the endpoint is never hit - getLambda.assertions().isNeverCalled() - } - - private fun parsedWellKnownContent() = DefaultJsonProvider().invoke().decodeFromString(WELLKNOWN_CONTENT).map() - - private fun TestScope.createDefaultSessionWellknownRetriever( - cacheStore: FakeElementWellknownStore = FakeElementWellknownStore(), - getUrlLambda: (String) -> Result, - elementWellKnownParser: ElementWellKnownParser = DefaultElementWellKnownParser(DefaultJsonProvider()), - enterpriseService: FakeEnterpriseService = FakeEnterpriseService(overrideWellKnownResult = { null }), - ) = DefaultSessionWellknownRetriever( - matrixClient = FakeMatrixClient( - userIdServerNameLambda = { "user.domain.org" }, - getUrlLambda = getUrlLambda, - ), - sessionCoroutineScope = backgroundScope, - elementWellknownStore = cacheStore, - elementWellKnownParser = elementWellKnownParser, - enterpriseService = enterpriseService, - ) - - companion object { - private const val WELLKNOWN_URL = "https://user.domain.org/.well-known/element/element.json" - private const val WELLKNOWN_CONTENT = """{ - "registration_helper_url": "a_registration_url", - "enforce_element_pro": true, - "rageshake_url": "a_rageshake_url", - "brand_color": "#FF0000", - "notification_sound": "a_notification_sound.flac", - "idp_app_scheme": "an_app_scheme", - "content_scanner_url": "https://content-scanner.example.com", - "force_disable_e2ee": false, - }""" - } -} From e24468c74f226034c5e38c188923f8eff3266655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 10 Aug 2026 14:28:11 +0200 Subject: [PATCH 115/338] Fix and add tests --- .../enterprise/test/FakeEnterpriseService.kt | 12 +- .../FakeRemoteEnterpriseConfigProvider.kt | 21 ++ ...keRemoteEnterpriseConfigProviderFactory.kt | 17 + ...DefaultAccountProviderAccessControlTest.kt | 21 +- .../changeserver/ChangeServerPresenterTest.kt | 17 +- .../onboarding/OnBoardingPresenterTest.kt | 14 +- .../impl/RustMatrixClientFactoryTest.kt | 4 +- .../matrix/test/FakeTemporaryMatrixClient.kt | 20 ++ .../test/FakeTemporaryMatrixClientFactory.kt | 17 + .../impl/DefaultWellKnownStoreTest.kt | 14 + .../impl/DefaultWellknownRetrieverTest.kt | 301 ++++-------------- libraries/wellknown/test/build.gradle.kts | 1 + .../test/FakeSessionWellknownRetriever.kt | 22 -- .../wellknown/test/FakeWellknownRetriever.kt | 10 +- .../test/FakeWellknownRetrieverFactory.kt | 17 + 15 files changed, 216 insertions(+), 292 deletions(-) create mode 100644 features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProvider.kt create mode 100644 features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt create mode 100644 libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClient.kt create mode 100644 libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClientFactory.kt delete mode 100644 libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeSessionWellknownRetriever.kt create mode 100644 libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt index f6379bef30..524a1fb9dd 100644 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.graphics.Color import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.libraries.matrix.api.GetUrlResolver import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.tests.testutils.lambda.lambdaError @@ -31,8 +32,9 @@ class FakeEnterpriseService( private val firebasePushGatewayResult: () -> String? = { lambdaError() }, private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() }, private val getNoisyNotificationChannelIdResult: (SessionId?) -> String? = { lambdaError() }, - private val tweakMasUrlResult: (String, String) -> String = { _, _ -> lambdaError() }, + private val tweakMasUrlResult: (String, String, GetUrlResolver) -> String = { _, _, _ -> lambdaError() }, private val overrideWellKnownResult: () -> ElementWellKnown? = { lambdaError() }, + private val essConfigEndpointUrlResult: (String) -> String = { lambdaError() }, ) : EnterpriseService { private val brandColorState = MutableStateFlow(initialBrandColor) private val semanticColorsState = MutableStateFlow(initialSemanticColors) @@ -41,8 +43,8 @@ class FakeEnterpriseService( isEnterpriseUserResult(sessionId) } - override suspend fun tweakMasUrl(url: String, homeserver: String): String = simulateLongTask { - tweakMasUrlResult(url, homeserver) + override suspend fun tweakMasUrl(url: String, homeserver: String, getUrlResolver: GetUrlResolver): String = simulateLongTask { + tweakMasUrlResult(url, homeserver, getUrlResolver) } override fun defaultHomeserverList(): List { @@ -85,4 +87,8 @@ class FakeEnterpriseService( override fun overriddenElementWellKnown(): ElementWellKnown? { return overrideWellKnownResult() } + + override fun essConfigEndpointUrl(domain: String): String { + return essConfigEndpointUrlResult(domain) + } } diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProvider.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProvider.kt new file mode 100644 index 0000000000..2c57dbb530 --- /dev/null +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProvider.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.enterprise.test.remoteconfig + +import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfig +import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.wellknown.api.WellknownRetrieverResult + +class FakeRemoteEnterpriseConfigProvider( + private val getResult: (SessionId) -> WellknownRetrieverResult = { WellknownRetrieverResult.NotFound } +) : RemoteEnterpriseConfigProvider { + override suspend fun get(sessionId: SessionId): WellknownRetrieverResult { + return getResult(sessionId) + } +} diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt new file mode 100644 index 0000000000..1ba699cfed --- /dev/null +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.enterprise.test.remoteconfig + +import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider +import io.element.android.libraries.matrix.api.GetUrlResolver + +class FakeRemoteEnterpriseConfigProviderFactory( + private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(), +) : RemoteEnterpriseConfigProvider.Factory { + override fun create(getUrlResolver: GetUrlResolver): RemoteEnterpriseConfigProvider = remoteEnterpriseConfigProvider +} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt index e3cf1c0ea7..ee4cea0532 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt @@ -12,10 +12,12 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.changeserver.AccountProviderAccessException import io.element.android.features.wellknown.test.FakeWellknownRetriever +import io.element.android.features.wellknown.test.FakeWellknownRetrieverFactory import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2 import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_URL +import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClientFactory import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import kotlinx.coroutines.test.runTest @@ -156,15 +158,18 @@ class DefaultAccountProviderAccessControlTest { isAllowedToConnectToHomeserverResult = { isAllowedToConnectToHomeserver }, defaultHomeserverListResult = { allowedAccountProviders }, ), - wellknownRetriever = FakeWellknownRetriever( - getElementWellKnownResult = { - if (elementWellKnown == null) { - WellknownRetrieverResult.NotFound - } else { - WellknownRetrieverResult.Success(elementWellKnown) - } - }, + wellknownRetrieverFactory = FakeWellknownRetrieverFactory( + wellknownRetriever = FakeWellknownRetriever( + getElementWellKnownResult = { _, _ -> + if (elementWellKnown == null) { + WellknownRetrieverResult.NotFound + } else { + WellknownRetrieverResult.Success(elementWellKnown) + } + }, + ), ), + temporaryMatrixClientFactory = FakeTemporaryMatrixClientFactory(), ) private fun DefaultAccountProviderAccessControl.expectNeedElementProException() { diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index 5d9040adac..c28fee77ad 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -17,18 +17,19 @@ import io.element.android.features.login.impl.accountprovider.AccountProviderDat import io.element.android.features.login.impl.error.ChangeServerError import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate import io.element.android.features.wellknown.test.FakeWellknownRetriever +import io.element.android.features.wellknown.test.FakeWellknownRetrieverFactory import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_HOMESERVER_URL +import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClientFactory import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionDialog import io.element.android.libraries.permissions.test.FakeLocalNetworkPermissionAdvisor import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory -import io.element.android.libraries.wellknown.api.ElementWellKnown +import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource import io.element.android.libraries.wellknown.api.WellknownRetriever import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.tests.testutils.WarmUpRule @@ -158,7 +159,8 @@ class ChangeServerPresenterTest { @Test fun `present - change server element pro required error`() = runTest { - val getElementWellKnownResult = lambdaRecorder> { + val getElementWellKnownResult = lambdaRecorder { homeserver: String, _: EnterpriseRemoteConfigSource -> + assertThat(homeserver).isEqualTo(A_HOMESERVER_URL) WellknownRetrieverResult.Success( anElementWellKnown( enforceElementPro = true, @@ -174,8 +176,6 @@ class ChangeServerPresenterTest { assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) val anAccountProvider = AccountProvider(url = A_HOMESERVER_URL) initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(anAccountProvider)) - val loadingState = awaitItem() - assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java) val failureState = awaitItem() assertThat( (failureState.changeServerAction.errorOrNull() as ChangeServerError.NeedElementPro).unauthorisedAccountProviderTitle @@ -183,9 +183,7 @@ class ChangeServerPresenterTest { assertThat( (failureState.changeServerAction.errorOrNull() as ChangeServerError.NeedElementPro).applicationId ).isEqualTo("io.element.enterprise") - getElementWellKnownResult.assertions() - .isCalledOnce() - .with(value(A_HOMESERVER_URL.ensureProtocol())) + getElementWellKnownResult.assertions().isCalledOnce() } } @@ -269,7 +267,8 @@ class ChangeServerPresenterTest { accountProviderDataSource = accountProviderDataSource, defaultAccountProviderAccessControl = DefaultAccountProviderAccessControl( enterpriseService = enterpriseService, - wellknownRetriever = wellknownRetriever, + wellknownRetrieverFactory = FakeWellknownRetrieverFactory(wellknownRetriever = wellknownRetriever), + temporaryMatrixClientFactory = FakeTemporaryMatrixClientFactory(), ), localNetworkPermissionGate = LocalNetworkPermissionGate( advisor = localNetworkPermissionAdvisor, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt index 79268aa1e4..782ff00ec0 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt @@ -20,6 +20,7 @@ import io.element.android.features.login.impl.login.LoginModePresenter import io.element.android.features.login.impl.web.FakeWebClientUrlForAuthenticationRetriever import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationRetriever import io.element.android.features.wellknown.test.FakeWellknownRetriever +import io.element.android.features.wellknown.test.FakeWellknownRetrieverFactory import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService @@ -30,11 +31,15 @@ import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.A_HOMESERVER_URL_2 import io.element.android.libraries.matrix.test.A_LOGIN_HINT +import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClientFactory import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.oauth.api.OAuthActionFlow import io.element.android.libraries.oauth.test.customtab.FakeOAuthActionFlow +import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionAdvisor +import io.element.android.libraries.permissions.test.FakeLocalNetworkPermissionAdvisor +import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.aSessionData @@ -304,7 +309,8 @@ private fun createPresenter( enterpriseService = enterpriseService, defaultAccountProviderAccessControl = DefaultAccountProviderAccessControl( enterpriseService = enterpriseService, - wellknownRetriever = wellknownRetriever, + wellknownRetrieverFactory = FakeWellknownRetrieverFactory(wellknownRetriever = wellknownRetriever), + temporaryMatrixClientFactory = FakeTemporaryMatrixClientFactory(), ), rageshakeFeatureAvailability = rageshakeFeatureAvailability, loginModePresenter = loginModePresenter, @@ -318,9 +324,9 @@ fun createLoginModePresenter( authenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever(), localNetworkPermissionAdvisor: LocalNetworkPermissionAdvisor = - io.element.android.libraries.permissions.test.FakeLocalNetworkPermissionAdvisor(), - permissionsPresenterFactory: io.element.android.libraries.permissions.api.PermissionsPresenter.Factory = - io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory(), + FakeLocalNetworkPermissionAdvisor(), + permissionsPresenterFactory: PermissionsPresenter.Factory = + FakePermissionsPresenterFactory(), ): LoginModePresenter = LoginModePresenter( oAuthActionFlow = oAuthActionFlow, authenticationService = authenticationService, diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactoryTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactoryTest.kt index d1536d949e..5e5000aedc 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactoryTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactoryTest.kt @@ -52,7 +52,7 @@ fun TestScope.createRustMatrixClientFactory( ), clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(), workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(), - contentScannerUrlProvider: ContentScannerUrlProvider = { Result.success(null) }, + contentScannerUrlProviderFactory: ContentScannerUrlProvider.Factory = { ContentScannerUrlProvider { Result.success(null) } }, ) = RustMatrixClientFactory( cacheDirectory = cacheDirectory, appCoroutineScope = backgroundScope, @@ -67,5 +67,5 @@ fun TestScope.createRustMatrixClientFactory( clientBuilderProvider = clientBuilderProvider, sqliteStoreBuilderProvider = FakeSqliteStoreBuilderProvider(), workManagerScheduler = workManagerScheduler, - contentScannerUrlProvider = contentScannerUrlProvider, + contentScannerUrlProviderFactory = contentScannerUrlProviderFactory, ) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClient.kt new file mode 100644 index 0000000000..83bde4653a --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClient.kt @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.test + +import io.element.android.libraries.matrix.api.TemporaryMatrixClient +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeTemporaryMatrixClient( + private val getUrlResult: (String) -> Result = { lambdaError() }, + private val closeLambda: () -> Unit = {}, +) : TemporaryMatrixClient { + override suspend fun getUrl(url: String): Result = getUrlResult(url) + + override fun close() = closeLambda() +} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClientFactory.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClientFactory.kt new file mode 100644 index 0000000000..1d8dc0c26c --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClientFactory.kt @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.test + +import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory +import io.element.android.libraries.matrix.api.TemporaryMatrixClient + +class FakeTemporaryMatrixClientFactory( + private val createResult: (String) -> Result = { Result.success(FakeTemporaryMatrixClient()) }, +) : TemporaryMatrixClientFactory { + override suspend fun createTemporaryMatrixClient(homeServerUrl: String): Result = createResult(homeServerUrl) +} diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt index b53041faf8..dc101c41a0 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt @@ -148,11 +148,25 @@ class DefaultWellKnownStoreTest { assertThat(result).isEqualTo(WellknownRetrieverResult.Success(wellKnown.map())) } + @Test + fun `prefix is used`() = runTest { + val prefix = "test-prefix" + val clock = FakeSystemClock(epochMillisResult = A_FAKE_TIMESTAMP) + val cacheStore = InMemoryCacheStore( + initialData = mapOf("$prefix-$A_DOMAIN" to CacheData("{}", A_FAKE_TIMESTAMP)) + ) + val sut = createDefaultWellKnownStore(prefix = prefix, cacheStore = cacheStore, systemClock = clock) + + assertThat(sut.get(A_DOMAIN)).isInstanceOf(WellknownRetrieverResult.Success::class.java) + } + private fun createDefaultWellKnownStore( + prefix: String? = null, cacheStore: InMemoryCacheStore = InMemoryCacheStore(), systemClock: FakeSystemClock = FakeSystemClock(), elementWellKnownParser: ElementWellKnownParser = DefaultElementWellKnownParser(jsonProvider), ) = DefaultWellKnownStore( + prefix = prefix, cacheStore = cacheStore, systemClock = systemClock, elementWellKnownParser = elementWellKnownParser, diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt index 96ae4c2b17..020abf55ca 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt @@ -14,275 +14,93 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.wellknown.test.FakeElementWellknownStore import io.element.android.features.wellknown.test.anElementWellKnown -import io.element.android.libraries.androidutils.json.DefaultJsonProvider -import io.element.android.libraries.androidutils.json.JsonProvider -import io.element.android.libraries.network.RetrofitFactory -import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase +import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.wellknown.api.ElementWellKnown +import io.element.android.libraries.wellknown.api.ElementWellKnownParser +import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.tests.testutils.lambda.lambdaRecorder -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify +import io.element.android.tests.testutils.lambda.value import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest -import okhttp3.Call -import okhttp3.Request -import okhttp3.Response -import okhttp3.ResponseBody.Companion.toResponseBody -import okio.IOException import org.junit.Test -import java.net.PortUnreachableException class DefaultWellknownRetrieverTest { @Test - fun `get empty element wellknown`() = runTest { - val mockCall = mockCall(defaultResponse(body = "{}")) + fun `get element wellknown calls resolver and parser`() = runTest { + val parser = lambdaRecorder> { Result.success(anElementWellKnown()) } + val resolver = SpyGetUrlResolver { Result.success(WELLKNOWN_CONTENT.toByteArray()) } val sut = createDefaultWellknownRetriever( - callFactory = { mockCall }, + elementWellKnownParser = parser, + urlResolver = resolver, ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isEqualTo( - WellknownRetrieverResult.Success( - ElementWellKnown( - registrationHelperUrl = null, - enforceElementPro = null, - rageshakeUrl = null, - brandColor = null, - notificationSound = null, - identityProviderAppScheme = null, - customRecoveryPassphrase = null, - contentScannerUrl = null, - forceDisableE2EE = null, - ) - ) - ) - verify(exactly = 1) { mockCall.enqueue(any()) } + + sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT) + + resolver.assertions().isCalledOnce().with(value(WELLKNOWN_URL)) + parser.assertions().isCalledOnce().with(value(WELLKNOWN_CONTENT)) } @Test - fun `get element wellknown with full content`() = runTest { + fun `get element wellknown parser error`() = runTest { val sut = createDefaultWellknownRetriever( - callFactory = { mockCall(defaultResponse()) } + elementWellKnownParser = { Result.failure(IllegalStateException("Invalid JSON")) }, ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isEqualTo( - WellknownRetrieverResult.Success( - ElementWellKnown( - registrationHelperUrl = "a_registration_url", - enforceElementPro = true, - rageshakeUrl = "a_rageshake_url", - brandColor = "#FF0000", - notificationSound = "a_notification_sound.flac", - identityProviderAppScheme = "an_app_scheme", - customRecoveryPassphrase = null, - contentScannerUrl = "https://content-scanner.example.com", - forceDisableE2EE = false, - ) - ) - ) - } - - @Test - fun `get element wellknown with unknown key`() = runTest { - val sut = createDefaultWellknownRetriever( - callFactory = { - mockCall(defaultResponse(body = """{ - "registration_helper_url": "a_registration_url", - "enforce_element_pro": true, - "rageshake_url": "a_rageshake_url", - // Note the trailing comma, and the comment! - "other": true, - }""".trimIndent())) - } - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isEqualTo( - WellknownRetrieverResult.Success( - ElementWellKnown( - registrationHelperUrl = "a_registration_url", - enforceElementPro = true, - rageshakeUrl = "a_rageshake_url", - brandColor = null, - notificationSound = null, - identityProviderAppScheme = null, - contentScannerUrl = null, - customRecoveryPassphrase = null, - forceDisableE2EE = null, - ) - ) - ) - } - - @Test - fun `get element wellknown with custom recovery passphrase settings`() = runTest { - val sut = createDefaultWellknownRetriever( - callFactory = { - mockCall(defaultResponse(body = """{ - "custom_recovery_passphrase": { - "min_character_count": 8 - } - }""".trimIndent())) - } - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isEqualTo( - WellknownRetrieverResult.Success( - anElementWellKnown( - customRecoveryPassphrase = CustomRecoveryPassphrase(minCharacterCount = 8) - ) - ) - ) - } - - @Test - fun `get element wellknown with custom recovery passphrase settings missing min character count floors to 1`() = runTest { - val sut = createDefaultWellknownRetriever( - callFactory = { - mockCall(defaultResponse(body = """{ - "custom_recovery_passphrase": {} - }""".trimIndent())) - } - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isEqualTo( - WellknownRetrieverResult.Success( - anElementWellKnown( - customRecoveryPassphrase = CustomRecoveryPassphrase(minCharacterCount = 1) - ) - ) - ) - } - - @Test - fun `get element wellknown with zero min character count floors to 1`() = runTest { - val sut = createDefaultWellknownRetriever( - callFactory = { - mockCall( - defaultResponse( - body = """{ - "custom_recovery_passphrase": { - "min_character_count": 0 - } - }""".trimIndent() - ) - ) - } - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isEqualTo( - WellknownRetrieverResult.Success( - anElementWellKnown( - customRecoveryPassphrase = CustomRecoveryPassphrase(minCharacterCount = 1) - ) - ) - ) - } - - @Test - fun `get element wellknown with negative min character count floors to 1`() = runTest { - val sut = createDefaultWellknownRetriever( - callFactory = { - mockCall( - defaultResponse( - body = """{ - "custom_recovery_passphrase": { - "min_character_count": -5 - } - }""".trimIndent() - ) - ) - } - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isEqualTo( - WellknownRetrieverResult.Success( - anElementWellKnown( - customRecoveryPassphrase = CustomRecoveryPassphrase(minCharacterCount = 1) - ) - ) - ) - } - - @Test - fun `get element wellknown json error`() = runTest { - val sut = createDefaultWellknownRetriever( - callFactory = { mockCall(defaultResponse(status = 500)) } - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isInstanceOf(WellknownRetrieverResult.Error::class.java) + assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) + .isInstanceOf(WellknownRetrieverResult.Error::class.java) } @Test fun `get element wellknown network error`() = runTest { - val sut = createDefaultWellknownRetriever( - callFactory = { mockCall(error = PortUnreachableException()) } - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isInstanceOf(WellknownRetrieverResult.Error::class.java) + val resolver = SpyGetUrlResolver { Result.failure(IllegalStateException("Network error")) } + val sut = createDefaultWellknownRetriever(urlResolver = resolver) + assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) + .isInstanceOf(WellknownRetrieverResult.Error::class.java) } @Test fun `get element wellknown 404 http error counts as not found`() = runTest { - val sut = createDefaultWellknownRetriever( - callFactory = { mockCall(defaultResponse(status = 404)) } - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isInstanceOf(WellknownRetrieverResult.NotFound::class.java) - } - - @Test - fun `get element wellknown hitting cache containing invalid json`() = runTest { - val cacheStore = FakeElementWellknownStore( - initialData = mapOf( - WELLKNOWN_URL to WellknownRetrieverResult.Error(IllegalStateException("Invalid JSON")) - ) - ) - val sut = createDefaultWellknownRetriever( - callFactory = { mockCall(defaultResponse(body = "invalid json")) }, - cacheStore = cacheStore, - jsonProvider = JsonProvider { error("Failed to parse JSON") } - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isInstanceOf(WellknownRetrieverResult.Error::class.java) - // Ensure that the cache is deleted after the failure to parse it - assertThat(cacheStore.get(WELLKNOWN_URL)).isEqualTo(WellknownRetrieverResult.NotFound) + val resolver = SpyGetUrlResolver { Result.failure(ClientException.Generic("Http error: 404", null)) } + val sut = createDefaultWellknownRetriever(urlResolver = resolver) + assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) + .isInstanceOf(WellknownRetrieverResult.NotFound::class.java) } @Test fun `get element wellknown was overridden`() = runTest { - val getLambda = lambdaRecorder { mockCall() } val wellKnown = anElementWellKnown() + val resolver = SpyGetUrlResolver { Result.success(WELLKNOWN_CONTENT.toByteArray()) } + val sut = createDefaultWellknownRetriever( - callFactory = getLambda, enterpriseService = FakeEnterpriseService( - overrideWellKnownResult = { wellKnown } - ) + overrideWellKnownResult = { wellKnown }, + ), + urlResolver = resolver, ) // The overridden value is returned - assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isEqualTo( + assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)).isEqualTo( WellknownRetrieverResult.Success(wellKnown) ) // And the endpoint is never hit - getLambda.assertions().isNeverCalled() + resolver.assertions().isNeverCalled() } - private fun defaultResponse( - body: String = WELLKNOWN_CONTENT, - status: Int = 200, - ) = Response.Builder() - .code(status) - .message("OK") - .protocol(okhttp3.Protocol.HTTP_1_1) - .request(Request.Builder().url(WELLKNOWN_URL).build()) - .body(body.toByteArray().toResponseBody()) - .build() - - private fun createDefaultWellknownRetriever( - callFactory: Call.Factory = Call.Factory { _: Request -> mockCall(defaultResponse()) }, + private fun TestScope.createDefaultWellknownRetriever( cacheStore: FakeElementWellknownStore = FakeElementWellknownStore(), - jsonProvider: JsonProvider = DefaultJsonProvider(), + elementWellKnownParser: ElementWellKnownParser = { Result.success(expectedElementWellKnown) }, enterpriseService: FakeEnterpriseService = FakeEnterpriseService(overrideWellKnownResult = { null }), + urlResolver: GetUrlResolver = SpyGetUrlResolver { Result.success(WELLKNOWN_CONTENT.toByteArray()) }, ) = DefaultWellknownRetriever( - retrofitFactory = RetrofitFactory( - callFactory = { callFactory }, - json = { jsonProvider } - ), - jsonProvider = jsonProvider, - elementWellknownStore = cacheStore, + elementWellknownStoreFactory = { cacheStore }, enterpriseService = enterpriseService, + elementWellKnownParser = elementWellKnownParser, + getUrlResolver = urlResolver, + coroutineScope = backgroundScope, ) companion object { @@ -297,24 +115,27 @@ class DefaultWellknownRetrieverTest { "content_scanner_url": "https://content-scanner.example.com", "force_disable_e2ee": false }""" + + private val expectedElementWellKnown = ElementWellKnown( + registrationHelperUrl = "a_registration_url", + enforceElementPro = true, + rageshakeUrl = "a_rageshake_url", + brandColor = "#FF0000", + notificationSound = "a_notification_sound.flac", + identityProviderAppScheme = "an_app_scheme", + contentScannerUrl = "https://content-scanner.example.com", + customRecoveryPassphrase = null, + forceDisableE2EE = false, + ) } } -private fun mockCall( - response: Response? = null, - error: IOException? = null, -): Call = mockk { - if (error != null) { - every { enqueue(any()) } answers { - val callback = firstArg() - callback.onFailure(this@mockk, error) - } - } else if (response != null) { - every { enqueue(any()) } answers { - val callback = firstArg() - callback.onResponse(this@mockk, response) - } - } else { - error("Either response or error must be provided") +private class SpyGetUrlResolver(lambda: (String) -> Result) : GetUrlResolver { + private val spy = lambdaRecorder> { lambda(it) } + + override suspend fun getUrl(url: String): Result { + return spy(url) } + + fun assertions() = spy.assertions() } diff --git a/libraries/wellknown/test/build.gradle.kts b/libraries/wellknown/test/build.gradle.kts index 8eb47ef5ca..7f12f380c7 100644 --- a/libraries/wellknown/test/build.gradle.kts +++ b/libraries/wellknown/test/build.gradle.kts @@ -17,6 +17,7 @@ android { dependencies { implementation(libs.serialization.json) implementation(projects.libraries.androidutils) + implementation(projects.libraries.matrix.api) implementation(projects.libraries.wellknown.api) implementation(projects.libraries.wellknown.impl) implementation(projects.tests.testutils) diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeSessionWellknownRetriever.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeSessionWellknownRetriever.kt deleted file mode 100644 index 3c675beed2..0000000000 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeSessionWellknownRetriever.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.wellknown.test - -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.SessionWellknownRetriever -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult -import io.element.android.tests.testutils.simulateLongTask - -class FakeSessionWellknownRetriever( - private val getElementWellKnownResult: () -> WellknownRetrieverResult = { WellknownRetrieverResult.NotFound }, -) : SessionWellknownRetriever { - override suspend fun getElementWellKnown(): WellknownRetrieverResult = simulateLongTask { - getElementWellKnownResult() - } -} diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt index 139332667e..e08969d0f9 100644 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt +++ b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt @@ -9,14 +9,16 @@ package io.element.android.features.wellknown.test import io.element.android.libraries.wellknown.api.ElementWellKnown +import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource import io.element.android.libraries.wellknown.api.WellknownRetriever import io.element.android.libraries.wellknown.api.WellknownRetrieverResult -import io.element.android.tests.testutils.simulateLongTask class FakeWellknownRetriever( - private val getElementWellKnownResult: (String) -> WellknownRetrieverResult = { WellknownRetrieverResult.NotFound }, + private val getElementWellKnownResult: (String, EnterpriseRemoteConfigSource) -> WellknownRetrieverResult = { _, _ -> + WellknownRetrieverResult.NotFound + }, ) : WellknownRetriever { - override suspend fun getElementWellKnown(baseUrl: String): WellknownRetrieverResult = simulateLongTask { - getElementWellKnownResult(baseUrl) + override suspend fun getElementWellKnown(host: String, source: EnterpriseRemoteConfigSource): WellknownRetrieverResult { + return getElementWellKnownResult(host, source) } } diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt new file mode 100644 index 0000000000..c0e804835e --- /dev/null +++ b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.wellknown.test + +import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.wellknown.api.WellknownRetriever + +class FakeWellknownRetrieverFactory( + private val wellknownRetriever: WellknownRetriever = FakeWellknownRetriever(), +) : WellknownRetriever.Factory { + override fun create(getUrlResolver: GetUrlResolver): WellknownRetriever = wellknownRetriever +} From e92016c68e8eed0f94debd190a3d3874e814b006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Tue, 11 Aug 2026 09:27:46 +0200 Subject: [PATCH 116/338] Improve `host` extraction in `DefaultWellknownRetriever` --- .../element/android/libraries/androidutils/text/TextUtils.kt | 2 ++ .../libraries/wellknown/impl/DefaultWellknownRetriever.kt | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/text/TextUtils.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/text/TextUtils.kt index dde63e79a6..5e78c3c1ac 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/text/TextUtils.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/text/TextUtils.kt @@ -13,3 +13,5 @@ import java.nio.charset.Charset fun String.urlEncoded(charset: Charset = Charsets.UTF_8): String = URLEncoder.encode(this, charset.name()) fun String.urlDecoded(charset: Charset = Charsets.UTF_8): String = URLDecoder.decode(this, charset.name()) + +fun String.takeIfNotBlank(): String? = ifBlank { null } diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt index df72eddb25..b6e3063a7b 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt @@ -14,7 +14,9 @@ import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.libraries.androidutils.text.takeIfNotBlank import io.element.android.libraries.core.extensions.mapCatchingExceptions +import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.matrix.api.GetUrlResolver @@ -53,7 +55,8 @@ class DefaultWellknownRetriever( return WellknownRetrieverResult.Success(overriddenElementWellKnown) } - val checkedHost = URL(host.ensureProtocol()).host ?: host + // The passed `host` function may be a full URL, in that case we're only interested in the actual host + val checkedHost = runCatchingExceptions { URL(host.ensureProtocol()).host.takeIfNotBlank() }.getOrNull() ?: host // We instantiate different stores for different sources, so that we can cache them separately. // The ESS_CONFIG source is cached with a prefix to differentiate it from the WELLKNOWN_ENDPOINT. From dbc28df3ebbcf98f8b172de959683192a33a257c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Tue, 11 Aug 2026 09:28:25 +0200 Subject: [PATCH 117/338] Remove unnecessary call to `fetchElementWellKnown` when the value was up to date in the store in `DefaultWellknownRetriever` --- .../libraries/wellknown/impl/DefaultWellknownRetriever.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt index b6e3063a7b..19ae6c4a22 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt @@ -68,7 +68,6 @@ class DefaultWellknownRetriever( return when (val cacheData = store.get(checkedHost)) { is WellknownRetrieverResult.Success -> { Timber.d("Using cached well-known for domain $checkedHost") - fetchElementWellKnown(checkedHost, source, store) cacheData } is WellknownRetrieverResult.Outdated -> { From e371b46c68f972539dab7a4024f9f71ac22de462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Tue, 11 Aug 2026 09:30:22 +0200 Subject: [PATCH 118/338] Rename `GetUrlResolver` to `UrlContentFetcher` and `TemporaryMatrixClientFactory.createTemporaryMatrixClient` to just `create` --- .../features/enterprise/api/EnterpriseService.kt | 4 ++-- .../RemoteEnterpriseConfigProvider.kt | 4 ++-- .../enterprise/impl/DefaultEnterpriseService.kt | 4 ++-- .../DefaultRemoteEnterpriseConfigProvider.kt | 4 ++-- .../scanner/DefaultContentScannerUrlProvider.kt | 4 ++-- .../enterprise/test/FakeEnterpriseService.kt | 8 ++++---- .../FakeRemoteEnterpriseConfigProviderFactory.kt | 4 ++-- .../DefaultAccountProviderAccessControl.kt | 2 +- .../WebClientUrlForAuthenticationRetriever.kt | 2 +- .../android/libraries/matrix/api/MatrixClient.kt | 2 +- .../matrix/api/TemporaryMatrixClient.kt | 2 +- .../matrix/api/TemporaryMatrixClientFactory.kt | 2 +- .../{GetUrlResolver.kt => UrlContentFetcher.kt} | 5 ++++- .../api/scanner/ContentScannerUrlProvider.kt | 4 ++-- .../impl/RustTemporaryMatrixClientFactory.kt | 4 ++-- .../impl/auth/RustMatrixAuthenticationService.kt | 2 +- .../matrix/impl/di/SessionMatrixModule.kt | 6 +++--- .../test/FakeTemporaryMatrixClientFactory.kt | 4 ++-- .../wellknown/api/WellknownRetriever.kt | 4 ++-- .../wellknown/impl/DefaultWellknownRetriever.kt | 10 +++++----- .../wellknown/impl/SessionWellknownModule.kt | 6 +++--- .../impl/DefaultWellknownRetrieverTest.kt | 16 ++++++++-------- .../test/FakeWellknownRetrieverFactory.kt | 4 ++-- 23 files changed, 55 insertions(+), 52 deletions(-) rename libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/{GetUrlResolver.kt => UrlContentFetcher.kt} (77%) diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt index f2923c2b40..a435b53e48 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt @@ -10,7 +10,7 @@ package io.element.android.features.enterprise.api import androidx.compose.ui.graphics.Color import io.element.android.compound.colors.SemanticColorsLightDark -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.wellknown.api.ElementWellKnown import kotlinx.coroutines.flow.Flow @@ -18,7 +18,7 @@ import kotlinx.coroutines.flow.Flow interface EnterpriseService { val isEnterpriseBuild: Boolean suspend fun isEnterpriseUser(sessionId: SessionId): Boolean - suspend fun tweakMasUrl(url: String, homeserver: String, getUrlResolver: GetUrlResolver): String + suspend fun tweakMasUrl(url: String, homeserver: String, urlContentFetcher: UrlContentFetcher): String fun defaultHomeserverList(): List suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt index 724508bb49..adbcc5b31a 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt @@ -7,7 +7,7 @@ package io.element.android.features.enterprise.api.remoteconfig -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.wellknown.api.WellknownRetrieverResult @@ -18,6 +18,6 @@ interface RemoteEnterpriseConfigProvider { suspend fun get(sessionId: SessionId): WellknownRetrieverResult interface Factory { - fun create(getUrlResolver: GetUrlResolver): RemoteEnterpriseConfigProvider + fun create(urlContentFetcher: UrlContentFetcher): RemoteEnterpriseConfigProvider } } diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt index 567f598c22..c75a2cb527 100644 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt @@ -14,7 +14,7 @@ import dev.zacsweers.metro.ContributesBinding import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.wellknown.api.ElementWellKnown import kotlinx.coroutines.flow.Flow @@ -25,7 +25,7 @@ class DefaultEnterpriseService : EnterpriseService { override val isEnterpriseBuild = false override suspend fun isEnterpriseUser(sessionId: SessionId) = false - override suspend fun tweakMasUrl(url: String, homeserver: String, getUrlResolver: GetUrlResolver) = url + override suspend fun tweakMasUrl(url: String, homeserver: String, urlContentFetcher: UrlContentFetcher) = url override fun defaultHomeserverList(): List = emptyList() override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt index e585885bdd..fbc5cd5827 100644 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt @@ -11,7 +11,7 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfig import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.wellknown.api.WellknownRetrieverResult @@ -23,7 +23,7 @@ class DefaultRemoteEnterpriseConfigProvider : RemoteEnterpriseConfigProvider { @ContributesBinding(AppScope::class) class Factory : RemoteEnterpriseConfigProvider.Factory { - override fun create(getUrlResolver: GetUrlResolver): RemoteEnterpriseConfigProvider { + override fun create(urlContentFetcher: UrlContentFetcher): RemoteEnterpriseConfigProvider { return DefaultRemoteEnterpriseConfigProvider() } } diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/scanner/DefaultContentScannerUrlProvider.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/scanner/DefaultContentScannerUrlProvider.kt index edd1514929..4d4a800de9 100644 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/scanner/DefaultContentScannerUrlProvider.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/scanner/DefaultContentScannerUrlProvider.kt @@ -9,7 +9,7 @@ package io.element.android.features.enterprise.impl.scanner import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.scanner.ContentScannerUrlProvider @@ -22,7 +22,7 @@ class DefaultContentScannerUrlProvider : ContentScannerUrlProvider { @ContributesBinding(AppScope::class) class Factory : ContentScannerUrlProvider.Factory { - override fun create(getUrlResolver: GetUrlResolver): ContentScannerUrlProvider { + override fun create(urlContentFetcher: UrlContentFetcher): ContentScannerUrlProvider { return DefaultContentScannerUrlProvider() } } diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt index 524a1fb9dd..858c46e3bf 100644 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.graphics.Color import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.tests.testutils.lambda.lambdaError @@ -32,7 +32,7 @@ class FakeEnterpriseService( private val firebasePushGatewayResult: () -> String? = { lambdaError() }, private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() }, private val getNoisyNotificationChannelIdResult: (SessionId?) -> String? = { lambdaError() }, - private val tweakMasUrlResult: (String, String, GetUrlResolver) -> String = { _, _, _ -> lambdaError() }, + private val tweakMasUrlResult: (String, String, UrlContentFetcher) -> String = { _, _, _ -> lambdaError() }, private val overrideWellKnownResult: () -> ElementWellKnown? = { lambdaError() }, private val essConfigEndpointUrlResult: (String) -> String = { lambdaError() }, ) : EnterpriseService { @@ -43,8 +43,8 @@ class FakeEnterpriseService( isEnterpriseUserResult(sessionId) } - override suspend fun tweakMasUrl(url: String, homeserver: String, getUrlResolver: GetUrlResolver): String = simulateLongTask { - tweakMasUrlResult(url, homeserver, getUrlResolver) + override suspend fun tweakMasUrl(url: String, homeserver: String, urlContentFetcher: UrlContentFetcher): String = simulateLongTask { + tweakMasUrlResult(url, homeserver, urlContentFetcher) } override fun defaultHomeserverList(): List { diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt index 1ba699cfed..a49bbba279 100644 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt @@ -8,10 +8,10 @@ package io.element.android.features.enterprise.test.remoteconfig import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher class FakeRemoteEnterpriseConfigProviderFactory( private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(), ) : RemoteEnterpriseConfigProvider.Factory { - override fun create(getUrlResolver: GetUrlResolver): RemoteEnterpriseConfigProvider = remoteEnterpriseConfigProvider + override fun create(urlContentFetcher: UrlContentFetcher): RemoteEnterpriseConfigProvider = remoteEnterpriseConfigProvider } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt index 854b4293f1..119923c67d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt @@ -40,7 +40,7 @@ class DefaultAccountProviderAccessControl( ) { if (enterpriseService.isEnterpriseBuild.not()) { // Ensure that Element Pro is not required for this account provider - val temporaryMatrixClient = temporaryMatrixClientFactory.createTemporaryMatrixClient(accountProviderUrl).getOrThrow() + val temporaryMatrixClient = temporaryMatrixClientFactory.create(accountProviderUrl).getOrThrow() temporaryMatrixClient.use { val wellknownRetriever = wellknownRetrieverFactory.create(temporaryMatrixClient) val wellKnown = wellknownRetriever.getElementWellKnown( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt index f9dcd83c24..2902126417 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt @@ -32,7 +32,7 @@ class DefaultWebClientUrlForAuthenticationRetriever( Timber.w("Temporary account creation flow is only supported on matrix.org") throw AccountCreationNotSupported() } - val temporaryMatrixClient = temporaryMatrixClientFactory.createTemporaryMatrixClient(homeServerUrl).getOrThrow() + val temporaryMatrixClient = temporaryMatrixClientFactory.create(homeServerUrl).getOrThrow() return temporaryMatrixClient.use { val wellknownRetriever = wellknownRetrieverFactory.create(temporaryMatrixClient) val wellknown = wellknownRetriever.getElementWellKnown( diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index a17859ac78..19353473c3 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -54,7 +54,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import java.util.Optional -interface MatrixClient : GetUrlResolver { +interface MatrixClient : UrlContentFetcher { val sessionId: SessionId val deviceId: DeviceId diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClient.kt index 918b6237a0..1e3ffea154 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClient.kt @@ -15,4 +15,4 @@ import java.lang.AutoCloseable * Note: this client implements an [AutoCloseable] interface, so using [AutoCloseable.close] or [AutoCloseable.use] will clean up any resources associated * with it, such as temporary files. */ -interface TemporaryMatrixClient : GetUrlResolver, AutoCloseable +interface TemporaryMatrixClient : UrlContentFetcher, AutoCloseable diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt index 605401af85..4be1b980a0 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt @@ -11,5 +11,5 @@ package io.element.android.libraries.matrix.api * Factory to create a temporary [TemporaryMatrixClient] for a given home server URL. */ interface TemporaryMatrixClientFactory { - suspend fun createTemporaryMatrixClient(homeServerUrl: String): Result + suspend fun create(homeServerUrl: String): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/GetUrlResolver.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/UrlContentFetcher.kt similarity index 77% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/GetUrlResolver.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/UrlContentFetcher.kt index 085aeff02f..a6fb234b86 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/GetUrlResolver.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/UrlContentFetcher.kt @@ -7,7 +7,10 @@ package io.element.android.libraries.matrix.api -fun interface GetUrlResolver { +/** + * An interface to fetch content from a URL using a simple GET request. + */ +fun interface UrlContentFetcher { /** * Execute generic GET requests through the SDKs internal HTTP client. */ diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt index 32794bff7b..76878080ae 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt @@ -7,7 +7,7 @@ package io.element.android.libraries.matrix.api.scanner -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.core.SessionId /** @@ -20,6 +20,6 @@ fun interface ContentScannerUrlProvider { suspend fun getContentScannerUrl(sessionId: SessionId): Result fun interface Factory { - fun create(getUrlResolver: GetUrlResolver): ContentScannerUrlProvider + fun create(urlContentFetcher: UrlContentFetcher): ContentScannerUrlProvider } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactory.kt index e2a55c2b8c..70903ba91a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactory.kt @@ -10,8 +10,8 @@ package io.element.android.libraries.matrix.impl import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.core.extensions.runCatchingExceptions -import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory import io.element.android.libraries.matrix.api.TemporaryMatrixClient +import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory @ContributesBinding(AppScope::class) @@ -19,7 +19,7 @@ class RustTemporaryMatrixClientFactory( private val sessionPathsFactory: SessionPathsFactory, private val rustMatrixClientFactory: RustMatrixClientFactory, ) : TemporaryMatrixClientFactory { - override suspend fun createTemporaryMatrixClient(homeServerUrl: String): Result { + override suspend fun create(homeServerUrl: String): Result { return runCatchingExceptions { val sessionPaths = sessionPathsFactory.create() val client = rustMatrixClientFactory.getBaseClientBuilder( 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 bef83c35ff..2c936c9b23 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 @@ -279,7 +279,7 @@ class RustMatrixAuthenticationService( enterpriseService.tweakMasUrl( url = it, homeserver = client.server() ?: client.homeserver(), - getUrlResolver = getUrlResolver, + urlContentFetcher = getUrlResolver, ) } pendingOAuthAuthorizationData = oAuthAuthorizationData diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt index 8f942e1ebf..46fa885d25 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt @@ -13,9 +13,9 @@ import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.Provides import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.annotations.SessionCoroutineScope -import io.element.android.libraries.matrix.api.GetUrlResolver import io.element.android.libraries.matrix.api.HomeserverCapabilitiesProvider import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.encryption.EncryptionService @@ -32,7 +32,7 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationS import kotlinx.coroutines.CoroutineScope @BindingContainer -@ContributesTo(SessionScope::class, replaces = [GetUrlResolver::class]) +@ContributesTo(SessionScope::class) object SessionMatrixModule { @Provides fun providesSessionId(matrixClient: MatrixClient): SessionId { @@ -111,7 +111,7 @@ object SessionMatrixModule { } @Provides - fun providesGetUrlResolverClient(matrixClient: MatrixClient): GetUrlResolver { + fun providesGetUrlResolverClient(matrixClient: MatrixClient): UrlContentFetcher { return matrixClient } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClientFactory.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClientFactory.kt index 1d8dc0c26c..cdec68bdd1 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClientFactory.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeTemporaryMatrixClientFactory.kt @@ -7,11 +7,11 @@ package io.element.android.libraries.matrix.test -import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory import io.element.android.libraries.matrix.api.TemporaryMatrixClient +import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory class FakeTemporaryMatrixClientFactory( private val createResult: (String) -> Result = { Result.success(FakeTemporaryMatrixClient()) }, ) : TemporaryMatrixClientFactory { - override suspend fun createTemporaryMatrixClient(homeServerUrl: String): Result = createResult(homeServerUrl) + override suspend fun create(homeServerUrl: String): Result = createResult(homeServerUrl) } diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt index 830cf4bd4e..56d251426a 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt @@ -8,7 +8,7 @@ package io.element.android.libraries.wellknown.api -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher interface WellknownRetriever { suspend fun getElementWellKnown( @@ -17,6 +17,6 @@ interface WellknownRetriever { ): WellknownRetrieverResult interface Factory { - fun create(getUrlResolver: GetUrlResolver): WellknownRetriever + fun create(urlContentFetcher: UrlContentFetcher): WellknownRetriever } } diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt index 19ae6c4a22..fbfa58b108 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt @@ -19,12 +19,12 @@ import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.di.annotations.AppCoroutineScope -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.ElementWellKnownParser -import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource import io.element.android.libraries.wellknown.api.ElementWellknownStore +import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource import io.element.android.libraries.wellknown.api.WellknownRetriever import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import kotlinx.coroutines.CoroutineScope @@ -37,13 +37,13 @@ class DefaultWellknownRetriever( private val elementWellknownStoreFactory: ElementWellknownStore.Factory, private val enterpriseService: EnterpriseService, private val elementWellKnownParser: ElementWellKnownParser, - @Assisted private val getUrlResolver: GetUrlResolver, + @Assisted private val urlContentFetcher: UrlContentFetcher, @AppCoroutineScope private val coroutineScope: CoroutineScope, ) : WellknownRetriever { @ContributesBinding(AppScope::class) @AssistedFactory fun interface Factory : WellknownRetriever.Factory { - override fun create(getUrlResolver: GetUrlResolver): DefaultWellknownRetriever + override fun create(urlContentFetcher: UrlContentFetcher): DefaultWellknownRetriever } override suspend fun getElementWellKnown( @@ -95,7 +95,7 @@ class DefaultWellknownRetriever( source: EnterpriseRemoteConfigSource, store: ElementWellknownStore ): WellknownRetrieverResult { - return getUrlResolver + return urlContentFetcher .getUrl(url(host, source)) .mapCatchingExceptions { val data = String(it) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt index bfd18ac7c2..1587e95765 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt @@ -13,7 +13,7 @@ import dev.zacsweers.metro.Provides import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.annotations.SessionCoroutineScope -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.wellknown.api.ElementWellKnownParser import io.element.android.libraries.wellknown.api.ElementWellknownStore import io.element.android.libraries.wellknown.api.WellknownRetriever @@ -27,14 +27,14 @@ object SessionWellknownModule { elementWellknownStoreFactory: ElementWellknownStore.Factory, enterpriseService: EnterpriseService, elementWellKnownParser: ElementWellKnownParser, - getUrlResolver: GetUrlResolver, + urlContentFetcher: UrlContentFetcher, @SessionCoroutineScope coroutineScope: CoroutineScope, ): WellknownRetriever { return DefaultWellknownRetriever( elementWellknownStoreFactory = elementWellknownStoreFactory, enterpriseService = enterpriseService, elementWellKnownParser = elementWellKnownParser, - getUrlResolver = getUrlResolver, + urlContentFetcher = urlContentFetcher, coroutineScope = coroutineScope ) } diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt index 020abf55ca..5151f17599 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt @@ -14,7 +14,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.wellknown.test.FakeElementWellknownStore import io.element.android.features.wellknown.test.anElementWellKnown -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.ElementWellKnownParser @@ -31,7 +31,7 @@ class DefaultWellknownRetrieverTest { @Test fun `get element wellknown calls resolver and parser`() = runTest { val parser = lambdaRecorder> { Result.success(anElementWellKnown()) } - val resolver = SpyGetUrlResolver { Result.success(WELLKNOWN_CONTENT.toByteArray()) } + val resolver = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) } val sut = createDefaultWellknownRetriever( elementWellKnownParser = parser, urlResolver = resolver, @@ -54,7 +54,7 @@ class DefaultWellknownRetrieverTest { @Test fun `get element wellknown network error`() = runTest { - val resolver = SpyGetUrlResolver { Result.failure(IllegalStateException("Network error")) } + val resolver = SpyUrlContentFetcher { Result.failure(IllegalStateException("Network error")) } val sut = createDefaultWellknownRetriever(urlResolver = resolver) assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) .isInstanceOf(WellknownRetrieverResult.Error::class.java) @@ -62,7 +62,7 @@ class DefaultWellknownRetrieverTest { @Test fun `get element wellknown 404 http error counts as not found`() = runTest { - val resolver = SpyGetUrlResolver { Result.failure(ClientException.Generic("Http error: 404", null)) } + val resolver = SpyUrlContentFetcher { Result.failure(ClientException.Generic("Http error: 404", null)) } val sut = createDefaultWellknownRetriever(urlResolver = resolver) assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) .isInstanceOf(WellknownRetrieverResult.NotFound::class.java) @@ -72,7 +72,7 @@ class DefaultWellknownRetrieverTest { fun `get element wellknown was overridden`() = runTest { val wellKnown = anElementWellKnown() - val resolver = SpyGetUrlResolver { Result.success(WELLKNOWN_CONTENT.toByteArray()) } + val resolver = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) } val sut = createDefaultWellknownRetriever( enterpriseService = FakeEnterpriseService( @@ -94,12 +94,12 @@ class DefaultWellknownRetrieverTest { cacheStore: FakeElementWellknownStore = FakeElementWellknownStore(), elementWellKnownParser: ElementWellKnownParser = { Result.success(expectedElementWellKnown) }, enterpriseService: FakeEnterpriseService = FakeEnterpriseService(overrideWellKnownResult = { null }), - urlResolver: GetUrlResolver = SpyGetUrlResolver { Result.success(WELLKNOWN_CONTENT.toByteArray()) }, + urlResolver: UrlContentFetcher = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) }, ) = DefaultWellknownRetriever( elementWellknownStoreFactory = { cacheStore }, enterpriseService = enterpriseService, elementWellKnownParser = elementWellKnownParser, - getUrlResolver = urlResolver, + urlContentFetcher = urlResolver, coroutineScope = backgroundScope, ) @@ -130,7 +130,7 @@ class DefaultWellknownRetrieverTest { } } -private class SpyGetUrlResolver(lambda: (String) -> Result) : GetUrlResolver { +private class SpyUrlContentFetcher(lambda: (String) -> Result) : UrlContentFetcher { private val spy = lambdaRecorder> { lambda(it) } override suspend fun getUrl(url: String): Result { diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt index c0e804835e..718649dd5d 100644 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt +++ b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt @@ -7,11 +7,11 @@ package io.element.android.features.wellknown.test -import io.element.android.libraries.matrix.api.GetUrlResolver +import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.wellknown.api.WellknownRetriever class FakeWellknownRetrieverFactory( private val wellknownRetriever: WellknownRetriever = FakeWellknownRetriever(), ) : WellknownRetriever.Factory { - override fun create(getUrlResolver: GetUrlResolver): WellknownRetriever = wellknownRetriever + override fun create(urlContentFetcher: UrlContentFetcher): WellknownRetriever = wellknownRetriever } From 4f5855bfa51d4f00a34d06a01553647b5ef2a3eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Tue, 11 Aug 2026 09:30:29 +0200 Subject: [PATCH 119/338] Bump enterprise module --- enterprise | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enterprise b/enterprise index 029db99513..f5f910856c 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit 029db99513d569b715ad3bb24145b3c2ac9c14cc +Subproject commit f5f910856cfd4f982d1dcce16a80f8fedfbcbd31 From 6c7f4d3b240c78f686e2c5d1cdf8622596762bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Tue, 11 Aug 2026 12:12:56 +0200 Subject: [PATCH 120/338] Add some missing tests to improve coverage --- .../RustTemporaryMatrixClientFactoryTest.kt | 84 ++++++++++++++++ .../RustMatrixAuthenticationServiceTest.kt | 24 +++++ .../impl/DefaultWellknownRetriever.kt | 4 +- .../impl/DefaultWellknownRetrieverTest.kt | 95 ++++++++++++++++++- 4 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactoryTest.kt diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactoryTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactoryTest.kt new file mode 100644 index 0000000000..a8843a9ca2 --- /dev/null +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientFactoryTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService +import io.element.android.libraries.matrix.api.scanner.ContentScannerUrlProvider +import io.element.android.libraries.matrix.impl.auth.FakeProxyProvider +import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory +import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory +import io.element.android.libraries.matrix.impl.storage.FakeSqliteStoreBuilderProvider +import io.element.android.libraries.network.useragent.SimpleUserAgentProvider +import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.libraries.sessionstorage.test.InMemorySessionStore +import io.element.android.libraries.workmanager.test.FakeWorkManagerScheduler +import io.element.android.services.analytics.test.FakeAnalyticsService +import io.element.android.services.toolbox.test.systemclock.FakeSystemClock +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import java.io.File + +class RustTemporaryMatrixClientFactoryTest { + @Test + fun `create returns a TemporaryMatrixClient`() = runTest { + val sut = createRustTemporaryMatrixClientFactory() + val result = sut.create("https://matrix.org") + assertThat(result.isSuccess).isTrue() + } + + @Test + fun `create can fail gracefully`() = runTest { + val sut = createRustTemporaryMatrixClientFactory( + rustMatrixClientFactory = createRustMatrixClientFactory( + clientBuilderProvider = FakeClientBuilderProvider( + provideResult = { throw IllegalStateException("Failed to create client builder") } + ) + ) + ) + val result = sut.create("https://matrix.org") + assertThat(result.isFailure).isTrue() + } + + private fun TestScope.createRustTemporaryMatrixClientFactory( + sessionPathsFactory: SessionPathsFactory = SessionPathsFactory(File("/base"), File("/cache")), + rustMatrixClientFactory: RustMatrixClientFactory = createRustMatrixClientFactory(), + ): RustTemporaryMatrixClientFactory { + return RustTemporaryMatrixClientFactory( + sessionPathsFactory = sessionPathsFactory, + rustMatrixClientFactory = rustMatrixClientFactory, + ) + } + + private fun TestScope.createRustMatrixClientFactory( + cacheDirectory: File = File("/cache"), + sessionStore: SessionStore = InMemorySessionStore( + updateUserProfileResult = { _, _, _ -> }, + ), + clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(), + workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(), + contentScannerUrlProviderFactory: ContentScannerUrlProvider.Factory = { ContentScannerUrlProvider { Result.success(null) } }, + ) = RustMatrixClientFactory( + cacheDirectory = cacheDirectory, + appCoroutineScope = backgroundScope, + coroutineDispatchers = testCoroutineDispatchers(), + sessionStore = sessionStore, + userAgentProvider = SimpleUserAgentProvider(), + proxyProvider = FakeProxyProvider(), + clock = FakeSystemClock(), + analyticsService = FakeAnalyticsService(), + featureFlagService = FakeFeatureFlagService(), + timelineEventFilterFactory = FakeTimelineEventFilterFactory(), + clientBuilderProvider = clientBuilderProvider, + sqliteStoreBuilderProvider = FakeSqliteStoreBuilderProvider(), + workManagerScheduler = workManagerScheduler, + contentScannerUrlProviderFactory = contentScannerUrlProviderFactory, + ) +} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt index f5f3ffd10b..e349c616c0 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.test.auth.FakeOAuthRedirectUrlProvide import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore +import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest @@ -50,6 +51,29 @@ class RustMatrixAuthenticationServiceTest { assertThat(sut.setHomeserver("matrix.org").isSuccess).isTrue() } + @Test + fun `setHomeserver can fail gracefully and clean up the temporary client`() = runTest { + val closeResult = lambdaRecorder {} + val sut = createRustMatrixAuthenticationService( + clientBuilderProvider = FakeClientBuilderProvider( + provideResult = { + FakeFfiClientBuilder( + buildResult = { + FakeFfiClient( + homeserverLoginDetailsResult = { + throw IllegalStateException("Failed to get homeserver login details") + }, + closeResult = closeResult, + ) + }, + ) + }, + ), + ) + assertThat(sut.setHomeserver("matrix.org").isFailure).isTrue() + closeResult.assertions().isCalledOnce() + } + private fun TestScope.createRustMatrixAuthenticationService( sessionStore: SessionStore = InMemorySessionStore(), clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(), diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt index fbfa58b108..60e8c621e2 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt @@ -56,7 +56,9 @@ class DefaultWellknownRetriever( } // The passed `host` function may be a full URL, in that case we're only interested in the actual host - val checkedHost = runCatchingExceptions { URL(host.ensureProtocol()).host.takeIfNotBlank() }.getOrNull() ?: host + val checkedHost = runCatchingExceptions { URL(host.ensureProtocol()).host.takeIfNotBlank() } + .getOrElse { return WellknownRetrieverResult.Error(IllegalArgumentException("host parameter is not valid", it)) } + ?: host // We instantiate different stores for different sources, so that we can cache them separately. // The ESS_CONFIG source is cached with a prefix to differentiate it from the WELLKNOWN_ENDPOINT. diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt index 5151f17599..224c847387 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt @@ -14,18 +14,22 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.wellknown.test.FakeElementWellknownStore import io.element.android.features.wellknown.test.anElementWellKnown +import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.ElementWellKnownParser +import io.element.android.libraries.wellknown.api.ElementWellknownStore import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test +import java.net.URL class DefaultWellknownRetrieverTest { @Test @@ -68,6 +72,93 @@ class DefaultWellknownRetrieverTest { .isInstanceOf(WellknownRetrieverResult.NotFound::class.java) } + @Test + fun `get element wellknown will return an error if the provided host is not valid`() = runTest { + val sut = createDefaultWellknownRetriever() + assertThat(sut.getElementWellKnown("!not valid", EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) + .isInstanceOf(WellknownRetrieverResult.Error::class.java) + } + + @Test + fun `get element wellknown will return a cached value if present instead of fetching`() = runTest { + val host = URL(WELLKNOWN_URL).host.ensureProtocol() + val sut = createDefaultWellknownRetriever( + cacheStoreFactory = { FakeElementWellknownStore(initialData = mapOf(host to WellknownRetrieverResult.Success(expectedElementWellKnown))) } + ) + assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) + .isInstanceOf(WellknownRetrieverResult.Success::class.java) + } + + @Test + fun `get element wellknown will return an outdated value first then fetch the current one`() = runTest { + val host = URL(WELLKNOWN_URL).host.ensureProtocol() + val resolver = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) } + val sut = createDefaultWellknownRetriever( + urlResolver = resolver, + cacheStoreFactory = { FakeElementWellknownStore(initialData = mapOf(host to WellknownRetrieverResult.Outdated(expectedElementWellKnown))) } + ) + // The first call returns the outdated value + assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) + .isInstanceOf(WellknownRetrieverResult.Outdated::class.java) + // The resolver is not called yet because the fetch is done in the background + resolver.assertions().isNeverCalled() + + // We give it some time to run the background fetch + runCurrent() + + // The resolver is called during the background fetch + resolver.assertions().isCalledOnce().with(value(WELLKNOWN_URL)) + } + + @Test + fun `get element wellknown will return an error if it happens when trying to retrieve the cached value`() = runTest { + val host = URL(WELLKNOWN_URL).host.ensureProtocol() + val sut = createDefaultWellknownRetriever( + cacheStoreFactory = { FakeElementWellknownStore(initialData = mapOf(host to WellknownRetrieverResult.Error(IllegalStateException("BOOM")))) } + ) + assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) + .isInstanceOf(WellknownRetrieverResult.Error::class.java) + } + + @Test + fun `get element wellknown will return an error if it fails to serialize the retrieved value into the cache`() = runTest { + val sut = createDefaultWellknownRetriever( + elementWellKnownParser = { Result.failure(IllegalStateException("BOOM")) }, + ) + assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) + .isInstanceOf(WellknownRetrieverResult.Error::class.java) + } + + @Test + fun `WELLKNOWN_ENDPOINT source creates a store with no prefix`() = runTest { + val storeFactory = lambdaRecorder { FakeElementWellknownStore() } + val sut = createDefaultWellknownRetriever(cacheStoreFactory = storeFactory) + + sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT) + + storeFactory.assertions().isCalledOnce().with(value(null)) + } + + @Test + fun `ESS_CONFIG source creates a store with ess prefix and checks the ESS config endpoint`() = runTest { + val essConfigUrl = "https://ess-config.example.com" + val storeFactory = lambdaRecorder { FakeElementWellknownStore() } + val resolver = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) } + val sut = createDefaultWellknownRetriever( + cacheStoreFactory = storeFactory, + enterpriseService = FakeEnterpriseService( + overrideWellKnownResult = { null }, + essConfigEndpointUrlResult = { essConfigUrl }, + ), + urlResolver = resolver, + ) + + sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.ESS_CONFIG) + + storeFactory.assertions().isCalledOnce().with(value("ess_config")) + resolver.assertions().isCalledOnce().with(value(essConfigUrl)) + } + @Test fun `get element wellknown was overridden`() = runTest { val wellKnown = anElementWellKnown() @@ -91,12 +182,12 @@ class DefaultWellknownRetrieverTest { } private fun TestScope.createDefaultWellknownRetriever( - cacheStore: FakeElementWellknownStore = FakeElementWellknownStore(), + cacheStoreFactory: ElementWellknownStore.Factory = { FakeElementWellknownStore() }, elementWellKnownParser: ElementWellKnownParser = { Result.success(expectedElementWellKnown) }, enterpriseService: FakeEnterpriseService = FakeEnterpriseService(overrideWellKnownResult = { null }), urlResolver: UrlContentFetcher = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) }, ) = DefaultWellknownRetriever( - elementWellknownStoreFactory = { cacheStore }, + elementWellknownStoreFactory = cacheStoreFactory, enterpriseService = enterpriseService, elementWellKnownParser = elementWellKnownParser, urlContentFetcher = urlResolver, From f9834c0b03bc4e4b609e8ecafc675277392e0891 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 13:39:16 +0000 Subject: [PATCH 121/338] Update dependency org.matrix.rustcomponents:sdk-android to v26.08.11 --- 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 9dbb2dbd5f..3e2b0a902f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -185,7 +185,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.08.05" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.08.11" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } From b21c239a9d014ba32e682b41a2be9cef63d85431 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 13:39:26 +0000 Subject: [PATCH 122/338] Update kotlin to v2.3.11 --- 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 9dbb2dbd5f..fc6d29f2d0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ android_gradle_plugin = "9.3.1" # When updating this, please also update the version in the file ./idea/kotlinc.xml kotlin = "2.4.10" kotlinpoet = "2.3.0" -ksp = "2.3.10" +ksp = "2.3.11" firebaseAppDistribution = "5.3.0" # AndroidX From 749ecb4576ac2b4854f103210b1e72c29e95112e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 11 Aug 2026 16:28:07 +0200 Subject: [PATCH 123/338] No need for a companion object. --- .../impl/screens/qrcode/ShowQrCodeNode.kt | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt index 7032458860..5110f8938f 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt @@ -20,6 +20,13 @@ import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope +/** + * Max number of QrCode rotation. + * When QrCode is rotated this number of time, a TimeOut error is emitted. + * Set to 0 to disable QrCode rotation. + */ +private const val MAX_QR_CODE_ROTATION = 0 + @ContributesNode(SessionScope::class) @AssistedInject class ShowQrCodeNode( @@ -51,13 +58,4 @@ class ShowQrCodeNode( onBackClick = callback::navigateBack, ) } - - companion object { - /** - * Max number of QrCode rotation. - * When QrCode is rotated this number of time, a TimeOut error is emitted. - * Set to 0 to disable QrCode rotation. - */ - const val MAX_QR_CODE_ROTATION = 0 - } } From 223973805f434e2dc848a92dcc98703e2b95c3c8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 11 Aug 2026 16:30:11 +0200 Subject: [PATCH 124/338] Create function to limit code duplication. --- .../linknewdevice/impl/LinkNewDesktopHandler.kt | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt index 1b0883ec4e..7afb9a7623 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt @@ -46,11 +46,15 @@ class LinkNewDesktopHandler( private var timerJob: Job? = null private var handler: LinkDesktopHandler? = null - fun createNewHandler() { + private fun resetJobs() { currentJob?.cancel() currentJob = null timerJob?.cancel() timerJob = null + } + + fun createNewHandler() { + resetJobs() handler = matrixClient.createLinkDesktopHandler().getOrNull() timerJob = sessionScope.launch { delay(2.minutes) @@ -59,20 +63,14 @@ class LinkNewDesktopHandler( } fun reset() { - currentJob?.cancel() - currentJob = null - timerJob?.cancel() - timerJob = null + resetJobs() sessionScope.launch { linkDesktopStepFlow.emit(LinkDesktopStep.Uninitialized) } } fun onScannedCode(data: ByteArray) { - currentJob?.cancel() - currentJob = null - timerJob?.cancel() - timerJob = null + resetJobs() val currentHandler = handler if (currentHandler == null) { Timber.tag(loggerTag.value).e("onScannedCode: Handler is not initialized. Call createNewHandler() first.") From 055fea8d2541521bfb158ebd0e24b833bd1a0e89 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Tue, 11 Aug 2026 17:48:45 +0300 Subject: [PATCH 125/338] Hide the forward action on live location shares The timeline action sheet offered Forward on a live location share, but forwarding one always failed: Mode.Live maps to the SDK's MsgLikeKind.LiveLocation, which RoomContentForwarder rejects outright. So the action was shown for exactly the set of events on which it could never work. canBeForwarded() now returns true for a location event only when its mode is Static. Ended live shares keep Mode.Live and fail for the same reason, so they are excluded too. ActionListPresenter needs no change: its check is the function's only call site in the repo, so the single gate covers the timeline sheet, the threaded timeline and the pinned messages list. Hiding the action mirrors Element Web and the sticker exclusion already present in the same function. Converting a live share into a static m.location on forward would be a product decision and is out of scope. Relates to https://github.com/element-hq/element-x-android/issues/7190 --- .../model/event/TimelineItemEventContent.kt | 4 +- .../actionlist/ActionListPresenterTest.kt | 120 ++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt index c60e3847da..01146a070b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt @@ -58,10 +58,12 @@ fun TimelineItemEventContent.canBeForwarded(): Boolean = is TimelineItemFileContent, is TimelineItemAudioContent, is TimelineItemVideoContent, - is TimelineItemLocationContent, is TimelineItemVoiceContent, is TimelineItemGalleryContent, is TimelineItemAttachmentsContent -> true + // Live location shares can't be forwarded, the SDK rejects them, so we only show the option for static locations + // See https://github.com/element-hq/element-x-android/issues/7190 + is TimelineItemLocationContent -> mode is TimelineItemLocationContent.Mode.Static // Stickers can't be forwarded (yet) so we don't show the option // See https://github.com/element-hq/element-x-android/issues/2161 is TimelineItemStickerContent -> false diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt index fb597df290..e987a32480 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt @@ -21,7 +21,10 @@ import io.element.android.features.messages.impl.timeline.model.event.RtcNotific import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRtcNotificationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent +import io.element.android.features.messages.impl.timeline.model.event.aLiveLocationMode +import io.element.android.features.messages.impl.timeline.model.event.aStaticLocationMode import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent @@ -601,6 +604,123 @@ class ActionListPresenterTest { } } + @Test + fun `present - compute for a static location item`() = runTest { + val presenter = createActionListPresenter(isDeveloperModeEnabled = true) + presenter.test { + val initialState = awaitItem() + val messageEvent = aMessageEvent( + isMine = true, + isEditable = false, + content = aTimelineItemLocationContent(mode = aStaticLocationMode()), + ) + initialState.eventSink.invoke( + ActionListEvent.ComputeForMessage( + event = messageEvent, + userEventPermissions = aUserEventPermissions( + canRedactOwn = true, + canRedactOther = false, + canSendMessage = true, + canSendReaction = true, + canPinUnpin = true, + ), + ) + ) + val successState = awaitItem() + assertThat(successState.target).isEqualTo( + ActionListState.Target.Success( + event = messageEvent, + sentTimeFull = "0 Full true", + displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, + actions = persistentListOf( + TimelineItemAction.Reply, + TimelineItemAction.Forward, + TimelineItemAction.CopyLink, + TimelineItemAction.Pin, + TimelineItemAction.ViewSource, + TimelineItemAction.Redact, + ), + recentEmojis = suggestedEmojis, + ) + ) + initialState.eventSink.invoke(ActionListEvent.Clear) + assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None) + } + } + + @Test + fun `present - compute for a live location item does not offer to forward it`() = runTest { + val presenter = createActionListPresenter(isDeveloperModeEnabled = true) + presenter.test { + val initialState = awaitItem() + val messageEvent = aMessageEvent( + isMine = true, + isEditable = false, + content = aTimelineItemLocationContent(mode = aLiveLocationMode(isActive = true)), + ) + initialState.eventSink.invoke( + ActionListEvent.ComputeForMessage( + event = messageEvent, + userEventPermissions = aUserEventPermissions( + canRedactOwn = true, + canRedactOther = false, + canSendMessage = true, + canSendReaction = true, + canPinUnpin = true, + ), + ) + ) + val successState = awaitItem() + assertThat(successState.target).isEqualTo( + ActionListState.Target.Success( + event = messageEvent, + sentTimeFull = "0 Full true", + displayEmojiReactions = true, + verifiedUserSendFailure = VerifiedUserSendFailure.None, + actions = persistentListOf( + TimelineItemAction.Reply, + TimelineItemAction.CopyLink, + TimelineItemAction.Pin, + TimelineItemAction.ViewSource, + TimelineItemAction.Redact, + ), + recentEmojis = suggestedEmojis, + ) + ) + initialState.eventSink.invoke(ActionListEvent.Clear) + assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None) + } + } + + @Test + fun `present - compute for an ended live location item does not offer to forward it`() = runTest { + val presenter = createActionListPresenter(isDeveloperModeEnabled = true) + presenter.test { + val initialState = awaitItem() + val messageEvent = aMessageEvent( + isMine = true, + isEditable = false, + content = aTimelineItemLocationContent(mode = aLiveLocationMode(isActive = false)), + ) + initialState.eventSink.invoke( + ActionListEvent.ComputeForMessage( + event = messageEvent, + userEventPermissions = aUserEventPermissions( + canRedactOwn = true, + canRedactOther = false, + canSendMessage = true, + canSendReaction = true, + canPinUnpin = true, + ), + ) + ) + val successState = awaitItem() + assertThat((successState.target as ActionListState.Target.Success).actions) + .doesNotContain(TimelineItemAction.Forward) + } + } + @Test fun `present - compute for a media with caption item`() = runTest { val presenter = createActionListPresenter(isDeveloperModeEnabled = true) From 7c4e81679ff3d84845b06e56285db4d045a25a88 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Aug 2026 09:49:24 +0200 Subject: [PATCH 126/338] Animate visibility of the developer option entry point. Related to #6703 --- .../features/preferences/impl/root/PreferencesRootView.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index abde8c6bc5..2a37309e26 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -8,6 +8,7 @@ package io.element.android.features.preferences.impl.root +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope @@ -337,7 +338,9 @@ private fun ColumnScope.GeneralSection( ) } // Put developer settings at the end, so nothing bad happens if the user clicks 8 times to enable the entry - if (state.showDeveloperSettings) { + AnimatedVisibility( + visible = state.showDeveloperSettings, + ) { DeveloperPreferencesView(onOpenDeveloperSettings) } } From 7b0745c5f1eaf492d12e52d56ea741ef8fb9a8f4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Aug 2026 10:38:54 +0200 Subject: [PATCH 127/338] Fix layout issues. --- .../space/impl/addroom/AddRoomToSpaceView.kt | 51 ++++++++++++++----- .../roomselect/impl/RoomSelectView.kt | 18 ++++++- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt index eb485f11b5..2f75072d0a 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt @@ -12,10 +12,15 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items @@ -112,7 +117,13 @@ fun AddRoomToSpaceView( .consumeWindowInsets(paddingValues) ) { SearchBar( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .padding( + WindowInsets.safeDrawing + .only(WindowInsetsSides.Horizontal) + .asPaddingValues() + ), placeHolderTitle = stringResource(CommonStrings.action_search), queryState = state.searchQuery, active = state.isSearchActive, @@ -147,23 +158,35 @@ fun AddRoomToSpaceView( } if (!state.isSearchActive) { - Spacer(modifier = Modifier.height(16.dp)) - Text( - text = stringResource(R.string.screen_space_add_rooms_room_access_description), - color = ElementTheme.colors.textSecondary, - style = ElementTheme.typography.fontBodySmRegular, - modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp) - ) - if (state.selectedRooms.isNotEmpty()) { - SelectedRoomsRow( - selectedRooms = state.selectedRooms, - onRemoveRoom = ::onRoomToggled, - modifier = Modifier.padding(vertical = 16.dp) + Column( + modifier = Modifier + .fillMaxWidth() + .padding( + WindowInsets.safeDrawing + .only(WindowInsetsSides.Horizontal) + .asPaddingValues() + ) + ) { + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = stringResource(R.string.screen_space_add_rooms_room_access_description), + color = ElementTheme.colors.textSecondary, + style = ElementTheme.typography.fontBodySmRegular, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp) ) + if (state.selectedRooms.isNotEmpty()) { + SelectedRoomsRow( + selectedRooms = state.selectedRooms, + onRemoveRoom = ::onRoomToggled, + modifier = Modifier.padding(vertical = 16.dp) + ) + } } if (state.suggestions.isNotEmpty()) { - LazyColumn { + LazyColumn( + contentPadding = lazyColumnContentPadding, + ) { item { ListSectionHeader( title = stringResource(id = CommonStrings.common_suggestions), diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index e16cce6fd9..357d86dd5a 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -15,11 +15,16 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items @@ -144,7 +149,13 @@ fun RoomSelectView( .consumeWindowInsets(paddingValues) ) { SearchBar( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .padding( + WindowInsets.safeDrawing + .only(WindowInsetsSides.Horizontal) + .asPaddingValues() + ), placeHolderTitle = stringResource(CommonStrings.action_search), queryState = state.searchQuery, active = state.isSearchActive, @@ -184,7 +195,10 @@ fun RoomSelectView( showVerticalSpace = true, ) if (state.resultState is SearchBarResultState.Results) { - LazyColumn(state = lazyListState) { + LazyColumn( + state = lazyListState, + contentPadding = lazyColumnContentPadding, + ) { items(state.resultState.results, key = { it.roomId.value }) { roomSummary -> Column { RoomSummaryView( From de05495d51565b0c72cb4b9239e67b83de3bf2e8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Aug 2026 12:34:30 +0200 Subject: [PATCH 128/338] Setting version for the release 26.08.1 --- plugins/src/main/kotlin/Versions.kt | 2 +- tools/manifest/gplay/release/aaptDump.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 179ff7ac90..1c77bea7cf 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -45,7 +45,7 @@ private const val versionMonth = 8 * Release number in the month. Value must be in [0,99]. * Do not update this value. it is updated by the release script. */ -private const val versionReleaseNumber = 0 +private const val versionReleaseNumber = 1 object Versions { /** diff --git a/tools/manifest/gplay/release/aaptDump.txt b/tools/manifest/gplay/release/aaptDump.txt index 23f4acfb38..fcb3669a59 100644 --- a/tools/manifest/gplay/release/aaptDump.txt +++ b/tools/manifest/gplay/release/aaptDump.txt @@ -1,4 +1,4 @@ -package: name='io.element.android.x' versionCode='202608000' versionName='26.08.0' platformBuildVersionName='17' platformBuildVersionCode='37' compileSdkVersion='37' compileSdkVersionCodename='17' +package: name='io.element.android.x' versionCode='202608010' versionName='26.08.1' platformBuildVersionName='17' platformBuildVersionCode='37' compileSdkVersion='37' compileSdkVersionCodename='17' sdkVersion:'24' targetSdkVersion:'37' uses-permission: name='android.permission.POST_NOTIFICATIONS' From 2805fc6cc132ef0975e17f471e48e446c4a1b870 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Aug 2026 12:34:50 +0200 Subject: [PATCH 129/338] Adding fastlane file for version 26.08.1 --- fastlane/metadata/android/en-US/changelogs/202608010.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/202608010.txt diff --git a/fastlane/metadata/android/en-US/changelogs/202608010.txt b/fastlane/metadata/android/en-US/changelogs/202608010.txt new file mode 100644 index 0000000000..a4b397f1bb --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202608010.txt @@ -0,0 +1,2 @@ +Main changes in this version: bug fixes and improvements. +Full changelog: https://github.com/element-hq/element-x-android/releases \ No newline at end of file From c8bfabcaf5687e508ee61b82d5196a459d544e58 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Aug 2026 13:41:43 +0200 Subject: [PATCH 130/338] Changelog for version 26.08.1 --- CHANGES.md | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 3f7360a338..2955fc8ad7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,95 @@ +Changes in Element X v26.08.1 +============================= + + + +## What's Changed +### 🙌 Improvements +* Render push notifications for live location share starts (MSC4505) by @ara4n in https://github.com/element-hq/element-x-android/pull/7203 +* [Task] Show active call participants in the people list #7199 by @Eklavya47 in https://github.com/element-hq/element-x-android/pull/7300 +### 🐛 Bugfixes +* Fix overlap issue. by @bmarty in https://github.com/element-hq/element-x-android/pull/7331 +* Announce checked/unchecked state for user rows in invite screen by @bmarty in https://github.com/element-hq/element-x-android/pull/7136 +* Display 'add reactions' menu in the threads screen by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7388 +* Expand the emoji picker to full height when the keyboard is shown by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7382 +* UI fixes by @bmarty in https://github.com/element-hq/element-x-android/pull/7409 +### 🗣 Translations +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/7389 +### 🧱 Build +* Don't check enterprise previews in `KonsistPreviewTest` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7297 +* CI: split unit tests, screenshots and coverage in `tests.yml` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7296 +* Update Gradle Wrapper from 9.6.1 to 9.7.0 by @ElementBot in https://github.com/element-hq/element-x-android/pull/7335 +### 🚧 In development 🚧 +* QR code login iteration by @bmarty in https://github.com/element-hq/element-x-android/pull/6719 +* User status last pieces by @ganfra in https://github.com/element-hq/element-x-android/pull/7328 +* [Link new device] Add timeout by @bmarty in https://github.com/element-hq/element-x-android/pull/7338 +### Dependency upgrades +* Update dependency org.maplibre.compose:maplibre-compose to v0.13.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7291 +* Update android.gradle.plugin to v9.3.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7248 +* Update dependency io.sentry:sentry-android to v8.50.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7298 +* Update zizmorcore/zizmor-action action to v0.6.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7306 +* Update android.gradle.plugin to v9.3.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7304 +* Update dependency com.posthog:posthog-android to v3.56.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7311 +* Update dependencyAnalysis to v3.18.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7318 +* Update actions/stale action to v11 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7317 +* Update dependency io.github.zxing-cpp:android to v3.1.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7322 +* Update dependency org.matrix.rustcomponents:sdk-android to v26.08.05 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7324 +* Update dependency androidx.constraintlayout:constraintlayout to v2.2.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7329 +* Update reactivecircus/android-emulator-runner action to v2.38.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7189 +* Update dependency io.sentry:sentry-android to v8.51.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7301 +* Update dependency org.maplibre.gl:android-sdk to v13.4.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7305 +* Update dependency io.element.android:element-call-embedded to v0.23.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7323 +* Update metro to v1.4.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7334 +* Update dependency com.google.firebase:firebase-bom to v34.17.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7333 +* Update dependency androidx.compose.material3:material3 to v1.5.0-alpha23 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7175 +* Update dependency org.jsoup:jsoup to v1.23.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7332 +* Update dependency androidx.constraintlayout:constraintlayout-compose to v1.1.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7330 +* Update zizmorcore/zizmor-action action to v0.6.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7383 +* Update actions/setup-java action to v5.7.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7339 +* Update gradle/actions action to v6.3.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7384 +* Update plugin dependencycheck to v13 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7386 +* Update dependency org.matrix.rustcomponents:sdk-android to v26.08.11 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7402 +### 💼 Element Pro +* Add support for checking the ESS config in Element Pro by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7302 +### Others +* Add extra vertical padding to `ReplyToInvalidContent` and `ReplyToNotFoundContent` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7288 +* Index messages locally in an encrypted on-device store so encrypted rooms can be searched at all by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7249 +* Bump enterprise module to the latest commit by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7307 +* Bump enterprise module to the latest commit by @jennaharris7 in https://github.com/element-hq/element-x-android/pull/7309 +* Move deviceId and git details to developer settings by @bmarty in https://github.com/element-hq/element-x-android/pull/7241 +* Show a Message not found placeholder when a replied to message cannot be loaded by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7376 +* Update in reply to message when message is not found. by @bmarty in https://github.com/element-hq/element-x-android/pull/7387 +* Add default `OAUTH_CLIENT_URL_PATH` alternative by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7385 +* Fix the feature module template writing the impl build script into the api module by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7374 +* Let users enter their full Matrix user ID when searching for a homeserver by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7375 +* Keep manually revealed media visible when re-entering a room by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7378 +* Copy a blocked user's Matrix ID by long pressing them by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7372 +* Do not draw the media loading progress bar under the status bar by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7366 +* Do not notify about an event sent by another account on the same device by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7360 +* Keep the opened image on screen when the media gallery fails to load by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7362 +* Do not linkify the server part of a fediverse handle as an email address by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7356 +* Expose global account data get and set on MatrixClient by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7373 +* Show the alias or the direct message user's Matrix ID in the room picker by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7371 +* Use a dedicated string for the Mute room notification mode by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7367 +* Keep playing a voice message when the screen turns off by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7365 +* Let a voice message without a waveform be seeked by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7363 +* Ignore a second voice recording start while one is already running by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7346 +* Do not offer to start a call in a DM with no other member by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7352 +* Do not notify about a redaction whose target event is unknown by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7350 +* Ignore a room list filter that is incompatible with an already selected one by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7342 +* Stop a right to left sender name from reordering a room list preview by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7348 +* Do not append the application MapTiler key to a custom map style url by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7340 +* Hide space children you have left that can only be joined by invitation by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7369 +* Never re-encode a video at a higher bitrate than the source by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7351 +* Display push notifications for knocks by @ara4n in https://github.com/element-hq/element-x-android/pull/7207 + +## New Contributors +* @hayaksi1 made their first contribution in https://github.com/element-hq/element-x-android/pull/7249 +* @ara4n made their first contribution in https://github.com/element-hq/element-x-android/pull/7203 +* @Eklavya47 made their first contribution in https://github.com/element-hq/element-x-android/pull/7300 + +**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.08.0...v26.08.1 + Changes in Element X v26.08.0 ============================= From 6badc9634c2bcd40564e46a316b1f020113519b1 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 12 Aug 2026 09:24:59 +0100 Subject: [PATCH 131/338] build-rust-sdk: print the command we run It's helpful to know what exactly is being run. Also some tweaks to reduce the amount of directory changing that needs to happen. --- tools/sdk/build-rust-sdk | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tools/sdk/build-rust-sdk b/tools/sdk/build-rust-sdk index 91c6ed6e02..b89ae2cf42 100755 --- a/tools/sdk/build-rust-sdk +++ b/tools/sdk/build-rust-sdk @@ -215,25 +215,26 @@ if [ "$update_components" == "1" ]; then printf "\n## Resetting ${kotlinComponentsPath} to the latest main...\n\n" - cd "${kotlinComponentsPath}" - git reset --hard - git checkout main - git pull -else - cd "${kotlinComponentsPath}" + ( + cd "${kotlinComponentsPath}" + git reset --hard + git checkout main + git pull + ) fi ## Build the SDK printf "\n## Building the SDK for ${target_arch}...\n\n" -./scripts/build.sh \ +cmd=("${kotlinComponentsPath}/scripts/build.sh" \ -p "${rustSdkPath}" \ -m sdk \ -t "${target_arch}" \ - -o "${elementPwd}/libraries/rustsdk" + -o "${elementPwd}/libraries/rustsdk") -cd "${elementPwd}" +echo "$ ${cmd[@]}" +"${cmd[@]}" mv \ ./libraries/rustsdk/sdk-android-debug.aar \ From 76092fcba65962346f6bcf8db8279ad16aa87fa8 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 12 Aug 2026 09:40:41 +0100 Subject: [PATCH 132/338] fix lint --- tools/sdk/build-rust-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/sdk/build-rust-sdk b/tools/sdk/build-rust-sdk index b89ae2cf42..014aec6770 100755 --- a/tools/sdk/build-rust-sdk +++ b/tools/sdk/build-rust-sdk @@ -233,7 +233,7 @@ cmd=("${kotlinComponentsPath}/scripts/build.sh" \ -t "${target_arch}" \ -o "${elementPwd}/libraries/rustsdk") -echo "$ ${cmd[@]}" +echo "$" "${cmd[@]}" "${cmd[@]}" mv \ From d4863db03ecd3e3ee563201d50b17f59acdf7cda Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Wed, 12 Aug 2026 16:06:02 +0300 Subject: [PATCH 133/338] Group the GitHub issue body limit constant with an underscore The GITHUB_MAX_ISSUE_BODY_SIZE constant added with the description truncation is a five-digit literal, above detekt's acceptableLength of 4, so :features:rageshake:impl:detekt failed. Write it as 65_536, matching MAX_LOG_LINES_SIZE in RageshakeConfig. --- .../features/rageshake/impl/reporter/DefaultBugReporterTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fe32678c27..7bb2add0e5 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 @@ -597,7 +597,7 @@ class DefaultBugReporterTest : RobolectricTest() { companion object { private const val EXPECTED_NUMBER_OF_PROGRESS_VALUE = 18 - private const val GITHUB_MAX_ISSUE_BODY_SIZE = 65536 + private const val GITHUB_MAX_ISSUE_BODY_SIZE = 65_536 private const val TRUNCATION_MARKER_MAX_SIZE = 200 } } From 7c648b34b703a6d6ca077fc1294bbeec1f66f37b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 12 Aug 2026 16:04:24 +0200 Subject: [PATCH 134/338] Remove the legacy web-based account creation for matrix.org This was added as a temporary measure to allow matrix.org users to use Element X before it supported MAS, but it hasn't been needed for a very long time --- .../features/login/impl/LoginFlowNode.kt | 26 --- .../login/impl/login/LoginModePresenter.kt | 8 +- .../login/impl/login/LoginModeState.kt | 1 - .../login/impl/login/LoginModeView.kt | 3 - .../ChooseAccountProviderNode.kt | 2 - .../ChooseAccountProviderView.kt | 3 - .../impl/screens/classic/ClassicFlowNode.kt | 5 - .../loginwithclassic/LoginWithClassicNode.kt | 2 - .../loginwithclassic/LoginWithClassicView.kt | 3 - .../ConfirmAccountProviderNode.kt | 2 - .../ConfirmAccountProviderView.kt | 3 - .../createaccount/CreateAccountEvents.kt | 14 -- .../createaccount/CreateAccountNode.kt | 58 ------ .../createaccount/CreateAccountPresenter.kt | 82 -------- .../createaccount/CreateAccountState.kt | 20 -- .../CreateAccountStateProvider.kt | 34 ---- .../createaccount/CreateAccountView.kt | 176 ------------------ .../screens/createaccount/MessageParser.kt | 44 ----- .../MobileRegistrationResponse.kt | 28 --- .../WebViewMessageInterceptor.kt | 91 --------- .../impl/screens/onboarding/OnBoardingNode.kt | 2 - .../impl/screens/onboarding/OnBoardingView.kt | 3 - .../WebClientUrlForAuthenticationRetriever.kt | 55 ------ .../ChooseAccountProviderViewTest.kt | 2 - .../ConfirmAccountProviderPresenterTest.kt | 54 +----- .../CreateAccountPresenterTest.kt | 124 ------------ .../createaccount/DefaultMessageParserTest.kt | 87 --------- .../createaccount/FakeMessageParser.kt | 20 -- .../onboarding/OnBoardingPresenterTest.kt | 4 - .../screens/onboarding/OnboardingViewTest.kt | 18 -- ...eWebClientUrlForAuthenticationRetriever.kt | 19 -- 31 files changed, 3 insertions(+), 990 deletions(-) delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountEvents.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountNode.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountState.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountStateProvider.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountView.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MessageParser.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MobileRegistrationResponse.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/WebViewMessageInterceptor.kt delete mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt delete mode 100644 features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt delete mode 100644 features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt delete mode 100644 features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/FakeMessageParser.kt delete mode 100644 features/login/impl/src/test/kotlin/io/element/android/features/login/impl/web/FakeWebClientUrlForAuthenticationRetriever.kt diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index 978d28dfa3..a662f20351 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -37,7 +37,6 @@ import io.element.android.features.login.impl.screens.changeaccountprovider.Chan import io.element.android.features.login.impl.screens.chooseaccountprovider.ChooseAccountProviderNode import io.element.android.features.login.impl.screens.classic.ClassicFlowNode import io.element.android.features.login.impl.screens.confirmaccountprovider.ConfirmAccountProviderNode -import io.element.android.features.login.impl.screens.createaccount.CreateAccountNode import io.element.android.features.login.impl.screens.loginpassword.LoginPasswordNode import io.element.android.features.login.impl.screens.onboarding.OnBoardingNode import io.element.android.features.login.impl.screens.searchaccountprovider.SearchAccountProviderNode @@ -140,9 +139,6 @@ class LoginFlowNode( data class LoginPassword( val initialLogin: String = "", ) : NavTarget - - @Parcelize - data class CreateAccount(val url: String) : NavTarget } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -164,10 +160,6 @@ class LoginFlowNode( override fun navigateToOAuth(oAuthDetails: OAuthDetails) { navigateToMas(oAuthDetails) } - - override fun navigateToCreateAccount(url: String) { - backstack.push(NavTarget.CreateAccount(url)) - } } createNode(buildContext, listOf(callback)) } @@ -201,10 +193,6 @@ class LoginFlowNode( navigateToMas(oAuthDetails) } - override fun navigateToCreateAccount(url: String) { - backstack.push(NavTarget.CreateAccount(url)) - } - override fun navigateToDeveloperSettings() { backstack.push(NavTarget.AppDeveloperSettings) } @@ -247,10 +235,6 @@ class LoginFlowNode( navigateToMas(oAuthDetails) } - override fun navigateToCreateAccount(url: String) { - backstack.push(NavTarget.CreateAccount(url)) - } - override fun navigateToLoginPassword() { backstack.push(NavTarget.LoginPassword()) } @@ -274,10 +258,6 @@ class LoginFlowNode( navigateToMas(oAuthDetails) } - override fun navigateToCreateAccount(url: String) { - backstack.push(NavTarget.CreateAccount(url)) - } - override fun navigateToLoginPassword() { backstack.push(NavTarget.LoginPassword()) } @@ -324,12 +304,6 @@ class LoginFlowNode( ) createNode(buildContext, plugins = listOf(inputs)) } - is NavTarget.CreateAccount -> { - val inputs = CreateAccountNode.Inputs( - url = navTarget.url, - ) - createNode(buildContext, listOf(inputs)) - } } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModePresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModePresenter.kt index 34188095b0..466cd210ab 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModePresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModePresenter.kt @@ -20,7 +20,6 @@ import io.element.android.features.login.impl.screens.classic.loginwithclassic.L import io.element.android.features.login.impl.screens.confirmaccountprovider.ConfirmAccountProviderPresenter import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported import io.element.android.features.login.impl.screens.onboarding.OnBoardingPresenter -import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationRetriever import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -39,7 +38,6 @@ import io.element.android.libraries.oauth.api.OAuthActionFlow class LoginModePresenter( private val oAuthActionFlow: OAuthActionFlow, private val authenticationService: MatrixAuthenticationService, - private val webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever, private val localNetworkPermissionGate: LocalNetworkPermissionGate, ) : Presenter { @Composable @@ -90,11 +88,9 @@ class LoginModePresenter( authenticationService.getOAuthUrl(prompt = oAuthPrompt, loginHint = request.loginHint).getOrThrow() ) } - request.isAccountCreation -> LoginMode.AccountCreation( - webClientUrlForAuthenticationRetriever.retrieve(request.homeserverUrl) - ) + request.isAccountCreation -> throw AccountCreationNotSupported() matrixHomeServerDetails.supportsPasswordLogin -> LoginMode.PasswordLogin - else -> error("Unsupported login flow") + else -> error("Unsupported authentication flow") } }.getOrThrow() }.runCatchingUpdatingState( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeState.kt index 849ff31f85..86dd550ea6 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeState.kt @@ -20,5 +20,4 @@ data class LoginModeState( sealed interface LoginMode { data object PasswordLogin : LoginMode data class OAuth(val oAuthDetails: OAuthDetails) : LoginMode - data class AccountCreation(val url: String) : LoginMode } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt index 3549e17457..8b59db1d61 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt @@ -34,7 +34,6 @@ fun LoginModeView( onLearnMoreClick: () -> Unit, onOAuthDetails: (OAuthDetails) -> Unit, onNeedLoginPassword: () -> Unit, - onCreateAccountContinue: (url: String) -> Unit ) { val context = LocalContext.current when (loginMode) { @@ -120,7 +119,6 @@ fun LoginModeView( when (val loginModeData = loginMode.data) { is LoginMode.OAuth -> onOAuthDetails(loginModeData.oAuthDetails) LoginMode.PasswordLogin -> onNeedLoginPassword() - is LoginMode.AccountCreation -> onCreateAccountContinue(loginModeData.url) } // Also clear the data, to let the next screen be able to go back onClearError() @@ -139,7 +137,6 @@ internal fun LoginModeViewPreview(@PreviewParameter(LoginModeViewErrorProvider:: onLearnMoreClick = {}, onOAuthDetails = {}, onNeedLoginPassword = {}, - onCreateAccountContinue = {} ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt index 5f79f197d9..6432042818 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt @@ -32,7 +32,6 @@ class ChooseAccountProviderNode( interface Callback : Plugin { fun navigateToLoginPassword() fun navigateToOAuth(oAuthDetails: OAuthDetails) - fun navigateToCreateAccount(url: String) } private val callback: Callback = callback() @@ -48,7 +47,6 @@ class ChooseAccountProviderNode( onOAuthDetails = callback::navigateToOAuth, onNeedLoginPassword = callback::navigateToLoginPassword, onLearnMoreClick = { openLearnMorePage(context) }, - onCreateAccountContinue = callback::navigateToCreateAccount, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt index e858789bb4..0d9d8cfba7 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt @@ -57,7 +57,6 @@ fun ChooseAccountProviderView( onOAuthDetails: (OAuthDetails) -> Unit, onNeedLoginPassword: () -> Unit, onLearnMoreClick: () -> Unit, - onCreateAccountContinue: (url: String) -> Unit, modifier: Modifier = Modifier, ) { val isLoading by remember(state.loginModeState.loginMode) { @@ -137,7 +136,6 @@ fun ChooseAccountProviderView( onLearnMoreClick = onLearnMoreClick, onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, - onCreateAccountContinue = onCreateAccountContinue, ) } } @@ -161,6 +159,5 @@ internal fun ChooseAccountProviderViewPreview(@PreviewParameter(ChooseAccountPro onLearnMoreClick = { }, onOAuthDetails = { }, onNeedLoginPassword = { }, - onCreateAccountContinue = { }, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNode.kt index cfbd86f363..160b500cf6 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNode.kt @@ -55,7 +55,6 @@ class ClassicFlowNode( fun navigateToOnBoarding(allowBackNavigation: Boolean) fun navigateToLoginPassword() fun navigateToOAuth(oAuthDetails: OAuthDetails) - fun navigateToCreateAccount(url: String) } sealed interface NavTarget : Parcelable { @@ -115,10 +114,6 @@ class ClassicFlowNode( callback.navigateToOAuth(oAuthDetails) } - override fun navigateToCreateAccount(url: String) { - callback.navigateToCreateAccount(url) - } - override fun navigateToMissingKeyBackup() { backstack.push(NavTarget.MissingKeyBackup) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicNode.kt index d5acca38ae..ff56b0e8e8 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicNode.kt @@ -36,7 +36,6 @@ class LoginWithClassicNode( fun navigateToOtherOptions() fun navigateToLoginPassword() fun navigateToOAuth(oAuthDetails: OAuthDetails) - fun navigateToCreateAccount(url: String) fun navigateToMissingKeyBackup() } @@ -63,7 +62,6 @@ class LoginWithClassicNode( onOAuthDetails = callback::navigateToOAuth, onNeedLoginPassword = callback::navigateToLoginPassword, onLearnMoreClick = { openLearnMorePage(context) }, - onCreateAccountContinue = callback::navigateToCreateAccount, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt index 75181a068e..81638b4be0 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt @@ -64,7 +64,6 @@ fun LoginWithClassicView( onOAuthDetails: (OAuthDetails) -> Unit, onNeedLoginPassword: () -> Unit, onLearnMoreClick: () -> Unit, - onCreateAccountContinue: (url: String) -> Unit, modifier: Modifier = Modifier, ) { val isLoading by remember(state.loginModeState.loginMode) { @@ -204,7 +203,6 @@ fun LoginWithClassicView( onLearnMoreClick = onLearnMoreClick, onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, - onCreateAccountContinue = onCreateAccountContinue, ) LocalNetworkPermissionDialogView( dialog = state.loginModeState.localNetworkPermissionDialog, @@ -226,6 +224,5 @@ internal fun LoginWithClassicViewPreview(@PreviewParameter(LoginWithClassicState onOAuthDetails = {}, onNeedLoginPassword = {}, onLearnMoreClick = {}, - onCreateAccountContinue = {}, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt index 928a493dc1..0accaea767 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt @@ -45,7 +45,6 @@ class ConfirmAccountProviderNode( interface Callback : Plugin { fun navigateToLoginPassword() fun navigateToOAuth(oAuthDetails: OAuthDetails) - fun navigateToCreateAccount(url: String) fun navigateToChangeAccountProvider() } @@ -60,7 +59,6 @@ class ConfirmAccountProviderNode( modifier = modifier, onOAuthDetails = callback::navigateToOAuth, onNeedLoginPassword = callback::navigateToLoginPassword, - onCreateAccountContinue = callback::navigateToCreateAccount, onChange = callback::navigateToChangeAccountProvider, onLearnMoreClick = { openLearnMorePage(context) }, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index 95fbff25ab..3702b195a1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -43,7 +43,6 @@ fun ConfirmAccountProviderView( onOAuthDetails: (OAuthDetails) -> Unit, onNeedLoginPassword: () -> Unit, onLearnMoreClick: () -> Unit, - onCreateAccountContinue: (url: String) -> Unit, onChange: () -> Unit, modifier: Modifier = Modifier, ) { @@ -107,7 +106,6 @@ fun ConfirmAccountProviderView( onLearnMoreClick = onLearnMoreClick, onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, - onCreateAccountContinue = onCreateAccountContinue, ) } @@ -131,7 +129,6 @@ internal fun ConfirmAccountProviderViewPreview( state = state, onOAuthDetails = {}, onNeedLoginPassword = {}, - onCreateAccountContinue = {}, onLearnMoreClick = {}, onChange = {}, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountEvents.kt deleted file mode 100644 index a51557347e..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountEvents.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -sealed interface CreateAccountEvents { - data class SetPageProgress(val progress: Int) : CreateAccountEvents - data class OnMessageReceived(val message: String) : CreateAccountEvents -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountNode.kt deleted file mode 100644 index e1972e5a0f..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountNode.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -import android.app.Activity -import androidx.activity.compose.LocalActivity -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.AssistedInject -import io.element.android.annotations.ContributesNode -import io.element.android.compound.theme.ElementTheme -import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab -import io.element.android.libraries.architecture.NodeInputs -import io.element.android.libraries.architecture.inputs - -@ContributesNode(AppScope::class) -@AssistedInject -class CreateAccountNode( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, - presenterFactory: CreateAccountPresenter.Factory, -) : Node(buildContext, plugins = plugins) { - data class Inputs( - val url: String, - ) : NodeInputs - - private val presenter = presenterFactory.create(inputs().url) - - private fun onOpenExternalUrl(activity: Activity, darkTheme: Boolean, url: String) { - activity.openUrlInChromeCustomTab(null, darkTheme, url) - } - - @Composable - override fun View(modifier: Modifier) { - val activity = requireNotNull(LocalActivity.current) - val isDark = ElementTheme.isLightTheme.not() - val state = presenter.present() - CreateAccountView( - state = state, - modifier = modifier, - onBackClick = ::navigateUp, - onOpenExternalUrl = { - onOpenExternalUrl(activity, isDark, it) - }, - ) - } -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt deleted file mode 100644 index bf7af111f4..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.AssistedInject -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.core.extensions.flatMap -import io.element.android.libraries.core.extensions.runCatchingExceptions -import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService -import io.element.android.libraries.matrix.api.core.SessionId -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -@AssistedInject -class CreateAccountPresenter( - @Assisted private val url: String, - private val authenticationService: MatrixAuthenticationService, - private val messageParser: MessageParser, - private val buildMeta: BuildMeta, -) : Presenter { - @AssistedFactory - interface Factory { - fun create(url: String): CreateAccountPresenter - } - - @Composable - override fun present(): CreateAccountState { - val coroutineScope = rememberCoroutineScope() - val pageProgress: MutableState = remember { mutableIntStateOf(0) } - val createAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - - fun handleEvent(event: CreateAccountEvents) { - when (event) { - is CreateAccountEvents.SetPageProgress -> { - pageProgress.value = event.progress - } - is CreateAccountEvents.OnMessageReceived -> { - // Ignore unexpected message - if (event.message.contains("isTrusted")) return - coroutineScope.importSession(event.message, createAction) - } - } - } - - return CreateAccountState( - url = url, - pageProgress = pageProgress.value, - isDebugBuild = buildMeta.isDebuggable, - createAction = createAction.value, - eventSink = ::handleEvent, - ) - } - - private fun CoroutineScope.importSession(message: String, loggedInState: MutableState>) = launch { - loggedInState.value = AsyncAction.Loading - runCatchingExceptions { - messageParser.parse(message) - }.flatMap { externalSession -> - authenticationService.importCreatedSession(externalSession) - }.onSuccess { sessionId -> - loggedInState.value = AsyncAction.Success(sessionId) - }.onFailure { failure -> - loggedInState.value = AsyncAction.Failure(failure) - } - } -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountState.kt deleted file mode 100644 index de7efe573e..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountState.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.matrix.api.core.SessionId - -data class CreateAccountState( - val url: String, - val pageProgress: Int, - val createAction: AsyncAction, - val isDebugBuild: Boolean, - val eventSink: (CreateAccountEvents) -> Unit -) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountStateProvider.kt deleted file mode 100644 index 976ceae70d..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountStateProvider.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.matrix.api.core.SessionId - -open class CreateAccountStateProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - aCreateAccountState(), - aCreateAccountState(pageProgress = 33), - aCreateAccountState(createAction = AsyncAction.Loading), - aCreateAccountState(createAction = AsyncAction.Failure(RuntimeException("Failed to create account"))), - ) -} - -private fun aCreateAccountState( - pageProgress: Int = 100, - createAction: AsyncAction = AsyncAction.Uninitialized, -) = CreateAccountState( - url = "https://example.com", - isDebugBuild = true, - pageProgress = pageProgress, - createAction = createAction, - eventSink = {} -) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountView.kt deleted file mode 100644 index 051188627a..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountView.kt +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -import android.annotation.SuppressLint -import android.view.ViewGroup -import android.webkit.JsResult -import android.webkit.WebChromeClient -import android.webkit.WebView -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp -import androidx.compose.ui.viewinterop.AndroidView -import io.element.android.compound.theme.ElementTheme -import io.element.android.features.login.impl.R -import io.element.android.libraries.designsystem.components.async.AsyncActionView -import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator -import io.element.android.libraries.designsystem.theme.components.Scaffold -import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor -import timber.log.Timber - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun CreateAccountView( - state: CreateAccountState, - onBackClick: () -> Unit, - onOpenExternalUrl: (String) -> Unit, - modifier: Modifier = Modifier, -) { - Scaffold( - modifier = modifier, - topBar = { - TopAppBar( - titleStr = stringResource(R.string.screen_create_account_title), - navigationIcon = { - BackButton(onClick = onBackClick) - }, - ) - } - ) { contentPadding -> - Box( - modifier = Modifier - .padding(contentPadding) - .consumeWindowInsets(contentPadding) - .fillMaxSize() - ) { - CreateAccountWebView( - modifier = Modifier - .fillMaxSize(), - state = state, - onWebViewCreate = { webView -> - WebViewMessageInterceptor( - webView, - state.isDebugBuild, - onOpenExternalUrl = onOpenExternalUrl, - onMessage = { - state.eventSink(CreateAccountEvents.OnMessageReceived(it)) - }, - ) - } - ) - AnimatedVisibility( - visible = state.pageProgress != 100, - // Disable enter animation - enter = fadeIn(initialAlpha = 1f), - exit = fadeOut(), - ) { - LinearProgressIndicator( - modifier = Modifier - .fillMaxWidth() - .height(2.dp), - progress = { state.pageProgress / 100f }, - trackColor = ElementTheme.colors.progressIndicatorTrackColor, - ) - } - } - } - - AsyncActionView( - async = state.createAction, - onSuccess = {}, - onErrorDismiss = onBackClick, - onRetry = null - ) -} - -@Composable -private fun CreateAccountWebView( - state: CreateAccountState, - onWebViewCreate: (WebView) -> Unit, - modifier: Modifier = Modifier, -) { - if (LocalInspectionMode.current) { - Box(modifier = modifier, contentAlignment = Alignment.Center) { - Text("WebView - can't be previewed") - } - } else { - AndroidView( - modifier = modifier, - factory = { context -> - WebView(context).apply { - onWebViewCreate(this) - setup(state) - } - }, - update = { webView -> - if (webView.url != state.url) { - webView.loadUrl(state.url) - } - }, - onRelease = { webView -> - webView.destroy() - } - ) - } -} - -@SuppressLint("SetJavaScriptEnabled") -private fun WebView.setup(state: CreateAccountState) { - layoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ) - with(settings) { - javaScriptEnabled = true - domStorageEnabled = true - } - - webChromeClient = object : WebChromeClient() { - override fun onProgressChanged(view: WebView?, newProgress: Int) { - super.onProgressChanged(view, newProgress) - state.eventSink(CreateAccountEvents.SetPageProgress(newProgress)) - } - - override fun onJsBeforeUnload(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean { - Timber.w("onJsBeforeUnload, cancelling the dialog, we will open external links in a Custom Chrome Tab") - result?.confirm() - return true - } - } -} - -@PreviewsDayNight -@Composable -internal fun CreateAccountViewPreview(@PreviewParameter(CreateAccountStateProvider::class) state: CreateAccountState) = ElementPreview { - CreateAccountView( - state = state, - onBackClick = {}, - onOpenExternalUrl = {}, - ) -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MessageParser.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MessageParser.kt deleted file mode 100644 index d7954373bc..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MessageParser.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.ContributesBinding -import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource -import io.element.android.libraries.androidutils.json.JsonProvider -import io.element.android.libraries.matrix.api.auth.external.ExternalSession - -interface MessageParser { - /** - * Parse the message and return the ExternalSession object, or - * throw an exception if the message is invalid. - */ - fun parse(message: String): ExternalSession -} - -@ContributesBinding(AppScope::class) -class DefaultMessageParser( - private val accountProviderDataSource: AccountProviderDataSource, - private val json: JsonProvider, -) : MessageParser { - override fun parse(message: String): ExternalSession { - val response = json().decodeFromString(MobileRegistrationResponse.serializer(), message) - val userId = response.userId ?: error("No user ID in response") - val homeServer = response.homeServer ?: accountProviderDataSource.flow.value.url - val accessToken = response.accessToken ?: error("No access token in response") - val deviceId = response.deviceId ?: error("No device ID in response") - return ExternalSession( - userId = userId, - homeserverUrl = homeServer, - accessToken = accessToken, - deviceId = deviceId, - refreshToken = null, - ) - } -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MobileRegistrationResponse.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MobileRegistrationResponse.kt deleted file mode 100644 index 5e78b1bd37..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MobileRegistrationResponse.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -/** - * For ref: - * https://github.com/element-hq/matrix-react-sdk/pull/42/files#diff-2bbba5a742004fd4e924a639ded444279f66f7ad890cb669fbc91ac6b8638c64R56 - */ -@Serializable -data class MobileRegistrationResponse( - @SerialName("user_id") - val userId: String? = null, - @SerialName("home_server") - val homeServer: String? = null, - @SerialName("access_token") - val accessToken: String? = null, - @SerialName("device_id") - val deviceId: String? = null, -) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/WebViewMessageInterceptor.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/WebViewMessageInterceptor.kt deleted file mode 100644 index 20d503522b..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/WebViewMessageInterceptor.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -import android.graphics.Bitmap -import android.webkit.JavascriptInterface -import android.webkit.WebResourceRequest -import android.webkit.WebView -import android.webkit.WebViewClient -import androidx.webkit.WebViewCompat -import androidx.webkit.WebViewFeature - -class WebViewMessageInterceptor( - webView: WebView, - private val debugLog: Boolean, - private val onOpenExternalUrl: (String) -> Unit, - private val onMessage: (String) -> Unit, -) { - companion object { - // We call both the WebMessageListener and the JavascriptInterface objects in JS with this - // 'listenerName' so they can both receive the data from the WebView when - // `${LISTENER_NAME}.postMessage(...)` is called - const val LISTENER_NAME = "elementX" - } - - init { - webView.webViewClient = object : WebViewClient() { - override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { - super.onPageStarted(view, url, favicon) - - // We inject this JS code when the page starts loading to attach a message listener to the window. - view?.evaluateJavascript( - """ - window.addEventListener( - "mobileregistrationresponse", - (event) => { - let json = JSON.stringify(event.detail) - ${"console.log('message sent: ' + json);".takeIf { debugLog }} - $LISTENER_NAME.postMessage(json); - }, - false, - ); - """.trimIndent(), - null - ) - } - - override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { - request ?: return false - // Load the URL in a Chrome Custom Tab, and return true to cancel the load - onOpenExternalUrl(request.url.toString()) - return true - } - } - - // Use WebMessageListener if supported, otherwise use JavascriptInterface - if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) { - // Create a WebMessageListener, which will receive messages from the WebView and reply to them - val webMessageListener = WebViewCompat.WebMessageListener { _, message, _, _, _ -> - onMessageReceived(message.data) - } - WebViewCompat.addWebMessageListener( - webView, - LISTENER_NAME, - setOf("*"), - webMessageListener - ) - } else { - webView.addJavascriptInterface( - object { - @JavascriptInterface - fun postMessage(json: String?) { - onMessageReceived(json) - } - }, - LISTENER_NAME, - ) - } - } - - private fun onMessageReceived(json: String?) { - // Here is where we would handle the messages from the WebView, passing them to the listener - json?.let { onMessage(it) } - } -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt index 99f7e86fd3..244e684c5b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt @@ -41,7 +41,6 @@ class OnBoardingNode( fun navigateToBugReport() fun navigateToLoginPassword() fun navigateToOAuth(oAuthDetails: OAuthDetails) - fun navigateToCreateAccount(url: String) fun navigateToDeveloperSettings() fun onDone() } @@ -74,7 +73,6 @@ class OnBoardingNode( onOAuthDetails = callback::navigateToOAuth, onNeedLoginPassword = callback::navigateToLoginPassword, onLearnMoreClick = { openLearnMorePage(context) }, - onCreateAccountContinue = callback::navigateToCreateAccount, onBackClick = callback::onDone, onDeveloperSettingsClick = callback::navigateToDeveloperSettings, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt index bf96b2dbe0..10fe3a854c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt @@ -71,7 +71,6 @@ fun OnBoardingView( onOAuthDetails: (OAuthDetails) -> Unit, onNeedLoginPassword: () -> Unit, onLearnMoreClick: () -> Unit, - onCreateAccountContinue: (url: String) -> Unit, onReportProblem: () -> Unit, modifier: Modifier = Modifier, ) { @@ -84,7 +83,6 @@ fun OnBoardingView( onLearnMoreClick = onLearnMoreClick, onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, - onCreateAccountContinue = onCreateAccountContinue, ) LocalNetworkPermissionDialogView( dialog = state.loginModeState.localNetworkPermissionDialog, @@ -366,6 +364,5 @@ internal fun OnBoardingViewPreview( onOAuthDetails = {}, onNeedLoginPassword = {}, onLearnMoreClick = {}, - onCreateAccountContinue = {}, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt deleted file mode 100644 index 2902126417..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.web - -import androidx.core.net.toUri -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.ContributesBinding -import io.element.android.appconfig.AuthenticationConfig -import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported -import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory -import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource -import io.element.android.libraries.wellknown.api.WellknownRetriever -import timber.log.Timber - -interface WebClientUrlForAuthenticationRetriever { - suspend fun retrieve(homeServerUrl: String): String -} - -@ContributesBinding(AppScope::class) -class DefaultWebClientUrlForAuthenticationRetriever( - private val wellknownRetrieverFactory: WellknownRetriever.Factory, - private val temporaryMatrixClientFactory: TemporaryMatrixClientFactory, -) : WebClientUrlForAuthenticationRetriever { - override suspend fun retrieve(homeServerUrl: String): String { - if (homeServerUrl != AuthenticationConfig.MATRIX_ORG_URL) { - Timber.w("Temporary account creation flow is only supported on matrix.org") - throw AccountCreationNotSupported() - } - val temporaryMatrixClient = temporaryMatrixClientFactory.create(homeServerUrl).getOrThrow() - return temporaryMatrixClient.use { - val wellknownRetriever = wellknownRetrieverFactory.create(temporaryMatrixClient) - val wellknown = wellknownRetriever.getElementWellKnown( - host = homeServerUrl, - source = EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT - ).dataOrNull() ?: throw AccountCreationNotSupported() - - val registrationHelperUrl = wellknown.registrationHelperUrl - if (registrationHelperUrl != null) { - registrationHelperUrl.toUri() - .buildUpon() - .appendQueryParameter("hs_url", homeServerUrl) - .build() - .toString() - } else { - throw AccountCreationNotSupported() - } - } - } -} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt index f1ce703d1c..cd64638563 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt @@ -84,7 +84,6 @@ class ChooseAccountProviderViewTest : RobolectricTest() { onOAuthDetails: (OAuthDetails) -> Unit = EnsureNeverCalledWithParam(), onNeedLoginPassword: () -> Unit = EnsureNeverCalled(), onLearnMoreClick: () -> Unit = EnsureNeverCalled(), - onCreateAccountContinue: (url: String) -> Unit = EnsureNeverCalledWithParam(), ) { setContent { ChooseAccountProviderView( @@ -93,7 +92,6 @@ class ChooseAccountProviderViewTest : RobolectricTest() { onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, onLearnMoreClick = onLearnMoreClick, - onCreateAccountContinue = onCreateAccountContinue, ) } } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index c20896870b..52858b77af 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -15,8 +15,6 @@ import io.element.android.features.login.impl.accountprovider.AccountProviderDat import io.element.android.features.login.impl.login.LoginMode import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported import io.element.android.features.login.impl.screens.onboarding.createLoginModePresenter -import io.element.android.features.login.impl.web.FakeWebClientUrlForAuthenticationRetriever -import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationRetriever import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.test.AN_EXCEPTION @@ -283,7 +281,7 @@ class ConfirmAccountProviderPresenterTest { } @Test - fun `present - confirm account creation without oidc and without url generates an error`() = runTest { + fun `present - confirm account creation without oidc generates an error`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { Result.success(aMatrixHomeServerDetails()) @@ -292,9 +290,6 @@ class ConfirmAccountProviderPresenterTest { val presenter = createConfirmAccountProviderPresenter( params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), matrixAuthenticationService = authenticationService, - webClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever { - throw AccountCreationNotSupported() - }, ) presenter.test { val initialState = awaitItem() @@ -331,64 +326,17 @@ class ConfirmAccountProviderPresenterTest { } } - @Test - fun `present - confirm account creation with OAuth and url continues with OAuth`() = runTest { - val aUrl = "aUrl" - val authenticationService = FakeMatrixAuthenticationService( - setHomeserverResult = { - Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) - }, - ) - val presenter = createConfirmAccountProviderPresenter( - params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), - matrixAuthenticationService = authenticationService, - webClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever { aUrl }, - ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.Continue) - skipItems(1) // Loading - val submittedState = awaitItem() - assertThat(submittedState.loginModeState.loginMode).isInstanceOf(AsyncData.Success::class.java) - assertThat(submittedState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) - } - } - - @Test - fun `present - confirm account creation without OAuth and with url continuing with url`() = runTest { - val aUrl = "aUrl" - val authenticationService = FakeMatrixAuthenticationService( - setHomeserverResult = { - Result.success(aMatrixHomeServerDetails()) - }, - ) - val presenter = createConfirmAccountProviderPresenter( - params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), - matrixAuthenticationService = authenticationService, - webClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever { aUrl }, - ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.Continue) - skipItems(1) // Loading - val submittedState = awaitItem() - assertThat(submittedState.loginModeState.loginMode.dataOrNull()).isEqualTo(LoginMode.AccountCreation(aUrl)) - } - } - private fun createConfirmAccountProviderPresenter( params: ConfirmAccountProviderPresenter.Params = ConfirmAccountProviderPresenter.Params(isAccountCreation = false), accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), matrixAuthenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), defaultOAuthActionFlow: OAuthActionFlow = FakeOAuthActionFlow(), - webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever(), ) = ConfirmAccountProviderPresenter( params = params, accountProviderDataSource = accountProviderDataSource, loginModePresenter = createLoginModePresenter( authenticationService = matrixAuthenticationService, oAuthActionFlow = defaultOAuthActionFlow, - webClientUrlForAuthenticationRetriever = webClientUrlForAuthenticationRetriever, ), ) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt deleted file mode 100644 index c70a5c9a9a..0000000000 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService -import io.element.android.libraries.matrix.api.auth.external.ExternalSession -import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus -import io.element.android.libraries.matrix.test.AN_EXCEPTION -import io.element.android.libraries.matrix.test.A_SESSION_ID -import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService -import io.element.android.libraries.matrix.test.core.aBuildMeta -import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService -import io.element.android.tests.testutils.WarmUpRule -import io.element.android.tests.testutils.lambda.lambdaRecorder -import io.element.android.tests.testutils.lambda.value -import io.element.android.tests.testutils.test -import kotlinx.coroutines.test.runTest -import org.junit.Rule -import org.junit.Test - -class CreateAccountPresenterTest { - @get:Rule - val warmUpRule = WarmUpRule() - - @Test - fun `present - initial state`() = runTest { - val presenter = createPresenter() - presenter.test { - val initialState = awaitItem() - assertThat(initialState.url).isEqualTo("aUrl") - assertThat(initialState.pageProgress).isEqualTo(0) - assertThat(initialState.createAction).isEqualTo(AsyncAction.Uninitialized) - assertThat(initialState.isDebugBuild).isTrue() - } - } - - @Test - fun `present - set up progress update the state`() = runTest { - val presenter = createPresenter() - presenter.test { - val initialState = awaitItem() - initialState.eventSink(CreateAccountEvents.SetPageProgress(33)) - assertThat(awaitItem().pageProgress).isEqualTo(33) - } - } - - @Test - fun `present - receiving a message not able to be parsed change the state to error`() = runTest { - val presenter = createPresenter( - messageParser = FakeMessageParser { error("An error") } - ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink(CreateAccountEvents.OnMessageReceived("")) - assertThat(awaitItem().createAction).isInstanceOf(AsyncAction.Failure::class.java) - } - } - - @Test - fun `present - receiving a message containing isTrusted is ignored`() = runTest { - val presenter = createPresenter() - presenter.test { - val initialState = awaitItem() - initialState.eventSink(CreateAccountEvents.OnMessageReceived("isTrusted")) - } - } - - @Test - fun `present - receiving a message able to be parsed change the state to success`() = runTest { - val lambda = lambdaRecorder { _ -> anExternalSession() } - val sessionVerificationService = FakeSessionVerificationService() - val presenter = createPresenter( - authenticationService = FakeMatrixAuthenticationService( - importCreatedSessionLambda = { Result.success(A_SESSION_ID) } - ), - messageParser = FakeMessageParser(lambda), - ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink(CreateAccountEvents.OnMessageReceived("aMessage")) - assertThat(awaitItem().createAction.isLoading()).isTrue() - sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.Verified) - assertThat(awaitItem().createAction.dataOrNull()).isEqualTo(A_SESSION_ID) - } - lambda.assertions().isCalledOnce().with(value("aMessage")) - } - - @Test - fun `present - receiving a message able to be parsed but error in importing change the state to error`() = runTest { - val presenter = createPresenter( - authenticationService = FakeMatrixAuthenticationService( - importCreatedSessionLambda = { Result.failure(AN_EXCEPTION) } - ), - messageParser = FakeMessageParser { anExternalSession() } - ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink(CreateAccountEvents.OnMessageReceived("")) - assertThat(awaitItem().createAction.isLoading()).isTrue() - assertThat(awaitItem().createAction.errorOrNull()).isNotNull() - } - } - - private fun createPresenter( - url: String = "aUrl", - authenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), - messageParser: MessageParser = FakeMessageParser(), - buildMeta: BuildMeta = aBuildMeta(), - ) = CreateAccountPresenter( - url = url, - authenticationService = authenticationService, - messageParser = messageParser, - buildMeta = buildMeta, - ) -} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt deleted file mode 100644 index 680fda2838..0000000000 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -import com.google.common.truth.Truth.assertThat -import io.element.android.appconfig.AuthenticationConfig -import io.element.android.features.enterprise.test.FakeEnterpriseService -import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource -import io.element.android.libraries.androidutils.json.DefaultJsonProvider -import io.element.android.libraries.matrix.api.auth.external.ExternalSession -import kotlinx.serialization.SerializationException -import org.junit.Assert.assertThrows -import org.junit.Test - -class DefaultMessageParserTest { - private val validMessage = """ - { - "user_id": "user_id", - "home_server": "home_server", - "access_token": "access_token", - "device_id": "device_id" - } - """.trimIndent() - - @Test - fun `DefaultMessageParser is able to parse correct message`() { - val sut = createDefaultMessageParser() - assertThat(sut.parse(validMessage)).isEqualTo( - anExternalSession( - homeserverUrl = "home_server", - ) - ) - } - - @Test - fun `DefaultMessageParser should throw Exception in case of error`() { - val sut = createDefaultMessageParser() - // kotlinx.serialization.json.internal.JsonDecodingException - assertThrows(SerializationException::class.java) { sut.parse("invalid json") } - // missing userId - assertThrows(IllegalStateException::class.java) { sut.parse(validMessage.replace(""""user_id": "user_id",""", "")) } - // missing accessToken - assertThrows(IllegalStateException::class.java) { sut.parse(validMessage.replace(""""access_token": "access_token",""", "")) } - // missing deviceId - assertThrows(IllegalStateException::class.java) { - sut.parse( - validMessage - .replace(""""access_token": "access_token",""", """"access_token": "access_token"""") - .replace(""""device_id": "device_id"""", "") - ) - } - } - - @Test - fun `DefaultMessageParser should be successful even is homeserver url is missing`() { - val sut = createDefaultMessageParser() - // missing homeServer - assertThat(sut.parse(validMessage.replace(""""home_server": "home_server",""", ""))).isEqualTo( - anExternalSession( - homeserverUrl = AuthenticationConfig.MATRIX_ORG_URL, - ) - ) - } - - private fun createDefaultMessageParser(): DefaultMessageParser { - return DefaultMessageParser( - accountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), - json = DefaultJsonProvider(), - ) - } -} - -internal fun anExternalSession( - homeserverUrl: String = "home_server", -) = ExternalSession( - userId = "user_id", - homeserverUrl = homeserverUrl, - accessToken = "access_token", - deviceId = "device_id", - refreshToken = null, -) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/FakeMessageParser.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/FakeMessageParser.kt deleted file mode 100644 index 62d6dd7dd4..0000000000 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/FakeMessageParser.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.screens.createaccount - -import io.element.android.libraries.matrix.api.auth.external.ExternalSession -import io.element.android.tests.testutils.lambda.lambdaError - -class FakeMessageParser( - private val parseResult: (String) -> ExternalSession = { lambdaError() } -) : MessageParser { - override fun parse(message: String): ExternalSession { - return parseResult(message) - } -} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt index 782ff00ec0..fbee19f05e 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt @@ -17,8 +17,6 @@ import io.element.android.features.login.impl.accesscontrol.DefaultAccountProvid import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate import io.element.android.features.login.impl.login.LoginModePresenter -import io.element.android.features.login.impl.web.FakeWebClientUrlForAuthenticationRetriever -import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationRetriever import io.element.android.features.wellknown.test.FakeWellknownRetriever import io.element.android.features.wellknown.test.FakeWellknownRetrieverFactory import io.element.android.libraries.architecture.AsyncData @@ -322,7 +320,6 @@ private fun createPresenter( fun createLoginModePresenter( oAuthActionFlow: OAuthActionFlow = FakeOAuthActionFlow(), authenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), - webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever(), localNetworkPermissionAdvisor: LocalNetworkPermissionAdvisor = FakeLocalNetworkPermissionAdvisor(), permissionsPresenterFactory: PermissionsPresenter.Factory = @@ -330,7 +327,6 @@ fun createLoginModePresenter( ): LoginModePresenter = LoginModePresenter( oAuthActionFlow = oAuthActionFlow, authenticationService = authenticationService, - webClientUrlForAuthenticationRetriever = webClientUrlForAuthenticationRetriever, localNetworkPermissionGate = LocalNetworkPermissionGate( advisor = localNetworkPermissionAdvisor, permissionsPresenterFactory = permissionsPresenterFactory, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt index 1bdcb9bda1..dfa4119711 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt @@ -234,22 +234,6 @@ class OnboardingViewTest : RobolectricTestParameter() { eventSink.assertSingle(OnBoardingEvents.ClearError) } - @Test - fun `when success AccountCreation - the expected callback is invoked and the event is received`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder() - val oAuthDetails = OAuthDetails("aUrl") - ensureCalledOnceWithParam(oAuthDetails.url) { callback -> - setOnboardingView( - state = anOnBoardingState( - loginModeState = aLoginModeState(loginMode = AsyncData.Success(LoginMode.AccountCreation("aUrl"))), - eventSink = eventSink, - ), - onCreateAccountContinue = callback, - ) - } - eventSink.assertSingle(OnBoardingEvents.ClearError) - } - private fun AndroidComposeUiTest.setOnboardingView( state: OnBoardingState, onBackClick: () -> Unit = EnsureNeverCalled(), @@ -261,7 +245,6 @@ class OnboardingViewTest : RobolectricTestParameter() { onOAuthDetails: (OAuthDetails) -> Unit = EnsureNeverCalledWithParam(), onNeedLoginPassword: () -> Unit = EnsureNeverCalled(), onLearnMoreClick: () -> Unit = EnsureNeverCalled(), - onCreateAccountContinue: (url: String) -> Unit = EnsureNeverCalledWithParam(), ) { setContent { OnBoardingView( @@ -275,7 +258,6 @@ class OnboardingViewTest : RobolectricTestParameter() { onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, onLearnMoreClick = onLearnMoreClick, - onCreateAccountContinue = onCreateAccountContinue, ) } } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/web/FakeWebClientUrlForAuthenticationRetriever.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/web/FakeWebClientUrlForAuthenticationRetriever.kt deleted file mode 100644 index f52af0adf3..0000000000 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/web/FakeWebClientUrlForAuthenticationRetriever.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.login.impl.web - -import io.element.android.tests.testutils.lambda.lambdaError - -class FakeWebClientUrlForAuthenticationRetriever( - private val retrieveLambda: suspend (homeServerUrl: String) -> String = { lambdaError() } -) : WebClientUrlForAuthenticationRetriever { - override suspend fun retrieve(homeServerUrl: String): String { - return retrieveLambda(homeServerUrl) - } -} From 00e9a8ab71d3c09666c21923874f722e21e0a2fa Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Aug 2026 16:40:49 +0200 Subject: [PATCH 135/338] Fix wrong TextComposer placeholder displayed when in a thread. We cannot only use MessageComposerMode since use can edit a message in a thread, so this is becoming handly. --- .../messagecomposer/MessageComposerPresenter.kt | 1 + .../impl/messagecomposer/MessageComposerState.kt | 1 + .../MessageComposerStateProvider.kt | 2 ++ .../impl/messagecomposer/MessageComposerView.kt | 1 + .../MessageComposerPresenterTest.kt | 14 ++++++++++++++ .../android/libraries/textcomposer/TextComposer.kt | 3 ++- 6 files changed, 21 insertions(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 18252ee17d..5f12bf4be1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -425,6 +425,7 @@ class MessageComposerPresenter( textEditorState = textEditorState, isFullScreen = isFullScreen.value, mode = messageComposerContext.composerMode, + isInThreadTimeline = isInThread, showAttachmentSourcePicker = showAttachmentSourcePicker, showTextFormatting = showTextFormatting, canShareLocation = canShareLocation.value, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt index f3fdb3d59a..b4da119d96 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt @@ -21,6 +21,7 @@ data class MessageComposerState( val textEditorState: TextEditorState, val isFullScreen: Boolean, val mode: MessageComposerMode, + val isInThreadTimeline: Boolean, val showAttachmentSourcePicker: Boolean, val showTextFormatting: Boolean, val canShareLocation: Boolean, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt index ef9cd7933b..b08fea28ec 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt @@ -29,6 +29,7 @@ fun aMessageComposerState( textEditorState: TextEditorState = aTextEditorStateRich(), isFullScreen: Boolean = false, mode: MessageComposerMode = MessageComposerMode.Normal, + isInThreadTimeline: Boolean = false, showTextFormatting: Boolean = false, showAttachmentSourcePicker: Boolean = false, canShareLocation: Boolean = true, @@ -39,6 +40,7 @@ fun aMessageComposerState( textEditorState = textEditorState, isFullScreen = isFullScreen, mode = mode, + isInThreadTimeline = isInThreadTimeline, showTextFormatting = showTextFormatting, showAttachmentSourcePicker = showAttachmentSourcePicker, canShareLocation = canShareLocation, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt index 245b23cf8e..cd20f3e58b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt @@ -102,6 +102,7 @@ internal fun MessageComposerView( onRequestFocus = ::onRequestFocus, onSendMessage = ::sendMessage, composerMode = state.mode, + isInThreadTimeline = state.isInThreadTimeline, showTextFormatting = state.showTextFormatting, onResetComposerMode = ::onCloseSpecialMode, onAddAttachment = ::onAddAttachment, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt index fda8621087..07b9a4e330 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt @@ -61,6 +61,7 @@ import io.element.android.libraries.matrix.test.A_CAPTION import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_REPLY import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_THREAD_ID import io.element.android.libraries.matrix.test.A_TRANSACTION_ID import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID_2 @@ -148,12 +149,25 @@ class MessageComposerPresenterTest : RobolectricTest() { assertThat(initialState.isFullScreen).isFalse() assertThat(initialState.textEditorState.messageHtml()).isEqualTo("") assertThat(initialState.mode).isEqualTo(MessageComposerMode.Normal) + assertThat(initialState.isInThreadTimeline).isFalse() assertThat(initialState.showAttachmentSourcePicker).isFalse() assertThat(initialState.canShareLocation).isTrue() assertThat(initialState.slashCommandAction).isEqualTo(AsyncAction.Uninitialized) } } + @Test + fun `present - when a threadRoot is provided, isInThreadTimeline is true`() = runTest { + val presenter = createPresenter( + threadRoot = A_THREAD_ID, + ) + presenter.test { + val initialState = awaitFirstItem() + assertThat(initialState.mode).isEqualTo(MessageComposerMode.Normal) + assertThat(initialState.isInThreadTimeline).isTrue() + } + } + @Test fun `present - toggle fullscreen`() = runTest { val presenter = createPresenter() diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 0f62eb5234..a2ee9dd867 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -131,6 +131,7 @@ fun TextComposer( resolveAtRoomMentionDisplay: () -> TextDisplay, modifier: Modifier = Modifier, showTextFormatting: Boolean = false, + isInThreadTimeline: Boolean = false, ) { val markdown = when (state) { is TextEditorState.Markdown -> state.state.text.value() @@ -153,7 +154,7 @@ fun TextComposer( .fillMaxSize() .height(IntrinsicSize.Min) - val placeholder = if (composerMode.inThread) { + val placeholder = if (composerMode.inThread || (composerMode is MessageComposerMode.Normal && isInThreadTimeline)) { stringResource(id = CommonStrings.action_reply_in_thread) } else if (composerMode is MessageComposerMode.Attachment || composerMode is MessageComposerMode.EditCaption) { stringResource(id = R.string.rich_text_editor_composer_caption_placeholder) From ae5c4161c7bd4d6c9c08cbe9477c53bf42da2e19 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Aug 2026 18:14:36 +0200 Subject: [PATCH 136/338] Remove redundant parenthesis --- .../io/element/android/libraries/textcomposer/TextComposer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index a2ee9dd867..b8839cac67 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -154,7 +154,7 @@ fun TextComposer( .fillMaxSize() .height(IntrinsicSize.Min) - val placeholder = if (composerMode.inThread || (composerMode is MessageComposerMode.Normal && isInThreadTimeline)) { + val placeholder = if (composerMode.inThread || composerMode is MessageComposerMode.Normal && isInThreadTimeline) { stringResource(id = CommonStrings.action_reply_in_thread) } else if (composerMode is MessageComposerMode.Attachment || composerMode is MessageComposerMode.EditCaption) { stringResource(id = R.string.rich_text_editor_composer_caption_placeholder) From e2aa3071a903f947964ab6afff85d874a4177e3a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 23:03:33 +0000 Subject: [PATCH 137/338] Update dependency io.sentry:sentry-android to v8.52.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 3e2b0a902f..470fc76171 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -230,7 +230,7 @@ color_picker = "io.mhssn:colorpicker:1.0.0" # Analytics posthog = "com.posthog:posthog-android:3.56.4" -sentry = "io.sentry:sentry-android:8.51.0" +sentry = "io.sentry:sentry-android:8.52.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.37.0" From 8c8d232eacbb69e63983bb818b57e84e6e941824 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 09:21:31 +0200 Subject: [PATCH 138/338] Make onSendMessage parameter not nullable --- .../io/element/android/libraries/textcomposer/TextComposer.kt | 2 +- .../textcomposer/components/markdown/MarkdownTextInput.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 45a9e95bc0..66c9dcba03 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -218,10 +218,10 @@ fun TextComposer( placeholder = placeholder, placeholderColor = ElementTheme.colors.textSecondary, onTyping = onTyping, + onSendMessage = { if (canSendTextMessage) onSendMessage() }, onReceiveSuggestion = onReceiveSuggestion, richTextEditorStyle = style, onSelectRichContent = onSelectRichContent, - onSendMessage = { if (canSendTextMessage) onSendMessage() }, ) } } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt index 8c6f30eed1..52ec2b762a 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt @@ -51,10 +51,10 @@ fun MarkdownTextInput( placeholder: String, placeholderColor: androidx.compose.ui.graphics.Color, onTyping: (Boolean) -> Unit, + onSendMessage: () -> Unit, onReceiveSuggestion: (Suggestion?) -> Unit, richTextEditorStyle: RichTextEditorStyle, onSelectRichContent: ((Uri) -> Unit)?, - onSendMessage: (() -> Unit)? = null, ) { // Copied from io.element.android.wysiwyg.internal.utils.UriContentListener class ReceiveUriContentListener( @@ -217,6 +217,7 @@ internal fun MarkdownTextInputPreview() { placeholder = "Placeholder", placeholderColor = ElementTheme.colors.textSecondary, onTyping = {}, + onSendMessage = {}, onReceiveSuggestion = {}, richTextEditorStyle = style, onSelectRichContent = {}, From 690ffa54ce806a84b5d71c0f54e5d7bb8c56fa93 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2026 07:12:14 +0000 Subject: [PATCH 139/338] Update plugin sonarqube to v7.4.0.8496 --- 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 61b053b3a5..1ab1574c9c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -276,7 +276,7 @@ paparazzi = "app.cash.paparazzi:2.0.0-alpha05" roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } -sonarqube = "org.sonarqube:7.3.1.8318" +sonarqube = "org.sonarqube:7.4.0.8496" licensee = "app.cash.licensee:1.14.1" compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } gms_google_services = { id = "com.google.gms.google-services", version = "4.5.0" } From 590e33de7167ed2e70d61b9ac1f91e28259adc52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 13 Aug 2026 09:46:16 +0200 Subject: [PATCH 140/338] Add explicit dependency of `sonarResolver` task to `processResources` Otherwise `:codegen:sonarResolver` fails --- codegen/build.gradle.kts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/codegen/build.gradle.kts b/codegen/build.gradle.kts index 5c71c170bf..396ef665eb 100644 --- a/codegen/build.gradle.kts +++ b/codegen/build.gradle.kts @@ -1,3 +1,5 @@ +import org.sonarqube.gradle.SonarResolverTask + /* * Copyright (c) 2025 Element Creations Ltd. * Copyright 2022-2025 New Vector Ltd. @@ -18,3 +20,9 @@ dependencies { implementation(libs.ksp.plugin) implementation(libs.kotlinpoet.ksp) } + +// Ensure that the processResources task is executed before the SonarResolverTask to avoid issues with missing resources during SonarQube analysis. +// This *should not be necesssary* but is a workaround for a known issue with the SonarQube Gradle plugin. +tasks.withType().configureEach { + dependsOn("processResources") +} From d017a84b2b49122f497ca3c22593e7ac202b7c0f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 11:47:22 +0200 Subject: [PATCH 141/338] Remove useless let statement. --- .../textcomposer/components/markdown/MarkdownTextInput.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt index 52ec2b762a..63f9e0a8ea 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt @@ -133,11 +133,9 @@ fun MarkdownTextInput( update = { editText -> editText.contentDescription = placeholder editText.applyStyleInCompose(richTextEditorStyle) - editText.onEnterKeyListener = onSendMessage?.let { send -> - { - send() - true - } + editText.onEnterKeyListener = { + onSendMessage() + true } val text = state.text.value() mentionSpanUpdater.updateMentionSpans(text) From 07e90ff704bb3e8be2621c84426917c8767c40bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 13 Aug 2026 12:10:06 +0200 Subject: [PATCH 142/338] Fix `MarkdownTextInputTest` using the previous `onSendMessage` signature --- .../impl/components/markdown/MarkdownTextInputTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/components/markdown/MarkdownTextInputTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/components/markdown/MarkdownTextInputTest.kt index 15687cb4c9..d08048241e 100644 --- a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/components/markdown/MarkdownTextInputTest.kt +++ b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/components/markdown/MarkdownTextInputTest.kt @@ -191,7 +191,7 @@ class MarkdownTextInputTest : RobolectricTest() { state: MarkdownTextEditorState = aMarkdownTextEditorState(), onTyping: (Boolean) -> Unit = {}, onSuggestionReceived: (Suggestion?) -> Unit = {}, - onSendMessage: (() -> Unit)? = null, + onSendMessage: (() -> Unit) = {}, ) { setContent { val style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus) From 913b566270fd7f3ce8fce4533815ab20303b27cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 13 Aug 2026 10:35:36 +0200 Subject: [PATCH 143/338] Add missing tests for `RustTemporaryMatrixClient` --- .../impl/RustTemporaryMatrixClientTest.kt | 92 +++++++++++++++++++ .../impl/fixtures/fakes/FakeFfiClient.kt | 5 + 2 files changed, 97 insertions(+) create mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientTest.kt diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientTest.kt new file mode 100644 index 0000000000..322103042b --- /dev/null +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustTemporaryMatrixClientTest.kt @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.exception.ClientException +import io.element.android.libraries.matrix.api.paths.SessionPaths +import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClient +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.matrix.rustcomponents.sdk.Client +import org.matrix.rustcomponents.sdk.NoHandle +import java.io.File +import kotlin.io.path.createTempDirectory +import org.matrix.rustcomponents.sdk.ClientException as RustClientException + +class RustTemporaryMatrixClientTest { + @Test + fun `getUrl method uses client getUrl`() = runTest { + val paths = SessionPaths( + fileDirectory = File("files"), + cacheDirectory = File("cache"), + ) + + val getUrlRecorder = lambdaRecorder { ByteArray(0) } + val client = createRustTemporaryMatrixClient( + client = FakeFfiClient( + getUrlResult = getUrlRecorder, + ), + paths = paths, + ) + val url = "https://example.com" + + client.getUrl(url) + + getUrlRecorder.assertions().isCalledOnce().with(value(url)) + } + + @Test + fun `getUrl method can fail gracefully and map errors to public ones`() = runTest { + val paths = SessionPaths( + fileDirectory = File("files"), + cacheDirectory = File("cache"), + ) + + val getUrlRecorder = lambdaRecorder { throw RustClientException.Generic("Error", null) } + val client = createRustTemporaryMatrixClient( + client = FakeFfiClient( + getUrlResult = getUrlRecorder, + ), + paths = paths, + ) + val url = "https://example.com" + + val result = client.getUrl(url) + + assertThat(result.isFailure).isTrue() + assertThat(result.exceptionOrNull()).isInstanceOf(ClientException.Generic::class.java) + } + + @Test + fun `close deletes the directories if they exist`() = runTest { + val paths = SessionPaths( + fileDirectory = createTempDirectory().toFile(), + cacheDirectory = createTempDirectory().toFile(), + ) + + val client = createRustTemporaryMatrixClient(paths = paths) + + assertThat(paths.fileDirectory.exists()).isTrue() + assertThat(paths.cacheDirectory.exists()).isTrue() + + client.close() + + assertThat(paths.fileDirectory.exists()).isFalse() + assertThat(paths.cacheDirectory.exists()).isFalse() + } + + private fun createRustTemporaryMatrixClient( + client: Client = Client(NoHandle), + paths: SessionPaths, + ): RustTemporaryMatrixClient { + return RustTemporaryMatrixClient(client, paths) + } +} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt index a2c1f5b017..13be3cf2d5 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt @@ -60,6 +60,7 @@ class FakeFfiClient( private val setAccountDataResult: (String, String) -> Unit = { _, _ -> lambdaError() }, private val isUserStatusSupportedResult: () -> Boolean = { false }, private val subscribeToOwnProfileResult: (ProfileListener) -> Unit = {}, + private val getUrlResult: (String) -> ByteArray = { lambdaError() }, private val closeResult: () -> Unit = {}, ) : Client(NoHandle) { override fun userId(): String = userId @@ -134,5 +135,9 @@ class FakeFfiClient( setAccountDataResult(eventType, content) } + override suspend fun getUrl(url: String): ByteArray = simulateLongTask { + getUrlResult(url) + } + override fun close() = closeResult() } From 776182234e8e3059f9a2c6a7a3db7e5d7b5d8bbe Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 16:40:56 +0200 Subject: [PATCH 144/338] Remove license check on Pro modules. --- .../tests/konsist/KonsistLicenseTest.kt | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistLicenseTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistLicenseTest.kt index e7f82292a4..a2790d99df 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistLicenseTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistLicenseTest.kt @@ -23,24 +23,6 @@ class KonsistLicenseTest { \*/ """.trimIndent().toRegex() - private val enterpriseLicense = """ - /\* - \* © 20\d\d((, |-)20\d\d)? Element Creations Ltd\. - (?:.*\n)* \* - \* Element Creations Ltd, Element Software SARL, Element Software Inc\., - \* and Element Software GmbH \(the "Element Group"\) only make this file available - \* under a proprietary license model\. - \* - \* Without a proprietary license with us, you cannot use this file\. The terms of - \* the proprietary license agreement between you and any member of the Element Group - \* shall always apply to your use of this file\. Unauthorised use, copying, distribution, - \* or modification of this file, via any medium, is strictly prohibited\. - \* - \* For details about the licensing terms, you must either visit our website or contact - \* a member of our sales team\. - \*/ - """.trimIndent().toRegex() - @Test fun `assert that FOSS files have the correct license header`() { Konsist @@ -60,19 +42,6 @@ class KonsistLicenseTest { } } - @Test - fun `assert that Enterprise files have the correct license header`() { - Konsist - .scopeFromProject() - .files - .filter { - it.moduleName.startsWith("enterprise") - } - .assertTrue { - enterpriseLicense.containsMatchIn(it.text) - } - } - @Test fun `assert that files do not have double license header`() { Konsist From 5b728d911b159b1cda5386fc3e80d0c370bb5f56 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 12 Aug 2026 09:30:49 +0100 Subject: [PATCH 145/338] Add hooks to allow providing additional feature flags in EX pro We modify the existing `FeaturesProvider` interface so that there can be more than one of them, and the Features provided by *all* of the instances are shown. We can then make another instance in element-android-enterprise, which exposes enterprise-only features. --- .../featureflag/api/FeaturesProvider.kt | 21 +++++++++++++++++++ .../impl/DefaultFeatureFlagService.kt | 5 +++-- ...Provider.kt => DefaultFeaturesProvider.kt} | 9 +++----- 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeaturesProvider.kt rename libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/{FeaturesProvider.kt => DefaultFeaturesProvider.kt} (78%) diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeaturesProvider.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeaturesProvider.kt new file mode 100644 index 0000000000..a342ccdd9b --- /dev/null +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeaturesProvider.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.featureflag.api + +/** + * Provides a list of available [Feature]s that the application supports. + * Used by [FeatureFlagService.getAvailableFeatures] to obtain a list of features + * that can be presented to the user. + * + * Multiple instances can exist within the application, and all instances are polled + * to provide features for the list. This means that additional features can be provided + * by compile-time extensions. + */ +fun interface FeaturesProvider { + fun provide(): List +} diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt index 74967a574d..675cf7855c 100644 --- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt @@ -14,6 +14,7 @@ import dev.zacsweers.metro.SingleIn import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.featureflag.api.Feature import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeaturesProvider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf @@ -22,7 +23,7 @@ import kotlinx.coroutines.flow.flowOf class DefaultFeatureFlagService( private val providers: Set<@JvmSuppressWildcards FeatureFlagProvider>, private val buildMeta: BuildMeta, - private val featuresProvider: FeaturesProvider, + private val featuresProviders: Set, ) : FeatureFlagService { override fun isFeatureEnabledFlow(feature: Feature): Flow { return if (feature.isFinished) { @@ -47,7 +48,7 @@ class DefaultFeatureFlagService( includeFinishedFeatures: Boolean, isInLabs: Boolean, ): List { - return featuresProvider.provide().filter { flag -> + return featuresProviders.flatMap { provider -> provider.provide() }.filter { flag -> (includeFinishedFeatures || !flag.isFinished) && flag.isInLabs == isInLabs } diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/FeaturesProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeaturesProvider.kt similarity index 78% rename from libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/FeaturesProvider.kt rename to libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeaturesProvider.kt index e24ae66783..c96a781d82 100644 --- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/FeaturesProvider.kt +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeaturesProvider.kt @@ -9,15 +9,12 @@ package io.element.android.libraries.featureflag.impl import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.ContributesIntoSet import io.element.android.libraries.featureflag.api.Feature import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.api.FeaturesProvider -fun interface FeaturesProvider { - fun provide(): List -} - -@ContributesBinding(AppScope::class) +@ContributesIntoSet(AppScope::class) class DefaultFeaturesProvider : FeaturesProvider { override fun provide(): List = FeatureFlags.entries } From 42d389375543c2954d34976070dffacfc0e87a04 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 18:34:30 +0200 Subject: [PATCH 146/338] Add KDoc to matrix API session and sync interfaces Document MatrixClientProvider, SdkMetadata, HomeserverCapabilitiesProvider, TemporaryMatrixClientFactory and SyncService, along with each of their members. Behaviour described here was checked against the implementations (MatrixSessionCache, RustSdkMetadata, RustHomeserverCapabilitiesProvider, RustTemporaryMatrixClientFactory and RustSyncService), so the docs mention the caching, idempotence and error-wrapping that callers cannot infer from the signatures alone. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/HomeserverCapabilitiesProvider.kt | 2 ++ .../matrix/api/MatrixClientProvider.kt | 20 +++++++++++++------ .../libraries/matrix/api/SdkMetadata.kt | 8 ++++++++ .../api/TemporaryMatrixClientFactory.kt | 6 ++++++ .../libraries/matrix/api/sync/SyncService.kt | 14 +++++++++++-- 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/HomeserverCapabilitiesProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/HomeserverCapabilitiesProvider.kt index e914656f5f..fe05b88dc4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/HomeserverCapabilitiesProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/HomeserverCapabilitiesProvider.kt @@ -10,6 +10,8 @@ package io.element.android.libraries.matrix.api /** * Provides information about the capabilities of the homeserver. * + * The getters read a snapshot held by the SDK rather than querying the server on each call, so [refresh] must be used to pick up server-side changes. + * * Spec: https://spec.matrix.org/latest/client-server-api/#capabilities-negotiation */ interface HomeserverCapabilitiesProvider { diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClientProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClientProvider.kt index 54f70845fa..08073d0cc0 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClientProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClientProvider.kt @@ -10,18 +10,26 @@ package io.element.android.libraries.matrix.api import io.element.android.libraries.matrix.api.core.SessionId +/** + * In-memory cache of the [MatrixClient] of every logged in session. + * + * Prefer injecting the [MatrixClient] directly: this provider is meant for the entry points that only know a [SessionId], such as push handling and workers. + */ interface MatrixClientProvider { /** - * Can be used to get or restore a MatrixClient with the given [SessionId]. - * If a [MatrixClient] is already in memory, it'll return it. Otherwise it'll try to restore one. - * Most of the time you want to use injected constructor instead of retrieving a MatrixClient with this provider. + * Returns the cached [MatrixClient] for [sessionId], restoring the session from storage and starting its sync if it is not in memory yet. + * Concurrent calls are serialised, so only one restore happens per session. + * + * @param sessionId the [SessionId] of the session to get or restore. + * @return the client, or a failure if the session could not be restored. */ suspend fun getOrRestore(sessionId: SessionId): Result /** - * Can be used to retrieve an existing [MatrixClient] with the given [SessionId]. - * @param sessionId the [SessionId] of the [MatrixClient] to retrieve. - * @return the [MatrixClient] if it exists. + * Returns the [MatrixClient] for [sessionId] only if it is already in memory, without attempting to restore it. + * + * @param sessionId the [SessionId] of the session to retrieve. + * @return the cached client, or `null` if that session is not currently held in memory. */ fun getOrNull(sessionId: SessionId): MatrixClient? } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/SdkMetadata.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/SdkMetadata.kt index 16f34807fc..939734e314 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/SdkMetadata.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/SdkMetadata.kt @@ -8,6 +8,14 @@ package io.element.android.libraries.matrix.api +/** + * Exposes build information about the bundled `matrix-rust-sdk`. + * + * Mostly used to identify the exact SDK revision in bug reports and in the user agent. + */ interface SdkMetadata { + /** + * The full git SHA of the commit the bundled `matrix-rust-sdk` was built from. + */ val sdkGitSha: String } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt index 4be1b980a0..3bd22c64f3 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/TemporaryMatrixClientFactory.kt @@ -11,5 +11,11 @@ package io.element.android.libraries.matrix.api * Factory to create a temporary [TemporaryMatrixClient] for a given home server URL. */ interface TemporaryMatrixClientFactory { + /** + * Builds an unauthenticated client pointing at [homeServerUrl], backed by a fresh set of temporary session directories. + * The caller owns the returned client and must close it to delete those directories. + * + * @param homeServerUrl the URL of the homeserver the client will talk to. + */ suspend fun create(homeServerUrl: String): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt index ce0019d8dc..92f60352d7 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt @@ -10,21 +10,31 @@ package io.element.android.libraries.matrix.api.sync import kotlinx.coroutines.flow.StateFlow +/** + * Drives the sync loop of a single session and exposes its current state. + * + * Both [startSync] and [stopSync] are idempotent and never throw: any error is wrapped in the returned [Result]. + */ interface SyncService { /** - * Tries to start the sync. If already syncing it has no effect. + * Tries to start the sync. If already syncing, or if the service has been destroyed, it has no effect. */ suspend fun startSync(): Result /** - * Tries to stop the sync. If service is not syncing it has no effect. + * Tries to stop the sync. If the service is not syncing, or has been destroyed, it has no effect. */ suspend fun stopSync(): Result /** * Flow of [SyncState]. Will be updated as soon as the current [SyncState] changes. + * Starts at [SyncState.Idle] and only emits on an actual change, for as long as the session lives. */ val syncState: StateFlow + /** + * Whether the client currently considers itself online, derived from [syncState]. + * This is `false` only while the state is [SyncState.Offline]; an idle or errored sync still counts as online. + */ val isOnline: StateFlow } From f7d24050f8498add8c360e374044b7e94835528e Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 13 Aug 2026 17:38:59 +0100 Subject: [PATCH 147/338] fix unit test --- .../featureflag/impl/DefaultFeatureFlagServiceTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt index 00bc687bb9..0ed6d8eafd 100644 --- a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt +++ b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt @@ -12,6 +12,7 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.featureflag.api.Feature +import io.element.android.libraries.featureflag.api.FeaturesProvider import io.element.android.libraries.featureflag.test.FakeFeature import io.element.android.libraries.matrix.test.core.aBuildMeta import kotlinx.coroutines.test.runTest @@ -166,5 +167,5 @@ private fun createDefaultFeatureFlagService( ) = DefaultFeatureFlagService( providers = providers, buildMeta = buildMeta, - featuresProvider = { features } + featuresProviders = setOf(FeaturesProvider { features }) ) From 5d6384e175abcf24f5ff17686f631a5a6545c62e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 18:49:45 +0200 Subject: [PATCH 148/338] Add KDoc to `MatrixClient` Document the interface itself and all of its 60+ properties and functions. Notable behaviours taken from RustMatrixClient that callers cannot guess from the signatures: getRoom and getJoinedRoom build a new closeable instance on every call, createRoom/joinRoom/knockRoom wait for the membership to come back through sync and report a null RoomInfo rather than a failure when that wait times out, getCacheSize excludes the crypto database, and clearCache destroys the client while keeping the session. Also fix the stale wording and the malformed KDoc block on resolveRoomAlias, and add the missing @param entries on the functions that already had a summary. Co-Authored-By: Claude Opus 5 (1M context) --- .../libraries/matrix/api/MatrixClient.kt | 247 +++++++++++++++++- 1 file changed, 239 insertions(+), 8 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index e3aa2004bf..519ccfe3a4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -54,47 +54,188 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import java.util.Optional +/** + * Entry point for everything scoped to a single logged in session: the sub-services, room access, and account level operations. + * + * One instance exists per session, created on login or session restore and torn down by [logout], [clearCache] or account deactivation. + * Suspending members here report failures through a [Result] instead of throwing, and run their work off the main thread. + */ interface MatrixClient : UrlContentFetcher { + /** Identifier of the logged in user; a [SessionId] and a [UserId] are the same value. */ val sessionId: SessionId + + /** Identifier of this device, as assigned by the homeserver when the session was created. */ val deviceId: DeviceId /** Base URL of the homeserver this client is connected to. */ val homeserverUrl: String + + /** Directories holding this session's databases, caches and downloaded media. */ val sessionPaths: SessionPaths + + /** + * Display name and avatar of the logged in user. + * Seeded from the local session store so a value is available before the first network fetch, then kept up to date by the SDK. + */ val userProfile: StateFlow + + /** Gives access to the list of rooms the user is a member of, or has been invited to. */ val roomListService: RoomListService + + /** Gives access to the spaces the user belongs to and their hierarchy. */ val spaceService: SpaceService + + /** Controls the sync loop of this session and exposes its state. */ val syncService: SyncService + + /** Handles verifying this session against the user's other devices or their recovery key. */ val sessionVerificationService: SessionVerificationService + + /** Manages the push gateways (pushers) registered on the homeserver for this session. */ val pushersService: PushersService + + /** Resolves incoming push notifications into renderable events. */ val notificationService: NotificationService + + /** Reads and writes the push notification rules of the account. */ val notificationSettingsService: NotificationSettingsService + + /** Gives access to end-to-end encryption state, recovery and key backup. */ val encryptionService: EncryptionService + + /** Gives access to the public room directory of the homeserver. */ val roomDirectoryService: RoomDirectoryService /** Whether this live client was built with a message search index attached. */ val isMessageSearchAvailable: Boolean + + /** Searches messages in the local index; only usable when [isMessageSearchAvailable] is `true`. */ val messageSearchService: MessageSearchService + + /** Reads and writes the account settings controlling whether media previews and avatars are shown. */ val mediaPreviewService: MediaPreviewService + + /** Downloads media content, thumbnails and files from the homeserver, caching them on disk. */ val matrixMediaLoader: MatrixMediaLoader + + /** + * Coroutine scope tied to the lifetime of this session, to be used for work that must stop when the session goes away. + * It is cancelled when the session is logged out or otherwise destroyed, so never use it for work that must outlive the session. + */ val sessionCoroutineScope: CoroutineScope + + /** The users the logged in user has ignored; starts empty and is updated by the SDK, including with an initial value. */ val ignoredUsersFlow: StateFlow> + + /** Notifies of membership changes the current user makes, so that callers can react to leaving or joining a room. */ val roomMembershipObserver: RoomMembershipObserver + + /** Emits updates about the live location beacons published by the current user. */ val ownBeaconInfoUpdates: Flow + + /** Scans media content before it is displayed, or `null` when no content scanner is configured for this session. */ val contentScanner: ContentScanner? + /** + * Returns the room with the given id if the user has joined it, along with its live [Timeline]. + * A new instance is created on each call and the caller owns it, so it must be closed once no longer needed. + * + * @param roomId the id of the room to open. + * @return the room, or `null` if it is unknown to the client or the user is not joined. + */ suspend fun getJoinedRoom(roomId: RoomId): JoinedRoom? + + /** + * Returns the room with the given id whatever the current membership is, without building a timeline. + * As with [getJoinedRoom], a new instance is created on each call and must be closed by the caller. + * + * @param roomId the id of the room to open. + * @return the room, or `null` if it is unknown to the client. + */ suspend fun getRoom(roomId: RoomId): BaseRoom? + + /** + * Looks for an existing direct message room shared with [userId]. + * + * @param userId the other member of the direct message room. + * @return the room id, or `null` if no direct message room exists with this user. + */ suspend fun findDM(userId: UserId): Result + + /** + * Returns the ids of every room the user has joined, as currently known by the client. + * Invited, knocked and left rooms are excluded. + */ suspend fun getJoinedRoomIds(): Result> + + /** + * Adds [userId] to the ignore list of the account, which hides their events and makes [ignoredUsersFlow] emit. + * + * @param userId the user to ignore. + */ suspend fun ignoreUser(userId: UserId): Result + + /** + * Removes [userId] from the ignore list of the account, which makes [ignoredUsersFlow] emit. + * + * @param userId the user to stop ignoring. + */ suspend fun unignoreUser(userId: UserId): Result + + /** + * Creates a room, applying the app's default power levels on top of [createRoomParams]. + * Waits a short while for the new room to come back through sync, but a timeout there is only logged: the result is still a success. + * + * @param createRoomParams the name, topic, preset, invitees and other settings of the room to create. + */ suspend fun createRoom(createRoomParams: CreateRoomParameters): Result + + /** + * Creates a direct message room with [userId], as a private, invite-only room with the user already invited. + * This does not check whether such a room already exists; use [findDM] first if you want to reuse one. + * + * @param userId the user to start the direct message with. + * @param isEncrypted whether the room should have end-to-end encryption enabled. + */ suspend fun createDM(userId: UserId, isEncrypted: Boolean): Result + + /** + * Fetches the current profile of any user from the homeserver, bypassing the local cache. + * + * @param userId the user whose profile is requested. + */ suspend fun getProfile(userId: UserId): Result + + /** + * Searches the homeserver's user directory. + * + * @param searchTerm the text to look for in user ids and display names. + * @param limit the maximum number of results to return. + */ suspend fun searchUsers(searchTerm: String, limit: Long): Result + + /** + * Changes the display name of the logged in user on the homeserver. + * + * @param displayName the new display name. + */ suspend fun setDisplayName(displayName: String): Result + + /** + * Uploads [data] as the new avatar of the logged in user. + * + * @param mimeType the MIME type of the image, for instance `image/jpeg`. + * @param data the raw bytes of the image. + */ suspend fun uploadAvatar(mimeType: String, data: ByteArray): Result + + /** Removes the avatar of the logged in user. */ suspend fun removeAvatar(): Result + + /** + * Sets the `m.status` profile field of the logged in user (MSC4426), refreshing [userProfile] if the server does not push the change itself. + * + * @param status the status to publish. + */ suspend fun setUserStatus(status: UserStatus): Result /** Clears both m.status and m.call profile fields (maps to DELETE on the profile endpoint per MSC4426). */ @@ -103,16 +244,52 @@ interface MatrixClient : UrlContentFetcher { /** Whether the homeserver advertises support for user status (MSC4426). */ suspend fun isUserStatusSupported(): Result - /** Enable or disable automatically setting the user's status to "in a call" (m.call) while in a call. */ + /** + * Enable or disable automatically setting the user's status to "in a call" (m.call) while in a call. + * + * @param enabled whether the status should be maintained automatically. + */ fun enableAutomaticCallStatus(enabled: Boolean) + + /** + * Joins a room the client already knows about, then waits briefly for the new membership to arrive through sync. + * The returned [RoomInfo] is `null` when that wait times out, which is not treated as a failure: the join itself succeeded. + * + * @param roomId the id of the room to join. + */ suspend fun joinRoom(roomId: RoomId): Result + + /** + * Joins a room by id or by alias, resolving it through [serverNames] when the client does not know it yet. + * As with [joinRoom], the returned [RoomInfo] is `null` if the membership did not arrive through sync in time. + * + * @param roomIdOrAlias the id or the alias of the room to join. + * @param serverNames servers to ask about the room, used when it cannot be resolved locally. + */ suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result + + /** + * Sends a request to join a room whose join rule is knock, then waits briefly for the knocked membership to arrive through sync. + * As with [joinRoom], the returned [RoomInfo] is `null` if that wait times out. + * + * @param roomIdOrAlias the id or the alias of the room to knock on. + * @param message the reason shown to the room moderators; may be empty. + * @param serverNames servers to ask about the room, used when it cannot be resolved locally. + */ suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result + + /** + * Returns the number of bytes this session occupies on disk that [clearCache] would be able to reclaim. + * This covers the cache directory, the state database and the message search index, but not the crypto database. + */ suspend fun getCacheSize(): Long + + /** Returns the size of each individual store the SDK maintains for this session, as reported by the SDK. */ suspend fun getDatabaseSizes(): Result /** - * Will close the client and delete the cache data. + * Deletes the cached data of this session and destroys the client. + * The instance is unusable afterwards, so callers are expected to restart the session, unlike with [logout] the session itself is kept. */ suspend fun clearCache() @@ -125,29 +302,56 @@ interface MatrixClient : UrlContentFetcher { suspend fun logout(userInitiated: Boolean, ignoreSdkError: Boolean) /** - * Retrieve the user profile, will also eventually emit a new value to [userProfile]. + * Retrieve the profile of the logged in user, emitting it to [userProfile] and persisting it in the local session store on success. */ suspend fun getUserProfile(): Result + + /** + * Returns the web URL where the user can manage their account on the authentication provider, or `null` if the homeserver exposes none. + * + * @param action an optional section of the account management page to open directly, such as changing the password. + */ suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result + + /** + * Uploads arbitrary content to the homeserver's media repository. + * + * @param mimeType the MIME type of the content being uploaded. + * @param data the raw bytes to upload. + * @return the `mxc://` URI the content is now available at. + */ suspend fun uploadMedia(mimeType: String, data: ByteArray): Result /** * Get a room info flow for a given room ID. - * The flow will emit a new value whenever the room info is updated. - * The flow will emit Optional.empty item if the room is not found. + * The flow will emit a new value whenever the room info is updated, skipping duplicates, and an empty [Optional] if the room is not found. + * + * @param roomId the id of the room to observe. */ fun getRoomInfoFlow(roomId: RoomId): Flow> + /** + * Whether [userId] is the logged in user, `false` for a `null` value. + * + * @param userId the user id to compare against this session's user. + */ fun isMe(userId: UserId?) = userId == sessionId + /** + * Adds a room to the account's list of recently visited rooms, which backs the room suggestions when sharing or forwarding. + * + * @param roomId the id of the room that was just visited. + */ suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result + + /** Returns the recently visited rooms of the account, most recent first. */ suspend fun getRecentlyVisitedRooms(): Result> /** * Resolves the given room alias to a roomID (and a list of servers), if possible. - * @param roomAlias the room alias to resolve - * @return the resolved room alias if any, an empty result if not found,or an error if the resolution failed. * + * @param roomAlias the room alias to resolve. + * @return the resolved room alias if any, an empty result if not found, or an error if the resolution failed. */ suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result> @@ -158,6 +362,8 @@ interface MatrixClient : UrlContentFetcher { * event with it failed (e.g. sending an event via the Timeline), * so it's required to manually re-enable it as soon as * connectivity is back on the device. + * + * @param enabled whether the send queues of every room should be enabled. */ suspend fun setAllSendQueuesEnabled(enabled: Boolean) @@ -175,6 +381,9 @@ interface MatrixClient : UrlContentFetcher { /** * Get a room preview for a given room ID or alias. This is especially useful for rooms that the user is not a member of, or hasn't joined yet. + * + * @param roomIdOrAlias the id or the alias of the room to preview. + * @param serverNames servers to ask about the room, used when it cannot be resolved locally. */ suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result @@ -183,11 +392,21 @@ interface MatrixClient : UrlContentFetcher { */ suspend fun currentSlidingSyncVersion(): Result + /** Whether this account can be deactivated from the app, which depends on the authentication method used by the homeserver. */ fun canDeactivateAccount(): Boolean + + /** + * Deactivates the account on the homeserver, then destroys this client and deletes every local trace of the session. + * The password is only sent if the homeserver asks for re-authentication, and on failure the session is left untouched. + * + * @param password the account password, required to confirm the deactivation. + * @param eraseData whether the homeserver should also erase the messages the user sent. + */ suspend fun deactivateAccount(password: String, eraseData: Boolean): Result /** - * Check if the user can report a room. + * Whether the homeserver supports reporting a whole room, rather than individual events. + * Returns `false` if the capability cannot be determined. */ suspend fun canReportRoom(): Boolean @@ -208,16 +427,23 @@ interface MatrixClient : UrlContentFetcher { /** * Adds an emoji to the list of recent emoji reactions for this account. + * + * @param emoji the emoji that was just used. */ suspend fun addRecentEmoji(emoji: String): Result /** * Returns the raw JSON content of the global account data event of type [eventType], or null if it is not set. + * + * @param eventType the Matrix event type of the account data to read, for instance `m.direct`. */ suspend fun getAccountData(eventType: String): Result /** * Sets the global account data event of type [eventType] to the raw JSON [content]. + * + * @param eventType the Matrix event type of the account data to write. + * @param content the new content, as a raw JSON object. */ suspend fun setAccountData(eventType: String, content: String): Result @@ -226,6 +452,9 @@ interface MatrixClient : UrlContentFetcher { * * This method should be used with caution as providing the [eventId] ourselves can result in incorrect read receipts. * Use [Timeline.markAsRead] instead when possible. + * + * @param roomId the id of the room to mark as read. + * @param eventId the event up to which the room is considered read. */ suspend fun markRoomAsFullyRead(roomId: RoomId, eventId: EventId): Result @@ -252,6 +481,7 @@ interface MatrixClient : UrlContentFetcher { /** * Performs a database optimization that should flush cached data and improve performance. + * A periodic background job already calls this for every session, so callers rarely need to trigger it by hand. */ suspend fun performDatabaseVacuum(): Result @@ -265,6 +495,7 @@ interface MatrixClient : UrlContentFetcher { */ suspend fun resetWellKnownConfig(): Result + /** Returns a provider for the capabilities the homeserver advertises, such as whether the display name can be changed. */ fun homeserverCapabilities(): HomeserverCapabilitiesProvider } From 0bd4dc39cd9dafcf0b864dc2d7a0566a07547c00 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 19:02:12 +0200 Subject: [PATCH 149/338] Add KDoc to matrix API room interfaces Document BaseRoom, JoinedRoom, NotJoinedRoom, RoomAliasHelper and JoinRoom, along with their members, and add the missing @param entries. Fix the documentation of BaseRoom.getUpdatedIsEncrypted, which described the room directory visibility instead of the encryption state it actually returns. Behaviours taken from the implementations: BaseRoom.predecessorRoom swallows errors into null, updateMembers only reads the cache on its first call, JoinedRoom.syncUpdateFlow is an opaque change counter, notification settings start in an Unknown state until updateRoomNotificationSettings is called, and JoinRoom maps a Forbidden answer to Failures.UnauthorizedJoin. Co-Authored-By: Claude Opus 5 (1M context) --- .../libraries/matrix/api/room/BaseRoom.kt | 44 ++++++- .../libraries/matrix/api/room/JoinedRoom.kt | 108 +++++++++++++++++- .../matrix/api/room/NotJoinedRoom.kt | 6 + .../matrix/api/room/alias/RoomAliasHelper.kt | 14 +++ .../matrix/api/room/join/JoinRoom.kt | 11 ++ 5 files changed, 180 insertions(+), 3 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt index d49343f998..ab65b00f08 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt @@ -66,21 +66,30 @@ interface BaseRoom : Closeable { */ fun isDm() = roomInfoFlow.value.isDm + /** + * The room this one replaced when it was upgraded, or `null` if this room has no predecessor. + * Errors are swallowed and reported as `null`. + */ fun predecessorRoom(): PredecessorRoom? /** * Try to load the room members and update the membersFlow. + * The first call may serve members from the local cache before the server response arrives; later calls always go to the server. */ suspend fun updateMembers() /** * Get the members of the room. Note: generally this should not be used, please use * [membersStateFlow] and [updateMembers] instead. + * + * @param limit the maximum number of members to return. */ suspend fun getMembers(limit: Int = 5): Result> /** * Will return an updated member or an error. + * + * @param userId the member to fetch. */ suspend fun getUpdatedMember(userId: UserId): Result @@ -103,6 +112,8 @@ interface BaseRoom : Closeable { /** * Gets the role of the user with the provided [userId] in the room. + * + * @param userId the member whose role is requested. */ suspend fun userRole(userId: UserId): Result @@ -113,11 +124,15 @@ interface BaseRoom : Closeable { /** * Gets the display name of the user with the provided [userId] in the room. + * + * @param userId the member whose room specific display name is requested. */ suspend fun userDisplayName(userId: UserId): Result /** * Gets the avatar of the user with the provided [userId] in the room. + * + * @param userId the member whose room specific avatar is requested. */ suspend fun userAvatarUrl(userId: UserId): Result @@ -138,6 +153,8 @@ interface BaseRoom : Closeable { /** * Sets the room as favorite or not, based on the [isFavorite] parameter. + * + * @param isFavorite true to mark the room as favorite, false to remove the flag. */ suspend fun setIsFavorite(isFavorite: Boolean): Result @@ -176,22 +193,29 @@ interface BaseRoom : Closeable { suspend fun getRoomVisibility(): Result /** - * Returns the visibility for this room in the room directory, fetching it from the homeserver if needed. + * Returns whether this room is encrypted, based on the latest encryption state known to the SDK rather than on the cached [RoomInfo]. */ suspend fun getUpdatedIsEncrypted(): Result /** * Store the given `ComposerDraft` in the state store of this room. + * + * @param composerDraft the unsent message content to remember. + * @param threadRoot the thread the draft belongs to, or `null` for the main timeline. */ suspend fun saveComposerDraft(composerDraft: ComposerDraft, threadRoot: ThreadId?): Result /** * Retrieve the `ComposerDraft` stored in the state store for this room. + * + * @param threadRoot the thread whose draft is requested, or `null` for the main timeline. */ suspend fun loadComposerDraft(threadRoot: ThreadId?): Result /** * Clear the `ComposerDraft` stored in the state store for this room. + * + * @param threadRoot the thread whose draft should be cleared, or `null` for the main timeline. */ suspend fun clearComposerDraft(threadRoot: ThreadId?): Result @@ -202,10 +226,27 @@ interface BaseRoom : Closeable { */ suspend fun reportRoom(reason: String?): Result + /** + * Declines the incoming call advertised by the given notification, letting the other devices of the room know. + * + * @param notificationEventId the id of the call notification event being declined. + */ suspend fun declineCall(notificationEventId: EventId): Result + /** + * Emits the id of each user who declines the call advertised by the given notification. + * Used to stop ringing once the callee has declined from another device. + * + * @param notificationEventId the id of the call notification event to watch. + */ suspend fun subscribeToCallDecline(notificationEventId: EventId): Flow + /** + * Returns the id of the thread the given event belongs to, fetching the event from the server if it is not cached locally. + * + * @param eventId the event whose thread is requested. + * @return the thread root id, or `null` if the event is not part of a thread. + */ suspend fun threadRootIdForEvent(eventId: EventId): Result /** @@ -213,5 +254,6 @@ interface BaseRoom : Closeable { */ fun destroy() + /** Same as [destroy], so that a room can be used with `use { }`. */ override fun close() = destroy() } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt index 6d8325427b..98498131f9 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt @@ -28,11 +28,28 @@ import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +/** + * A room the current user has joined, adding to [BaseRoom] everything that requires membership: sending events, moderation and room settings. + * + * An instance owns SDK resources, in particular [liveTimeline], so it must be closed once no longer needed. + */ interface JoinedRoom : BaseRoom { + /** + * A counter incremented every time a synced event is received on [liveTimeline], so callers can refresh data derived from the room. + * It carries no meaning beyond "something changed", and is only updated while it has subscribers. + */ val syncUpdateFlow: StateFlow + /** The members currently typing in the room, never including the current user; starts as an empty list. */ val roomTypingMembersFlow: Flow> + + /** Emits when the verification state of other members' identities changes, for instance after a member resets their identity. */ val identityStateChangesFlow: Flow> + + /** + * The notification settings that apply to this room. + * Starts as [RoomNotificationSettingsState.Unknown]: [updateRoomNotificationSettings] must be called to populate it. + */ val roomNotificationSettingsStateFlow: StateFlow /** @@ -45,6 +62,7 @@ interface JoinedRoom : BaseRoom { */ val liveTimeline: Timeline + /** Gives access to the paginated list of threads started in this room. */ val threadsListService: ThreadsListService /** @@ -55,6 +73,14 @@ interface JoinedRoom : BaseRoom { createTimelineParams: CreateTimelineParams, ): Result + /** + * Replaces the content of an event previously sent by the current user, by sending an edit of it. + * + * @param eventId the event to edit. + * @param body the new content, as plain text. + * @param htmlBody the new content as HTML, or `null` when the message has no formatted version. + * @param intentionalMentions the users and rooms this new content deliberately mentions. + */ suspend fun editMessage(eventId: EventId, body: String, htmlBody: String?, intentionalMentions: List): Result /** @@ -63,12 +89,28 @@ interface JoinedRoom : BaseRoom { */ suspend fun typingNotice(isTyping: Boolean): Result + /** + * Invites a user to this room. + * + * @param id the user to invite. + */ suspend fun inviteUserById(id: UserId): Result + /** + * Uploads [data] as the new avatar of the room, not of the current user. + * + * @param mimeType the MIME type of the image, for instance `image/jpeg`. + * @param data the raw bytes of the image. + */ suspend fun updateAvatar(mimeType: String, data: ByteArray): Result + /** Removes the avatar of the room. */ suspend fun removeAvatar(): Result + /** + * Refreshes [roomNotificationSettingsStateFlow], which moves to a pending state and then to a ready or error state. + * The failure is reported through that flow as well as through the returned [Result]. + */ suspend fun updateRoomNotificationSettings(): Result /** @@ -83,11 +125,15 @@ interface JoinedRoom : BaseRoom { /** * Update the room's visibility in the room directory. + * + * @param roomVisibility whether the room should be listed publicly in the directory. */ suspend fun updateRoomVisibility(roomVisibility: RoomVisibility): Result /** - * Update room history visibility for this room. + * Update room history visibility for this room, i.e. how much of the past a new member is allowed to read. + * + * @param historyVisibility the new history visibility to apply. */ suspend fun updateHistoryVisibility(historyVisibility: RoomHistoryVisibility): Result @@ -98,6 +144,8 @@ interface JoinedRoom : BaseRoom { * - `true` if the room alias didn't exist and it's now published. * - `false` if the room alias was already present so it couldn't be * published. + * + * @param roomAlias the alias to publish. */ suspend fun publishRoomAliasInRoomDirectory(roomAlias: RoomAlias): Result @@ -108,6 +156,8 @@ interface JoinedRoom : BaseRoom { * - `true` if the room alias was present and it's now removed from the * room directory. * - `false` if the room alias didn't exist so it couldn't be removed. + * + * @param roomAlias the alias to remove. */ suspend fun removeRoomAliasFromRoomDirectory(roomAlias: RoomAlias): Result @@ -117,26 +167,74 @@ interface JoinedRoom : BaseRoom { suspend fun enableEncryption(): Result /** - * Update the join rule for this room. + * Update the join rule for this room, i.e. who is allowed to join it. + * + * @param joinRule the new join rule to apply. */ suspend fun updateJoinRule(joinRule: JoinRule): Result + /** + * Changes the power level of individual members, which is how roles such as moderator or administrator are granted. + * + * @param changes the new power level to apply to each user. + */ suspend fun updateUsersRoles(changes: List): Result + /** + * Changes the power level required for each action in the room, such as inviting, kicking or changing the room name. + * + * @param roomPowerLevelsValues the new threshold for every action. + */ suspend fun updatePowerLevels(roomPowerLevelsValues: RoomPowerLevelsValues): Result + /** Restores the power levels required for each action to the defaults the room was created with. */ suspend fun resetPowerLevels(): Result + /** + * Changes the name of the room. + * + * @param name the new room name. + */ suspend fun setName(name: String): Result + /** + * Changes the topic of the room. + * + * @param topic the new room topic. + */ suspend fun setTopic(topic: String): Result + /** + * Reports an event to the homeserver moderators, optionally ignoring its sender at the same time. + * + * @param eventId the event being reported. + * @param reason the explanation sent to the moderators. + * @param blockUserId the sender to also add to the ignore list, or `null` to only report the event. + */ suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result + /** + * Removes a member from the room; they are able to join again unless they are also banned. + * + * @param userId the member to remove. + * @param reason the reason recorded in the membership event, or `null` for none. + */ suspend fun kickUser(userId: UserId, reason: String? = null): Result + /** + * Bans a user from the room, removing them if they are a member and preventing them from joining again. + * + * @param userId the user to ban. + * @param reason the reason recorded in the membership event, or `null` for none. + */ suspend fun banUser(userId: UserId, reason: String? = null): Result + /** + * Lifts the ban on a user, allowing them to join the room again; it does not invite them back. + * + * @param userId the user to unban. + * @param reason the reason recorded in the membership event, or `null` for none. + */ suspend fun unbanUser(userId: UserId, reason: String? = null): Result /** @@ -161,6 +259,12 @@ interface JoinedRoom : BaseRoom { */ fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result + /** + * Enables or disables the send queue of this room only; see [io.element.android.libraries.matrix.api.MatrixClient.setAllSendQueuesEnabled] + * for the session-wide equivalent and for why a queue disables itself. + * + * @param enabled whether the send queue of this room should be enabled. + */ suspend fun setSendQueueEnabled(enabled: Boolean) /** diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/NotJoinedRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/NotJoinedRoom.kt index fd5e71ff9d..a47179cae7 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/NotJoinedRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/NotJoinedRoom.kt @@ -12,7 +12,13 @@ import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo /** A reference to a room either invited, knocked or banned. */ interface NotJoinedRoom : AutoCloseable { + /** What is publicly known about the room: name, topic, avatar, join rule and member count. */ val previewInfo: RoomPreviewInfo + + /** + * The local room, available when the user has a membership in it, for instance after being invited. + * It is `null` for a room only discovered through a preview, and closing this instance also closes it. + */ val localRoom: BaseRoom? /** diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/alias/RoomAliasHelper.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/alias/RoomAliasHelper.kt index c23252961f..aecd2ab7f4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/alias/RoomAliasHelper.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/alias/RoomAliasHelper.kt @@ -10,7 +10,21 @@ package io.element.android.libraries.matrix.api.room.alias import io.element.android.libraries.matrix.api.core.RoomAlias +/** + * Helpers to build and validate room aliases, delegating to the SDK so that the app and the server agree on what is acceptable. + */ interface RoomAliasHelper { + /** + * Turns a room display name into a usable alias local part, dropping the characters that are not allowed in an alias. + * + * @param name the room display name to derive the alias from. + */ fun roomAliasNameFromRoomDisplayName(name: String): String + + /** + * Whether the given alias is well-formed; this is a format check only and says nothing about the alias being taken. + * + * @param roomAlias the alias to validate. + */ fun isRoomAliasValid(roomAlias: RoomAlias): Boolean } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/join/JoinRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/join/JoinRoom.kt index 4220b68f0d..a6b1f3d36b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/join/JoinRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/join/JoinRoom.kt @@ -11,7 +11,18 @@ package io.element.android.libraries.matrix.api.room.join import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.libraries.matrix.api.core.RoomIdOrAlias +/** + * Joins a room and reports the join to analytics, so that callers do not have to do both. + */ interface JoinRoom { + /** + * Joins the room, picking the right SDK call depending on whether an id or an alias was given, and captures an analytics event on success. + * A `Forbidden` answer from the server is translated into [Failures.UnauthorizedJoin] so callers can tell it apart from other errors. + * + * @param roomIdOrAlias the id or the alias of the room to join. + * @param serverNames servers to ask about the room, ignored when joining by alias. + * @param trigger what made the user join, recorded in the analytics event. + */ suspend operator fun invoke( roomIdOrAlias: RoomIdOrAlias, serverNames: List, From 8a88638667e2600432d0b1b0abf432d29f2a464e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 19:03:48 +0200 Subject: [PATCH 150/338] Add KDoc to matrix API knock, threads and permissions interfaces Document KnockRequest and ThreadsListService along with all their members, and add the missing @param entries to the already documented RoomPermissions. For ThreadsListService the docs record what the implementation does with the subscriptions: subscribeToItemUpdates starts a shared subscription on the first call and re-emits the whole list, subscribeToPaginationUpdates replays the current state immediately, and destroy leaves the service unusable. Co-Authored-By: Claude Opus 5 (1M context) --- .../matrix/api/room/knock/KnockRequest.kt | 30 +++++++++++++++++++ .../api/room/powerlevels/RoomPermissions.kt | 24 +++++++++++++++ .../api/room/threads/ThreadsListService.kt | 17 +++++++++++ 3 files changed, 71 insertions(+) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/knock/KnockRequest.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/knock/KnockRequest.kt index 317157793e..386be0d5c9 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/knock/KnockRequest.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/knock/KnockRequest.kt @@ -11,20 +11,50 @@ package io.element.android.libraries.matrix.api.room.knock import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId +/** + * A pending request from a user to join a room whose join rule is knock, along with the actions a moderator can take on it. + * + * Instances are snapshots handed out by [io.element.android.libraries.matrix.api.room.JoinedRoom.knockRequestsFlow]. + */ interface KnockRequest { + /** The id of the membership event carrying the request. */ val eventId: EventId + + /** The user asking to join. */ val userId: UserId + + /** The display name of the user asking to join, or `null` if they have none. */ val displayName: String? + + /** The avatar of the user asking to join, or `null` if they have none. */ val avatarUrl: String? + + /** The message the user attached to their request, or `null` if they sent none. */ val reason: String? + + /** When the request was made, in milliseconds since the epoch, or `null` if the server did not provide it. */ val timestamp: Long? + + /** Whether a moderator has already marked this request as seen, so that it is no longer highlighted as new. */ val isSeen: Boolean + /** Accepts the request, which makes the user a member of the room. */ suspend fun accept(): Result + /** + * Declines the request; the user is free to knock again. + * + * @param reason the explanation recorded in the membership event, or `null` for none. + */ suspend fun decline(reason: String?): Result + /** + * Declines the request and bans the user, so that they cannot knock again. + * + * @param reason the explanation recorded in the membership event, or `null` for none. + */ suspend fun declineAndBan(reason: String?): Result + /** Marks the request as seen, without accepting or declining it. */ suspend fun markAsSeen(): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPermissions.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPermissions.kt index a12f7d9606..19c01b9975 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPermissions.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPermissions.kt @@ -59,12 +59,16 @@ interface RoomPermissions : AutoCloseable { /** * Returns true if the current user is able to send a specific message type * in the room. + * + * @param message the kind of message the caller wants to send. */ fun canOwnUserSendMessage(message: MessageEventType): Boolean /** * Returns true if the current user is able to send a specific state event * type in the room. + * + * @param stateEvent the kind of state event the caller wants to send. */ fun canOwnUserSendState(stateEvent: StateEventType): Boolean @@ -77,48 +81,66 @@ interface RoomPermissions : AutoCloseable { /** * Returns true if the user with the given userId is able to ban in the * room. + * + * @param userId the member whose ability is checked. */ fun canUserBan(userId: UserId): Boolean /** * Returns true if the user with the given userId is able to invite in the * room. + * + * @param userId the member whose ability is checked. */ fun canUserInvite(userId: UserId): Boolean /** * Returns true if the user with the given userId is able to kick in the * room. + * + * @param userId the member whose ability is checked. */ fun canUserKick(userId: UserId): Boolean /** * Returns true if the user with the given userId is able to pin or unpin * events in the room. + * + * @param userId the member whose ability is checked. */ fun canUserPinUnpin(userId: UserId): Boolean /** * Returns true if the user with the given userId is able to redact * messages of other users in the room. + * + * @param userId the member whose ability is checked. */ fun canUserRedactOther(userId: UserId): Boolean /** * Returns true if the user with the given userId is able to redact * their own messages in the room. + * + * @param userId the member whose ability is checked. */ fun canUserRedactOwn(userId: UserId): Boolean /** * Returns true if the user with the given userId is able to send a * specific message type in the room. + * + * @param userId the member whose ability is checked. + * @param message the kind of message to check. */ fun canUserSendMessage(userId: UserId, message: MessageEventType): Boolean /** * Returns true if the user with the given userId is able to send a * specific state event type in the room. + * + * @param userId the member whose ability is checked. + * @param stateEvent the kind of state event to check. */ fun canUserSendState(userId: UserId, stateEvent: StateEventType): Boolean @@ -127,6 +149,8 @@ interface RoomPermissions : AutoCloseable { * notification in the room. * * The call may fail if there is an error in getting the power levels. + * + * @param userId the member whose ability is checked. */ fun canUserTriggerRoomNotification(userId: UserId): Boolean } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/threads/ThreadsListService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/threads/ThreadsListService.kt index 7f819c540c..f544211741 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/threads/ThreadsListService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/threads/ThreadsListService.kt @@ -9,10 +9,27 @@ package io.element.android.libraries.matrix.api.room.threads import kotlinx.coroutines.flow.Flow +/** + * Exposes the paginated list of threads of a single room, kept up to date by the SDK. + * + * The list grows through explicit [paginate] calls; [destroy] must be called to release the underlying SDK resources. + */ interface ThreadsListService { + /** + * The threads loaded so far, starting empty and re-emitted in full whenever the SDK sends an update. + * The underlying subscription is started on the first call and shared by later ones. + */ fun subscribeToItemUpdates(): Flow> + + /** The pagination state of the list, emitting the current value immediately so callers know whether more threads can be loaded. */ fun subscribeToPaginationUpdates(): Flow + + /** Loads the next page of threads, which will be reflected in [subscribeToItemUpdates]. */ suspend fun paginate(): Result + + /** Discards the loaded threads and starts the list again from the most recent one. */ suspend fun reset(): Result + + /** Releases the SDK resources and stops the item subscription; the service is unusable afterwards. */ fun destroy() } From 54780f0f70f42bce6d65ee82740f4c450ff54ce0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 19:07:55 +0200 Subject: [PATCH 151/338] Add KDoc to matrix API timeline interfaces Document Timeline and all of its members, TimelineProvider, and the three lazy providers carried by EventTimelineItem. Behaviours taken from RustTimeline: collecting membershipChangeEventReceived or onSyncedEventReceived is what keeps the timeline subscribed to the SDK, paginate fails with CannotPaginate when the matching status forbids it, the initial hasMoreToLoad of each pagination status depends on the timeline mode, timelineItems also carries virtual items such as date separators, and the media send functions return a handler the caller must await. Co-Authored-By: Claude Opus 5 (1M context) --- .../libraries/matrix/api/timeline/Timeline.kt | 171 +++++++++++++++++- .../matrix/api/timeline/TimelineProvider.kt | 1 + .../timeline/item/event/EventTimelineItem.kt | 16 ++ 3 files changed, 187 insertions(+), 1 deletion(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt index 8cb933205b..72483e68f1 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt @@ -31,6 +31,12 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.parcelize.Parcelize import java.io.File +/** + * A window over the events of a room, restricted by its [Mode], and the entry point for sending events to that room. + * + * A timeline owns SDK resources and must be closed once no longer needed, except the live timeline, which is owned by its room. + * Sending returns successfully as soon as the event has been handed to the room's send queue, not when the server has accepted it. + */ interface Timeline : AutoCloseable { data class PaginationStatus( val isPaginating: Boolean, @@ -54,18 +60,66 @@ interface Timeline : AutoCloseable { data class Thread(val threadRootId: ThreadId) : Mode } + /** What this timeline is a view over: the live room, a single event and its context, the pinned events, the media, or a thread. */ val mode: Mode + + /** + * Emits every time a membership change event is received in this timeline, without saying which one. + * Collecting it keeps the timeline subscribed to the SDK, so it should not be collected for longer than needed. + */ val membershipChangeEventReceived: Flow + + /** + * Emits every time an event coming from sync is received in this timeline, without saying which one. + * As with [membershipChangeEventReceived], collecting it keeps the timeline subscribed to the SDK. + */ val onSyncedEventReceived: Flow + + /** + * Sends a read receipt for a single event, which is how the unread state of the room is cleared. + * + * @param eventId the event the receipt points at. + * @param receiptType whether the receipt is public, private or only marks the fully read position. + */ suspend fun sendReadReceipt(eventId: EventId, receiptType: ReceiptType): Result + + /** + * Marks the whole timeline as read by sending a receipt on its latest event. + * + * @param receiptType whether the receipt is public, private or only marks the fully read position. + */ suspend fun markAsRead(receiptType: ReceiptType): Result + + /** + * Loads one more page of events and updates the matching pagination status flow. + * Fails with a `CannotPaginate` error when the corresponding status does not allow it, for instance because a pagination is already running. + * + * @param direction whether to load older or newer events. + * @return whether the end of the timeline has been reached in that direction. + */ suspend fun paginate(direction: PaginationDirection): Result + /** Whether older events are being loaded and whether more remain; pinned event timelines start with nothing more to load. */ val backwardPaginationStatus: StateFlow + + /** Whether newer events are being loaded and whether more remain; only a timeline focused on an event starts with more to load. */ val forwardPaginationStatus: StateFlow + /** + * The items to display, in timeline order, re-emitted on every change. + * Beyond the room events this also carries the virtual items the UI needs, such as date separators, loading indicators and the room beginning. + */ val timelineItems: Flow> + /** + * Sends a text message to the room this timeline belongs to. + * + * @param body the message content, as plain text. + * @param htmlBody the message content as HTML, or `null` to send it unformatted. + * @param intentionalMentions the users and rooms the message deliberately mentions, so that they are notified. + * @param msgType the Matrix message type to use, which distinguishes a normal message from an emote or a notice. + * @param asPlainText whether to send the message without any formatting, ignoring markdown in [body]. + */ suspend fun sendMessage( body: String, htmlBody: String?, @@ -74,6 +128,14 @@ interface Timeline : AutoCloseable { asPlainText: Boolean = false, ): Result + /** + * Replaces the content of a message, whether it has already been sent or is still local in the send queue. + * + * @param eventOrTransactionId the event to edit, identified by its event id once sent or by its transaction id while still local. + * @param body the new content, as plain text. + * @param htmlBody the new content as HTML, or `null` to send it unformatted. + * @param intentionalMentions the users and rooms the new content deliberately mentions. + */ suspend fun editMessage( eventOrTransactionId: EventOrTransactionId, body: String, @@ -81,12 +143,29 @@ interface Timeline : AutoCloseable { intentionalMentions: List, ): Result + /** + * Replaces the caption of an already sent media message, leaving the media itself untouched. + * + * @param eventOrTransactionId the media event to edit, identified by its event id or by its transaction id while still local. + * @param caption the new caption, or `null` to remove it. + * @param formattedCaption the new caption as HTML, or `null` to send it unformatted. + */ suspend fun editCaption( eventOrTransactionId: EventOrTransactionId, caption: String?, formattedCaption: String?, ): Result + /** + * Sends a message as a reply to another event. + * + * @param repliedToEventId the event being replied to. + * @param body the reply content, as plain text. + * @param htmlBody the reply content as HTML, or `null` to send it unformatted. + * @param intentionalMentions the users and rooms the reply deliberately mentions. + * @param fromNotification whether the reply was written from a notification rather than from the timeline. + * @param msgType the Matrix message type to use. + */ suspend fun replyMessage( repliedToEventId: EventId, body: String, @@ -96,6 +175,16 @@ interface Timeline : AutoCloseable { msgType: MsgType = MsgType.MSG_TYPE_TEXT, ): Result + /** + * Starts sending an image. The returned [MediaUploadHandler] tracks the upload and must be awaited by the caller to know the outcome. + * + * @param file the image to upload. + * @param thumbnailFile a pre-generated thumbnail, or `null` to send none. + * @param imageInfo the dimensions, size and MIME type of the image. + * @param caption the text shown along with the image, or `null` for none. + * @param formattedCaption the caption as HTML, or `null` to send it unformatted. + * @param inReplyToEventId the event this message replies to, or `null` if it is not a reply. + */ suspend fun sendImage( file: File, thumbnailFile: File?, @@ -105,6 +194,16 @@ interface Timeline : AutoCloseable { inReplyToEventId: EventId?, ): Result + /** + * Starts sending a video, with the same upload contract as [sendImage]. + * + * @param file the video to upload. + * @param thumbnailFile a pre-generated thumbnail, or `null` to send none. + * @param videoInfo the duration, dimensions, size and MIME type of the video. + * @param caption the text shown along with the video, or `null` for none. + * @param formattedCaption the caption as HTML, or `null` to send it unformatted. + * @param inReplyToEventId the event this message replies to, or `null` if it is not a reply. + */ suspend fun sendVideo( file: File, thumbnailFile: File?, @@ -114,6 +213,15 @@ interface Timeline : AutoCloseable { inReplyToEventId: EventId?, ): Result + /** + * Starts sending an audio file, with the same upload contract as [sendImage]; use [sendVoiceMessage] for a recorded voice message. + * + * @param file the audio file to upload. + * @param audioInfo the duration, size and MIME type of the audio. + * @param caption the text shown along with the audio, or `null` for none. + * @param formattedCaption the caption as HTML, or `null` to send it unformatted. + * @param inReplyToEventId the event this message replies to, or `null` if it is not a reply. + */ suspend fun sendAudio( file: File, audioInfo: AudioInfo, @@ -122,6 +230,15 @@ interface Timeline : AutoCloseable { inReplyToEventId: EventId?, ): Result + /** + * Starts sending an arbitrary file, with the same upload contract as [sendImage]. + * + * @param file the file to upload. + * @param fileInfo the size and MIME type of the file. + * @param caption the text shown along with the file, or `null` for none. + * @param formattedCaption the caption as HTML, or `null` to send it unformatted. + * @param inReplyToEventId the event this message replies to, or `null` if it is not a reply. + */ suspend fun sendFile( file: File, fileInfo: FileInfo, @@ -151,6 +268,14 @@ interface Timeline : AutoCloseable { inReplyToEventId: EventId?, ): Result + /** + * Starts sending a recorded voice message, with the same upload contract as [sendImage]. + * + * @param file the recorded audio to upload. + * @param audioInfo the duration, size and MIME type of the recording. + * @param waveform the amplitudes used to draw the waveform, normalised between 0 and 1. + * @param inReplyToEventId the event this message replies to, or `null` if it is not a reply. + */ suspend fun sendVoiceMessage( file: File, audioInfo: AudioInfo, @@ -158,6 +283,14 @@ interface Timeline : AutoCloseable { inReplyToEventId: EventId?, ): Result + /** + * Starts sending several media items as a single gallery message, with the same upload contract as [sendImage]. + * + * @param items the media making up the gallery, in the order they should appear. + * @param caption the text shown along with the gallery, or `null` for none. + * @param formattedCaption the caption as HTML, or `null` to send it unformatted. + * @param inReplyToEventId the event this message replies to, or `null` if it is not a reply. + */ suspend fun sendGallery( items: List, caption: String?, @@ -165,12 +298,36 @@ interface Timeline : AutoCloseable { inReplyToEventId: EventId?, ): Result + /** + * Redacts an event, i.e. asks the server to remove its content; it also removes a local echo that has not been sent yet. + * + * @param eventOrTransactionId the event to redact, identified by its event id once sent or by its transaction id while still local. + * @param reason the explanation recorded in the redaction event, or `null` for none. + */ suspend fun redactEvent(eventOrTransactionId: EventOrTransactionId, reason: String?): Result + /** + * Adds the given reaction to an event, or removes it if the current user had already reacted with it. + * + * @param emoji the reaction key, usually an emoji. + * @param eventOrTransactionId the event to react to, identified by its event id or by its transaction id while still local. + * @return whether the reaction is now present. + */ suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result + /** + * Forwards the content of an event to other rooms, as new messages sent by the current user. + * + * @param eventId the event whose content is forwarded. + * @param roomIds the rooms to forward it to. + */ suspend fun forwardEvent(eventId: EventId, roomIds: List): Result + /** + * Cancels an event that is still waiting in the send queue, by redacting its local echo. + * + * @param transactionId the transaction id of the local echo to drop. + */ suspend fun cancelSend(transactionId: TransactionId): Result = redactEvent(transactionId.toEventOrTransactionId(), reason = null) @@ -222,12 +379,20 @@ interface Timeline : AutoCloseable { */ suspend fun endPoll(pollStartId: EventId, text: String): Result + /** + * Resolves the content of a replied-to event so it can be rendered above the reply. + * Served from the loaded timeline items when possible, otherwise fetched through the SDK. + * + * @param eventId the event being replied to. + */ suspend fun loadReplyDetails(eventId: EventId): InReplyTo /** * Returns true if [eventId] is currently loaded in this timeline's window, even if the display * layer does not render it (e.g. filtered or state events). This distinguishes "in the window * but not displayed" from "genuinely outside the loaded window". + * + * @param eventId the event to look for. */ suspend fun isEventLoaded(eventId: EventId): Boolean @@ -237,6 +402,8 @@ interface Timeline : AutoCloseable { * * Returns `true` if we sent the request, `false` if the event was already * pinned. + * + * @param eventId the event to pin. */ suspend fun pinEvent(eventId: EventId): Result @@ -246,11 +413,13 @@ interface Timeline : AutoCloseable { * * Returns `true` if we sent the request, `false` if the event wasn't * pinned + * + * @param eventId the event to unpin. */ suspend fun unpinEvent(eventId: EventId): Result /** - * Get the latest event id of the timeline. + * Get the latest event id of the timeline, or `null` when it holds no event yet. */ suspend fun getLatestEventId(): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/TimelineProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/TimelineProvider.kt index 28e487f527..ef9d5d794e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/TimelineProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/TimelineProvider.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.flow.first * By default, the active timeline is the live timeline. */ fun interface TimelineProvider { + /** The timeline currently in use, or `null` while none is available yet; see [getActiveTimeline] to await a non-null value. */ fun activeTimelineFlow(): StateFlow } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt index f3982e83d2..91703f7fa4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt @@ -60,14 +60,30 @@ data class EventTimelineItem( } } +/** + * Provides the debug information of a timeline item on demand, so the original event JSON is only fetched from the SDK when actually displayed. + */ fun interface TimelineItemDebugInfoProvider { + /** Returns the debug information of the item, including its original and latest edit JSON. */ operator fun invoke(): TimelineItemDebugInfo } +/** + * Provides the encryption warning to show next to a timeline item on demand, so the check is only run for items that are displayed. + */ fun interface MessageShieldProvider { + /** + * Returns the warning to display for this event, or `null` when there is nothing to warn about. + * + * @param strict whether to also report the weaker problems that are hidden by default. + */ operator fun invoke(strict: Boolean): MessageShield? } +/** + * Provides the send queue handle of a timeline item on demand, which is needed to retry or cancel a failed send. + */ fun interface SendHandleProvider { + /** Returns the send handle of the item, or `null` when it is not a local echo waiting in the send queue. */ operator fun invoke(): SendHandle? } From fa01166197175241d6882b925ace4919d9255bf9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 19:10:58 +0200 Subject: [PATCH 152/338] Add KDoc to matrix API encryption and verification interfaces Document EncryptionService, SessionVerificationService and SessionVerificationServiceListener along with their members, and add the missing @param entries. From RustEncryptionService: the backup and recovery state flows report a waiting-for-sync value for as long as the session is not syncing, and isLastDevice and hasDevicesToVerifyAgainst are polled rather than observed. PasswordStrengthEstimator was already accurate and is left untouched. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/encryption/EncryptionService.kt | 29 ++++++++++++++++++- .../SessionVerificationService.kt | 22 ++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) 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 f82cdc1969..e036a198df 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 @@ -14,13 +14,32 @@ import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +/** + * Gives access to the end-to-end encryption state of the session: key backup, recovery, and the trust of other users' identities. + */ interface EncryptionService { + /** + * The state of the room key backup on the server. + * Reports [BackupState.WAITING_FOR_SYNC] for as long as the session is not actively syncing, since the real state is unknown until then. + */ val backupStateStateFlow: StateFlow + + /** + * The state of recovery, i.e. whether the secrets needed to restore the keys on a new device are available. + * As with [backupStateStateFlow], it reports a waiting-for-sync state until the session is syncing. + */ val recoveryStateStateFlow: StateFlow + + /** Progress of the [enableRecovery] call currently in flight, to be observed while that call is running. */ val enableRecoveryProgressStateFlow: StateFlow + + /** Whether this is the only device of the account, which matters because logging out of it can make the room keys unrecoverable. */ val isLastDevice: StateFlow + + /** Whether the account has another device this one could be verified against; it stays loading until the first answer arrives. */ val hasDevicesToVerifyAgainst: StateFlow> + /** Enables the room key backup on the server, so that the keys survive losing every device. */ suspend fun enableBackups(): Result /** @@ -39,12 +58,16 @@ interface EncryptionService { */ suspend fun resetRecoveryKey(): Result + /** Disables recovery, which deletes the secrets from server-side storage; the key backup itself is left in place. */ suspend fun disableRecovery(): Result + /** Whether a room key backup currently exists on the server, whether or not this device is the one using it. */ suspend fun doesBackupExistOnServer(): Result /** - * Note: accept both recoveryKey and passphrase. + * Restores the secrets from server-side storage, which unlocks the room key backup for this device. + * + * @param recoveryKey the recovery key, or the passphrase it was derived from; both are accepted. */ suspend fun recover(recoveryKey: String): Result @@ -72,6 +95,8 @@ interface EncryptionService { /** * Remember this identity, ensuring it does not result in a pin violation. + * + * @param userId the user whose current identity should be pinned. */ suspend fun pinUserIdentity(userId: UserId): Result @@ -81,6 +106,8 @@ interface EncryptionService { * Useful when a user that was verified is not anymore, but it is not * possible to re-verify immediately. This allows to restore communication by reverting the * user trust from verified to TOFU verified. + * + * @param userId the user whose verification should be withdrawn. */ suspend fun withdrawVerification(userId: UserId): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt index 38a00d833d..de9b11e882 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt @@ -13,6 +13,12 @@ import io.element.android.libraries.matrix.api.core.UserId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +/** + * Drives session verification: proving to the account that this device is trusted, and verifying other users. + * + * A verification is a stateful exchange between two devices, so at most one flow is active at a time and its progress is reported + * through [verificationFlowState] rather than through the return value of each call. + */ interface SessionVerificationService { /** * State of the current verification flow ([VerificationFlowState.Initial] if not started). @@ -37,6 +43,8 @@ interface SessionVerificationService { /** * Request verification of the user with the given [userId]. + * + * @param userId the other user to verify. */ suspend fun requestUserVerification(userId: UserId) @@ -62,17 +70,23 @@ interface SessionVerificationService { /** * Returns the verification service state to the initial step. + * + * @param cancelAnyPendingVerificationAttempt whether to also cancel a verification that is still in progress. */ suspend fun reset(cancelAnyPendingVerificationAttempt: Boolean) /** * Register a listener to be notified of incoming session verification requests. + * + * @param listener the listener to notify, or `null` to stop being notified. */ fun setListener(listener: SessionVerificationServiceListener?) /** * Set this particular request as the currently active one and register for * events pertaining it. + * + * @param verificationRequest the incoming request to make active. */ suspend fun acknowledgeVerificationRequest(verificationRequest: VerificationRequest.Incoming) @@ -82,7 +96,15 @@ interface SessionVerificationService { suspend fun acceptVerificationRequest() } +/** + * Notified when another device asks to verify this session; register one through [SessionVerificationService.setListener]. + */ interface SessionVerificationServiceListener { + /** + * Called when a verification request is received, so the app can prompt the user about it. + * + * @param verificationRequest the incoming request, to be passed to [SessionVerificationService.acknowledgeVerificationRequest] to act on it. + */ fun onIncomingSessionRequest(verificationRequest: VerificationRequest.Incoming) } From 0b2d6eafc345aa8a60cf20d2d196b0ae59d2ed45 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 19:14:00 +0200 Subject: [PATCH 153/338] Add KDoc to matrix API authentication and device linking interfaces Document MatrixAuthenticationService, OAuthRedirectUrlProvider, the two QR code login data interfaces, and the four device linking interfaces, along with their members and parameters. The docs record the ordering constraints a caller has to respect: setHomeserver must precede any login attempt, the link handlers report their outcome through their step flow rather than through a return value, and ContinuationMessageSender expects exactly one of cancel or confirm. The oauth package holds only a sealed interface, so it is out of scope here. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/auth/MatrixAuthenticationService.kt | 41 ++++++++++++++++++- .../api/auth/OAuthRedirectUrlProvider.kt | 4 ++ .../api/auth/qrlogin/MatrixQrCodeLoginData.kt | 6 +++ .../qrlogin/MatrixQrCodeLoginDataFactory.kt | 8 ++++ .../api/linknewdevice/CheckCodeSender.kt | 3 ++ .../ContinuationMessageSender.kt | 8 ++++ .../api/linknewdevice/LinkDesktopHandler.kt | 14 +++++++ .../api/linknewdevice/LinkMobileHandler.kt | 9 ++++ 8 files changed, 92 insertions(+), 1 deletion(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt index 04d1d13593..3ff1eaa337 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt @@ -16,23 +16,41 @@ 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.api.core.UserId +/** + * Creates and restores sessions: password login, OAuth login, QR code login, and importing a session from another client. + * + * [setHomeserver] must be called before any login attempt, since it is what tells the SDK which server to authenticate against. + * Every successful login stores the session and notifies the listeners registered through [listenToNewMatrixClients]. + */ interface MatrixAuthenticationService { /** * Restore a session from a [sessionId]. * Do not restore anything it the access token is not valid anymore. * Generally this method should not be used directly, prefer using [MatrixClientProvider.getOrRestore] instead. + * + * @param sessionId the session to restore. */ suspend fun restoreSession(sessionId: SessionId): Result /** * Set the homeserver to use for authentication, and return its details. + * + * @param homeserver the homeserver URL or server name to authenticate against. */ suspend fun setHomeserver(homeserver: String): Result + /** + * Logs in with a password on the homeserver previously set through [setHomeserver]. + * + * @param username the user id or its local part. + * @param password the account password. + */ suspend fun login(username: String, password: String): Result /** * Import a session that was created using another client, for instance Element Web. + * + * @param externalSession the tokens and identifiers of the session to import. */ suspend fun importCreatedSession(externalSession: ExternalSession): Result @@ -42,6 +60,9 @@ interface MatrixAuthenticationService { /** * Get the OAuth url to display to the user. + * + * @param prompt whether the authentication server should show the sign-in or the account-creation page. + * @param loginHint the user identifier to pre-fill on that page, or `null` to leave it empty. */ suspend fun getOAuthUrl( prompt: OAuthPrompt, @@ -55,11 +76,17 @@ interface MatrixAuthenticationService { /** * Set the existing data about Element Classic session, if any. + * + * @param session the session found in a local Element Classic install, or `null` when there is none to migrate. */ fun setElementClassicSession(session: ElementClassicSession?) /** * Check if the provided secrets from Element Classic session contain a key backup. + * + * @param userId the user the secrets belong to. + * @param secrets the raw secrets exported from Element Classic. + * @param backupInfo the raw backup description exported alongside them. */ fun doSecretsContainBackupKey( userId: UserId, @@ -69,11 +96,23 @@ interface MatrixAuthenticationService { /** * Attempt to log in using the [callbackUrl] provided by the OAuth page. + * + * @param callbackUrl the redirect URL the authentication server sent the user back to, carrying the authorization code. */ suspend fun loginWithOAuth(callbackUrl: String): Result + /** + * Logs in by acting on a QR code shown by an already signed in device, reporting each step of the exchange as it happens. + * + * @param qrCodeData the decoded QR code, obtained from [io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginDataFactory]. + * @param progress called on every step of the flow, so the UI can follow along. + */ suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit): Result - /** Listen to new Matrix clients being created on authentication. */ + /** + * Listen to new Matrix clients being created on authentication. + * + * @param lambda called with each client created by a successful login or session restore. + */ fun listenToNewMatrixClients(lambda: (MatrixClient) -> Unit) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthRedirectUrlProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthRedirectUrlProvider.kt index 669d47501d..648884e930 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthRedirectUrlProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthRedirectUrlProvider.kt @@ -8,6 +8,10 @@ package io.element.android.libraries.matrix.api.auth +/** + * Provides the redirect URL registered for OAuth login, which the authentication server sends the user back to once they have signed in. + */ interface OAuthRedirectUrlProvider { + /** Returns the redirect URL, built from the app's custom URL scheme so that the system hands the callback back to the app. */ fun provide(): String } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/MatrixQrCodeLoginData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/MatrixQrCodeLoginData.kt index 413f17af63..f800ad2ba1 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/MatrixQrCodeLoginData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/MatrixQrCodeLoginData.kt @@ -8,6 +8,12 @@ package io.element.android.libraries.matrix.api.auth.qrlogin +/** + * The decoded content of a login QR code, as produced by [MatrixQrCodeLoginDataFactory]. + * + * The underlying data is opaque to the app: it is passed back to the SDK to perform the login. + */ interface MatrixQrCodeLoginData { + /** The homeserver advertised by the QR code, or `null` when it does not carry one. */ fun serverName(): String? } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/MatrixQrCodeLoginDataFactory.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/MatrixQrCodeLoginDataFactory.kt index b0e3089501..21d77b74f2 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/MatrixQrCodeLoginDataFactory.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/MatrixQrCodeLoginDataFactory.kt @@ -8,6 +8,14 @@ package io.element.android.libraries.matrix.api.auth.qrlogin +/** + * Decodes the raw bytes of a scanned QR code into login data the SDK can use. + */ interface MatrixQrCodeLoginDataFactory { + /** + * Parses the scanned bytes, failing with a [QrCodeDecodeException] when they are not a valid Matrix login QR code. + * + * @param data the raw content read from the QR code. + */ fun parseQrCodeData(data: ByteArray): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/CheckCodeSender.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/CheckCodeSender.kt index ecb440de83..a3a518f9ce 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/CheckCodeSender.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/CheckCodeSender.kt @@ -7,6 +7,9 @@ package io.element.android.libraries.matrix.api.linknewdevice +/** + * Lets the user confirm the two-digit check code displayed by the device being linked, which proves the channel is not being intercepted. + */ interface CheckCodeSender { /** * Validates the given [code]. Returns true if the code is valid, false otherwise. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/ContinuationMessageSender.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/ContinuationMessageSender.kt index 0bbbaf3187..0a2ce273d5 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/ContinuationMessageSender.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/ContinuationMessageSender.kt @@ -7,7 +7,15 @@ package io.element.android.libraries.matrix.api.linknewdevice +/** + * Lets the user confirm or refuse a device linking request once they have approved it in the browser. + * + * Handed out with the step that is waiting for authorisation; exactly one of the two methods is meant to be called. + */ interface ContinuationMessageSender { + /** Tells the other device that the user refused the linking, which ends the flow. */ suspend fun cancel(): Result + + /** Tells the other device that the user approved the linking, so the flow can carry on. */ suspend fun confirm(): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkDesktopHandler.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkDesktopHandler.kt index 343624aa34..26c2ed04c4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkDesktopHandler.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkDesktopHandler.kt @@ -10,8 +10,22 @@ package io.element.android.libraries.matrix.api.linknewdevice import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeDecodeException import kotlinx.coroutines.flow.StateFlow +/** + * Drives the linking of a new device that cannot scan a QR code, such as a desktop: this device scans the code the other one displays. + * + * Progress is reported entirely through [linkDesktopStep]; there is one handler per attempt, obtained from + * [io.element.android.libraries.matrix.api.MatrixClient.createLinkDesktopHandler]. + */ interface LinkDesktopHandler { + /** Each step of the flow, from establishing the secure channel to syncing the secrets to the new device. */ val linkDesktopStep: StateFlow + + /** + * Runs the whole flow from the QR code just scanned, suspending until it finishes or fails. + * A code that cannot be decoded is reported as an invalid-QR-code step rather than as a thrown error. + * + * @param data the raw content read from the QR code shown by the other device. + */ suspend fun handleScannedQrCode(data: ByteArray) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt index f187b8275a..ae51eb1f0a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt @@ -9,8 +9,17 @@ package io.element.android.libraries.matrix.api.linknewdevice import kotlinx.coroutines.flow.Flow +/** + * Drives the linking of a new device that is able to scan a QR code, by showing one on this already signed in device. + * + * Progress is reported entirely through [linkMobileStep]; there is one handler per attempt, obtained from + * [io.element.android.libraries.matrix.api.MatrixClient.createLinkMobileHandler]. + */ interface LinkMobileHandler { + /** Each step of the flow, from generating the QR code to syncing the secrets to the new device. */ val linkMobileStep: Flow + + /** Runs the whole flow, suspending until it finishes or fails; the outcome is reported through [linkMobileStep]. */ suspend fun start() } From 1e19f5b607b2b3d4226950562f116645c687f216 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 19:28:15 +0200 Subject: [PATCH 154/338] Add KDoc to matrix API room list, directory and spaces interfaces Document RoomDirectoryService, RoomDirectoryList, SpaceService, SpaceRoomList and LeaveSpaceHandle along with their members, add the missing DynamicRoomList page size doc and the missing @param entries, and give MessageSearchService the interface level doc it was lacking. The docs make the ownership rules explicit, since they differ across these types: a SpaceRoomList and a LeaveSpaceHandle are owned by the caller and must be destroyed or closed, while a RoomDirectoryList and a MessageSearch are released when the scope they were created with completes. RoomList, RoomListService and the search interfaces were already accurate and are left untouched. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/roomdirectory/RoomDirectoryList.kt | 9 +++++++ .../api/roomdirectory/RoomDirectoryService.kt | 8 ++++++ .../matrix/api/roomlist/DynamicRoomList.kt | 1 + .../matrix/api/search/MessageSearchService.kt | 5 ++++ .../matrix/api/spaces/LeaveSpaceHandle.kt | 5 ++++ .../matrix/api/spaces/SpaceRoomList.kt | 14 ++++++++++ .../matrix/api/spaces/SpaceService.kt | 27 +++++++++++++++++++ 7 files changed, 69 insertions(+) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt index c48bf54e9c..49de1bee89 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt @@ -10,12 +10,21 @@ package io.element.android.libraries.matrix.api.roomdirectory import kotlinx.coroutines.flow.Flow +/** + * A single, stateful search in the public room directory: one query at a time, with paginated results. + * + * Obtain an instance from [RoomDirectoryService.createRoomDirectoryList]. + */ interface RoomDirectoryList { /** * Starts a filtered search for the server. * If the filter is not provided it will search for all the rooms. You can specify a batch_size to control the number of rooms to fetch per request. * If the via_server is not provided it will search in the current homeserver by default. * This method will clear the current search results and start a new one + * + * @param filter the text to look for, or `null` to list every room. + * @param batchSize the number of rooms to fetch per request. + * @param viaServerName the server whose directory to search, or `null` to use the user's own homeserver. */ suspend fun filter(filter: String?, batchSize: Int, viaServerName: String?): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryService.kt index 8f71cfb18c..65f83299cb 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryService.kt @@ -10,6 +10,14 @@ package io.element.android.libraries.matrix.api.roomdirectory import kotlinx.coroutines.CoroutineScope +/** + * Gives access to the public room directory, i.e. the rooms a server advertises to users who have not joined them. + */ interface RoomDirectoryService { + /** + * Creates an independent directory search, which is stateful and holds one query at a time. + * + * @param scope the lifetime of the search; the underlying SDK resources are released when it completes, so there is nothing to close by hand. + */ fun createRoomDirectoryList(scope: CoroutineScope): RoomDirectoryList } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt index 952cee6f22..eee98b82e9 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt @@ -14,6 +14,7 @@ package io.element.android.libraries.matrix.api.roomlist * It lets load rooms on demand and filter them. */ interface DynamicRoomList : RoomList { + /** The number of rooms added to the list by each [loadMore] call, as chosen when the list was created. */ val pageSize: Int /** diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/search/MessageSearchService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/search/MessageSearchService.kt index 3e20b163f2..d7d8c017e3 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/search/MessageSearchService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/search/MessageSearchService.kt @@ -10,6 +10,11 @@ package io.element.android.libraries.matrix.api.search import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.coroutines.CoroutineScope +/** + * Creates the message search cursors backed by the local search index. + * + * Only usable when the session was built with the index attached, see [io.element.android.libraries.matrix.api.MatrixClient.isMessageSearchAvailable]. + */ interface MessageSearchService { /** * Create a new, independent search cursor. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/LeaveSpaceHandle.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/LeaveSpaceHandle.kt index 174ec20dc9..59b9657b58 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/LeaveSpaceHandle.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/LeaveSpaceHandle.kt @@ -10,6 +10,11 @@ package io.element.android.libraries.matrix.api.spaces import io.element.android.libraries.matrix.api.core.RoomId +/** + * Handles leaving a space, letting the user also choose which of its rooms to leave in the same operation. + * + * Obtain an instance from [SpaceService.getLeaveSpaceHandle]; the caller owns it and must [close] it. + */ interface LeaveSpaceHandle { /** * The id of the space to leave. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt index 2ccee8d1ee..b29d721d07 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt @@ -16,22 +16,36 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import java.util.Optional +/** + * The paginated list of the children of a single space, kept up to date by the SDK. + * + * Obtain an instance from [SpaceService.spaceRoomList]; the caller owns it and must call [destroy] when done. + */ interface SpaceRoomList { sealed interface PaginationStatus { data object Loading : PaginationStatus data class Idle(val hasMoreToLoad: Boolean) : PaginationStatus } + /** The space whose children this list holds. */ val spaceId: RoomId + /** What is known about the space itself, starting empty until the SDK provides it. */ val currentSpaceFlow: StateFlow> + /** The children loaded so far, re-emitted in full on every update; the latest value is replayed to new collectors. */ val spaceRoomsFlow: Flow> + + /** Whether a page is being loaded and whether more children remain; see [loadAllIncrementally] to drain the list. */ val paginationStatusFlow: StateFlow + /** Loads the next page of children, which will be reflected in [spaceRoomsFlow]. */ suspend fun paginate(): Result + + /** Discards the loaded children and starts the list again from the beginning. */ suspend fun reset(): Result + /** Releases the SDK resources and cancels the internal scope; the list is unusable afterwards. */ fun destroy() } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceService.kt index 299209e188..a7126a4766 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceService.kt @@ -11,13 +11,35 @@ package io.element.android.libraries.matrix.api.spaces import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.coroutines.flow.SharedFlow +/** + * Gives access to the spaces the user belongs to, their hierarchy, and the operations that change it. + */ interface SpaceService { + /** The spaces that have no joined parent, i.e. the roots of the hierarchy shown to the user; the latest value is replayed to new collectors. */ val topLevelSpacesFlow: SharedFlow> + + /** The filters the space list can be narrowed down with; as with [topLevelSpacesFlow], the latest value is replayed. */ val spaceFiltersFlow: SharedFlow> + + /** + * Returns the spaces the given room or space is a child of, restricted to the ones the user has joined. + * + * @param spaceId the room or space whose parents are requested. + */ suspend fun joinedParents(spaceId: RoomId): Result> + /** + * Returns what is known about a single space, or `null` if it is unknown or the lookup failed. + * + * @param spaceId the space to look up. + */ suspend fun getSpaceRoom(spaceId: RoomId): SpaceRoom? + /** + * Creates a paginated list of the children of a space; the caller owns it and must call `destroy` on it. + * + * @param id the space whose children are listed. + */ fun spaceRoomList(id: RoomId): SpaceRoomList /** @@ -25,6 +47,11 @@ interface SpaceService { */ suspend fun editableSpaces(): Result> + /** + * Creates a handle to leave a space, which lets the user also pick which of its rooms to leave; the caller must close it. + * + * @param spaceId the space the user wants to leave. + */ fun getLeaveSpaceHandle(spaceId: RoomId): LeaveSpaceHandle /** From 68e5ea5a7427dde337cee1c46a4fb47f04f8f14c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Aug 2026 19:30:24 +0200 Subject: [PATCH 155/338] Add KDoc to matrix API media, mxc and scanner interfaces Add the interface level docs that MediaPreviewService, MatrixMediaLoader and MxcTools were missing, document MediaFile.path and the nested ContentScannerUrlProvider.Factory, and add the missing @param entries. Most members in these packages already had accurate documentation and are left untouched. Co-Authored-By: Claude Opus 5 (1M context) --- .../libraries/matrix/api/media/MatrixMediaLoader.kt | 3 +++ .../android/libraries/matrix/api/media/MediaFile.kt | 1 + .../libraries/matrix/api/media/MediaPreviewService.kt | 9 +++++++++ .../element/android/libraries/matrix/api/mxc/MxcTools.kt | 3 +++ .../matrix/api/scanner/ContentScannerUrlProvider.kt | 6 ++++++ 5 files changed, 22 insertions(+) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt index a36849b812..28ae0762ff 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt @@ -8,6 +8,9 @@ package io.element.android.libraries.matrix.api.media +/** + * Fetches media content from the homeserver, decrypting it on the way when the room is encrypted. + */ interface MatrixMediaLoader { /** * @param source to fetch the content for. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaFile.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaFile.kt index b5cefe897b..ae6035da88 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaFile.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaFile.kt @@ -16,6 +16,7 @@ import java.io.File * When closed the file will be removed from the disk unless [persist] has been used. */ interface MediaFile : Closeable { + /** The absolute path of the file on disk; see [toFile] to get a [File] instead. */ fun path(): String /** diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewService.kt index 39718ddfd2..660bb0b80e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewService.kt @@ -10,6 +10,11 @@ package io.element.android.libraries.matrix.api.media import kotlinx.coroutines.flow.StateFlow +/** + * Reads and writes the account settings that decide whether media previews and invite avatars are shown. + * + * These are account data settings shared across clients, so the local value is only updated once the server has accepted the change. + */ interface MediaPreviewService { /** * Will fetch the media preview config from the server. @@ -24,11 +29,15 @@ interface MediaPreviewService { /** * Set the media preview display policy. This will update the value on the server and update the local value when successful. + * + * @param mediaPreviewValue whether media previews are shown always, never, or only in private rooms. */ suspend fun setMediaPreviewValue(mediaPreviewValue: MediaPreviewValue): Result /** * Set the invite avatars display policy. This will update the value on the server and update the local value when successful. + * + * @param hide true to hide the avatars of users and rooms the user has been invited by. */ suspend fun setHideInviteAvatars(hide: Boolean): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt index 09ceaa4712..c28bfa9933 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt @@ -8,6 +8,9 @@ package io.element.android.libraries.matrix.api.mxc +/** + * Helpers to work with Matrix Content (`mxc://`) URIs. + */ interface MxcTools { /** * Sanitizes an mxcUri to be used as a relative file path. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt index 76878080ae..2c11c750e4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/scanner/ContentScannerUrlProvider.kt @@ -19,7 +19,13 @@ fun interface ContentScannerUrlProvider { */ suspend fun getContentScannerUrl(sessionId: SessionId): Result + /** + * Creates a provider, which needs a fetcher because the scanner URL is itself discovered over HTTP. + */ fun interface Factory { + /** + * @param urlContentFetcher used to read the configuration advertised by the homeserver; a client that is not authenticated yet also works. + */ fun create(urlContentFetcher: UrlContentFetcher): ContentScannerUrlProvider } } From ddbc6a2a338e369d2590f7a380f1985d3f15e408 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2026 18:59:32 +0000 Subject: [PATCH 156/338] Update metro to v1.4.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1ab1574c9c..865906e84b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -54,7 +54,7 @@ haze = "1.7.2" dependencyAnalysis = "3.18.0" # DI -metro = "1.4.0" +metro = "1.4.1" # Auto service autoservice = "1.1.1" From 58f09422e3302e167274698499d0b72df46c0b1b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 06:17:33 +0000 Subject: [PATCH 157/338] Update roborazzi to v1.71.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 865906e84b..f7bad7e053 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ accompanist = "0.37.3" # Test test_core = "1.7.0" -roborazzi = "1.70.0" +roborazzi = "1.71.0" # Jetbrain datetime = "0.8.0" From 22195a47f2e8bbca50b117bc12bf698fa49ef5a8 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Fri, 14 Aug 2026 08:20:37 +0200 Subject: [PATCH 158/338] Move the custom tile server URL config to the Element wellknown content (#7416) * Move the custom tile server URL config to the Element wellknown content * Iteration: use whole `MapTilerConfig` in the remote config --- appnav/build.gradle.kts | 1 + .../android/appnav/LoggedInFlowNode.kt | 33 ++++++++---- features/location/api/build.gradle.kts | 1 + .../location/api/LocalMapTilerConfig.kt | 23 ++++++++ .../features/location/api/StaticMapView.kt | 9 +++- .../internal/MapTilerStaticMapUrlBuilder.kt | 8 +++ .../MapTilerTileServerStyleUriBuilder.kt | 14 +++-- .../api/internal/StaticMapUrlBuilder.kt | 15 +++++- .../api/internal/TileServerStyleUriBuilder.kt | 9 ++-- .../MapTilerTileServerStyleUriBuilderTest.kt | 52 +++++++++---------- features/location/impl/build.gradle.kts | 4 ++ .../impl/common/ui/MapBottomSheetScaffold.kt | 9 ++-- .../impl/share/ShareLocationPresenter.kt | 8 +-- .../location/impl/share/ShareLocationState.kt | 3 +- .../impl/share/ShareLocationStateProvider.kt | 9 ++-- .../location/impl/share/ShareLocationView.kt | 2 +- .../impl/show/ShowLocationPresenter.kt | 12 +++-- .../location/impl/show/ShowLocationState.kt | 3 +- .../impl/show/ShowLocationStateProvider.kt | 7 +-- .../location/impl/show/ShowLocationView.kt | 2 +- .../DefaultShareLocationEntryPointTest.kt | 2 + .../impl/share/ShareLocationPresenterTest.kt | 29 ++++++----- .../show/DefaultShowLocationEntryPointTest.kt | 10 ++-- .../impl/show/ShowLocationPresenterTest.kt | 47 +++++++++++++---- features/messages/impl/build.gradle.kts | 1 + .../event/TimelineItemLocationView.kt | 2 + .../libraries/matrix/api/MatrixClient.kt | 5 -- .../libraries/matrix/impl/RustMatrixClient.kt | 6 --- .../libraries/matrix/test/FakeMatrixClient.kt | 5 -- .../wellknown/api/ElementWellKnown.kt | 15 ++++++ .../impl/InternalElementWellKnown.kt | 14 +++++ .../libraries/wellknown/impl/Mapper.kt | 9 ++++ .../impl/DefaultWellknownRetrieverTest.kt | 11 +++- .../features/wellknown/test/Fixtures.kt | 15 ++++++ tools/detekt/detekt.yml | 1 + 35 files changed, 279 insertions(+), 117 deletions(-) create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/LocalMapTilerConfig.kt diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index 7b86b434b9..054e9525c7 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -44,6 +44,7 @@ dependencies { implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.uiCommon) implementation(projects.libraries.uiStrings) + implementation(projects.libraries.wellknown.api) implementation(projects.features.login.api) implementation(libs.coil) 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 14230d7c5a..d7b768132b 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -11,8 +11,10 @@ package io.element.android.appnav import android.os.Parcelable import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope @@ -49,11 +51,13 @@ import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.createroom.api.CreateRoomEntryPoint import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.SessionEnterpriseService +import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider import io.element.android.features.ftue.api.FtueEntryPoint import io.element.android.features.ftue.api.state.FtueService import io.element.android.features.ftue.api.state.FtueState import io.element.android.features.home.api.HomeEntryPoint import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint +import io.element.android.features.location.api.LocalMapTilerConfig import io.element.android.features.location.api.live.ActiveLiveLocationShareManager import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus @@ -153,6 +157,7 @@ class LoggedInFlowNode( private val analyticsRoomListStateWatcher: AnalyticsRoomListStateWatcher, private val createRoomEntryPoint: CreateRoomEntryPoint, private val activeLiveLocationShareManager: ActiveLiveLocationShareManager, + private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Placeholder, @@ -677,6 +682,12 @@ class LoggedInFlowNode( val colors by remember { enterpriseService.semanticColorsFlow(sessionId = matrixClient.sessionId) }.collectAsState(SemanticColorsLightDark.default) + + val currentMapTilerConfig = LocalMapTilerConfig.current + val updatedMapTilerConfig by produceState(currentMapTilerConfig) { + value = remoteEnterpriseConfigProvider.get(matrixClient.sessionId).dataOrNull()?.mapTilerConfig ?: currentMapTilerConfig + } + ElementThemeApp( appPreferencesStore = appPreferencesStore, featureFlagService = featureFlagService, @@ -684,16 +695,18 @@ class LoggedInFlowNode( compoundDark = colors.dark, buildMeta = buildMeta, ) { - val isOnline by syncService.isOnline.collectAsState() - ConnectivityIndicatorContainer( - isOnline = isOnline, - modifier = modifier, - ) { contentModifier -> - Box(modifier = contentModifier) { - val ftueState by ftueService.state.collectAsState() - BackstackView(transitionHandler = rememberLoggedInFlowTransitionHandler(backstack)) - if (ftueState is FtueState.Complete) { - PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent) + CompositionLocalProvider(LocalMapTilerConfig provides updatedMapTilerConfig) { + val isOnline by syncService.isOnline.collectAsState() + ConnectivityIndicatorContainer( + isOnline = isOnline, + modifier = modifier, + ) { contentModifier -> + Box(modifier = contentModifier) { + val ftueState by ftueService.state.collectAsState() + BackstackView(transitionHandler = rememberLoggedInFlowTransitionHandler(backstack)) + if (ftueState is FtueState.Complete) { + PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent) + } } } } diff --git a/features/location/api/build.gradle.kts b/features/location/api/build.gradle.kts index f8377389f1..b3eeacf87f 100644 --- a/features/location/api/build.gradle.kts +++ b/features/location/api/build.gradle.kts @@ -70,6 +70,7 @@ dependencies { implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.uiStrings) + implementation(projects.libraries.wellknown.api) implementation(libs.coil.compose) implementation(libs.datetime) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/LocalMapTilerConfig.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/LocalMapTilerConfig.kt new file mode 100644 index 0000000000..a2212119f5 --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/LocalMapTilerConfig.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.api + +import androidx.compose.runtime.staticCompositionLocalOf +import io.element.android.libraries.wellknown.api.MapTilerConfig + +/** + * Provides a [MapTilerConfig] for the MapLibre maps. + */ +val LocalMapTilerConfig = staticCompositionLocalOf { + MapTilerConfig( + apiKey = BuildConfig.MAPTILER_API_KEY, + lightStyleId = BuildConfig.MAPTILER_LIGHT_MAP_ID, + darkStyleId = BuildConfig.MAPTILER_DARK_MAP_ID, + baseUrl = BuildConfig.MAPTILER_BASE_URL + ) +} diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt index c16b1ae560..33e454722f 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt @@ -38,13 +38,14 @@ import coil3.request.ImageRequest import coil3.request.SuccessResult import io.element.android.compound.theme.ElementTheme import io.element.android.features.location.api.internal.StaticMapPlaceholder -import io.element.android.features.location.api.internal.StaticMapUrlBuilder import io.element.android.features.location.api.internal.centerBottomEdge +import io.element.android.features.location.api.internal.rememberStaticMapBuilder import io.element.android.libraries.designsystem.components.LocationPin import io.element.android.libraries.designsystem.components.PinVariant import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.utils.CommonDrawables +import io.element.android.libraries.wellknown.api.MapTilerConfig /** * Shows a static map image downloaded via a third party service's static maps API. @@ -57,6 +58,7 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables */ @Composable fun StaticMapView( + mapTilerConfig: MapTilerConfig?, location: Location?, zoom: Double, pinVariant: PinVariant, @@ -94,6 +96,7 @@ fun StaticMapView( } // Cases 3 & 4: Non-null location - fetch map else -> LoadableMapContent( + mapTilerConfig = mapTilerConfig, location = location, zoom = zoom, pinVariant = pinVariant, @@ -124,6 +127,7 @@ private fun BoxWithConstraintsScope.StaleMapContent( @Composable private fun BoxWithConstraintsScope.LoadableMapContent( + mapTilerConfig: MapTilerConfig?, location: Location, zoom: Double, pinVariant: PinVariant, @@ -132,7 +136,7 @@ private fun BoxWithConstraintsScope.LoadableMapContent( ) { val context = LocalContext.current var retryHash by remember { mutableIntStateOf(0) } - val builder = remember { StaticMapUrlBuilder() } + val builder = rememberStaticMapBuilder(mapTilerConfig) val (painter, state, contentScale) = if (LocalInspectionMode.current) { val painter = painterResource(CommonDrawables.sample_map) @@ -203,6 +207,7 @@ private fun BoxWithConstraintsScope.LoadableMapContent( @Composable internal fun StaticMapViewPreview() = ElementPreview { StaticMapView( + mapTilerConfig = null, location = Location(0.0, 0.0), zoom = 0.0, contentDescription = null, diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt index 666ca07a08..78eae2f633 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt @@ -9,6 +9,7 @@ package io.element.android.features.location.api.internal import io.element.android.features.location.api.BuildConfig +import io.element.android.libraries.wellknown.api.MapTilerConfig import kotlin.math.roundToInt /** @@ -29,6 +30,13 @@ internal class MapTilerStaticMapUrlBuilder( darkMapId = BuildConfig.MAPTILER_DARK_MAP_ID, ) + constructor(mapTilerConfig: MapTilerConfig) : this( + baseUrl = (mapTilerConfig.baseUrl ?: BuildConfig.MAPTILER_BASE_URL).removeSuffix("/"), + apiKey = mapTilerConfig.apiKey.takeIf { it.isNotBlank() } ?: BuildConfig.MAPTILER_API_KEY, + lightMapId = mapTilerConfig.lightStyleId ?: BuildConfig.MAPTILER_LIGHT_MAP_ID, + darkMapId = mapTilerConfig.darkStyleId ?: BuildConfig.MAPTILER_DARK_MAP_ID, + ) + override fun build( lat: Double, lon: Double, diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt index 0ff18a3767..10aef90116 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt @@ -11,6 +11,7 @@ package io.element.android.features.location.api.internal import io.element.android.features.location.api.BuildConfig +import io.element.android.libraries.wellknown.api.MapTilerConfig internal class MapTilerTileServerStyleUriBuilder( private val baseUrl: String, @@ -26,14 +27,17 @@ internal class MapTilerTileServerStyleUriBuilder( ) override fun build( - customMapStyleUrl: String?, + customMapTilerConfig: MapTilerConfig?, darkMode: Boolean, ): String { - return if (customMapStyleUrl.isNullOrBlank()) { - val mapId = if (darkMode) darkMapId else lightMapId - "$baseUrl/$mapId/style.json?key=$apiKey" + val baseUrl = customMapTilerConfig?.baseUrl.takeIf { !it.isNullOrBlank() } ?: baseUrl + val apiKey = customMapTilerConfig?.apiKey ?: apiKey + val mapId = if (darkMode) { + customMapTilerConfig?.darkStyleId ?: darkMapId } else { - customMapStyleUrl + customMapTilerConfig?.lightStyleId ?: lightMapId } + + return "$baseUrl/$mapId/style.json?key=$apiKey" } } diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt index 6636976828..9bff0ee754 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt @@ -8,6 +8,10 @@ package io.element.android.features.location.api.internal +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import io.element.android.libraries.wellknown.api.MapTilerConfig + /** * Builds an URL for a 3rd party service provider static maps API. */ @@ -25,4 +29,13 @@ interface StaticMapUrlBuilder { fun isServiceAvailable(): Boolean } -fun StaticMapUrlBuilder(): StaticMapUrlBuilder = MapTilerStaticMapUrlBuilder() +@Composable +fun rememberStaticMapBuilder(mapTilerConfig: MapTilerConfig?): StaticMapUrlBuilder { + return remember(mapTilerConfig) { + if (mapTilerConfig != null) { + MapTilerStaticMapUrlBuilder(mapTilerConfig) + } else { + MapTilerStaticMapUrlBuilder() + } + } +} diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt index 762d580f8e..f0c946f3d0 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt @@ -11,6 +11,7 @@ package io.element.android.features.location.api.internal import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.wellknown.api.MapTilerConfig /** * Builds a style URI for a MapLibre compatible tile server. @@ -19,7 +20,7 @@ import io.element.android.compound.theme.ElementTheme */ interface TileServerStyleUriBuilder { fun build( - customMapStyleUrl: String?, + customMapTilerConfig: MapTilerConfig?, darkMode: Boolean, ): String } @@ -31,12 +32,12 @@ interface TileServerStyleUriBuilder { */ @Composable fun rememberTileStyleUrl( - customMapStyleUrl: String?, + customMapTileConfig: MapTilerConfig?, ): String { val darkMode = !ElementTheme.isLightTheme - return remember(darkMode, customMapStyleUrl) { + return remember(darkMode, customMapTileConfig) { MapTilerTileServerStyleUriBuilder().build( - customMapStyleUrl = customMapStyleUrl, + customMapTilerConfig = customMapTileConfig, darkMode = darkMode, ) } diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt index addc71c3b9..09aae4f310 100644 --- a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt +++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt @@ -9,6 +9,7 @@ package io.element.android.features.location.api.internal import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.wellknown.api.MapTilerConfig import org.junit.Test class MapTilerTileServerStyleUriBuilderTest { @@ -23,7 +24,7 @@ class MapTilerTileServerStyleUriBuilderTest { fun `light map uri`() { assertThat( builder.build( - customMapStyleUrl = null, + customMapTilerConfig = null, darkMode = false, ) ).isEqualTo("https://base.url/aLightMapId/style.json?key=anApiKey") @@ -33,7 +34,7 @@ class MapTilerTileServerStyleUriBuilderTest { fun `dark map uri`() { assertThat( builder.build( - customMapStyleUrl = null, + customMapTilerConfig = null, darkMode = true, ) ).isEqualTo("https://base.url/aDarkMapId/style.json?key=anApiKey") @@ -43,49 +44,44 @@ class MapTilerTileServerStyleUriBuilderTest { fun `custom map uri light`() { assertThat( builder.build( - customMapStyleUrl = "https://custom.url/style.json", + customMapTilerConfig = MapTilerConfig( + apiKey = "API_KEY", + lightStyleId = "light", + darkStyleId = "dark", + baseUrl = "https://custom.url/style.json" + ), darkMode = false, ) - ).isEqualTo("https://custom.url/style.json") + ).isEqualTo("https://custom.url/style.json/light/style.json?key=API_KEY") } @Test fun `custom map uri dark`() { assertThat( builder.build( - customMapStyleUrl = "https://custom.url/style.json", + customMapTilerConfig = MapTilerConfig( + apiKey = "API_KEY", + lightStyleId = "light", + darkStyleId = "dark", + baseUrl = "https://custom.url/style.json" + ), darkMode = true, ) - ).isEqualTo("https://custom.url/style.json") - } - - @Test - fun `custom map uri with its own api key is not altered`() { - assertThat( - builder.build( - customMapStyleUrl = "https://api.maptiler.com/maps/streets-v2/style.json?key=anOperatorKey", - darkMode = false, - ) - ).isEqualTo("https://api.maptiler.com/maps/streets-v2/style.json?key=anOperatorKey") - } - - @Test - fun `custom map uri with an existing query is not altered`() { - assertThat( - builder.build( - customMapStyleUrl = "https://self.hosted/style.json?foo=bar", - darkMode = true, - ) - ).isEqualTo("https://self.hosted/style.json?foo=bar") + ).isEqualTo("https://custom.url/style.json/dark/style.json?key=API_KEY") } @Test fun `blank custom map uri falls back to the built-in style`() { assertThat( builder.build( - customMapStyleUrl = " ", + customMapTilerConfig = MapTilerConfig( + apiKey = "API_KEY", + lightStyleId = "light", + darkStyleId = "dark", + baseUrl = " " + ), darkMode = false, ) - ).isEqualTo("https://base.url/aLightMapId/style.json?key=anApiKey") + ).isEqualTo("https://base.url/light/style.json?key=API_KEY") } } diff --git a/features/location/impl/build.gradle.kts b/features/location/impl/build.gradle.kts index 165c32b7c5..43e4f58cab 100644 --- a/features/location/impl/build.gradle.kts +++ b/features/location/impl/build.gradle.kts @@ -26,6 +26,7 @@ setupDependencyInjection() dependencies { api(projects.features.location.api) + implementation(projects.features.enterprise.api) implementation(projects.features.messages.api) implementation(libs.maplibre.compose) implementation(libs.coil) @@ -45,6 +46,7 @@ dependencies { implementation(projects.libraries.preferences.api) implementation(projects.libraries.push.api) implementation(projects.libraries.sessionStorage.api) + implementation(projects.libraries.wellknown.api) implementation(libs.androidx.datastore.preferences) implementation(libs.datetime) @@ -58,5 +60,7 @@ dependencies { testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.sessionStorage.test) + testImplementation(projects.libraries.wellknown.test) testImplementation(projects.features.location.test) + testImplementation(projects.features.enterprise.test) } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt index 0eff1b6028..eaa238bed0 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt @@ -50,6 +50,7 @@ import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.theme.components.BottomSheetScaffold import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator +import io.element.android.libraries.wellknown.api.MapTilerConfig import org.maplibre.compose.camera.CameraState import org.maplibre.compose.camera.rememberCameraState import org.maplibre.compose.map.MapOptions @@ -66,7 +67,7 @@ import kotlin.math.roundToInt * - Updating camera position padding based on sheet height * - Rendering the MaplibreMap with proper ornament positioning * - * @param customMapStyleUrl Optional custom style URL for the map + * @param customMapTilerConfig Optional custom style URL for the map * @param modifier Modifier for the root layout * @param scaffoldState State for the bottom sheet scaffold * @param cameraState The camera state for the map @@ -83,7 +84,7 @@ import kotlin.math.roundToInt @OptIn(ExperimentalMaterial3Api::class) @Composable fun MapBottomSheetScaffold( - customMapStyleUrl: AsyncData, + customMapTilerConfig: AsyncData, modifier: Modifier = Modifier, scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState( bottomSheetState = rememberBottomSheetState( @@ -137,11 +138,11 @@ fun MapBottomSheetScaffold( val ornamentOptions = mapOptions.ornamentOptions.copy(padding = sheetPadding) val mapOptions = mapOptions.copy(ornamentOptions = ornamentOptions) Box { - when (customMapStyleUrl) { + when (customMapTilerConfig) { is AsyncData.Success -> { MaplibreMap( options = mapOptions, - baseStyle = BaseStyle.Uri(rememberTileStyleUrl(customMapStyleUrl.data)), + baseStyle = BaseStyle.Uri(rememberTileStyleUrl(customMapTilerConfig.data)), modifier = Modifier.fillMaxSize(), cameraState = cameraState, content = mapContent, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt index a3f3cac432..d388e0c5d2 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt @@ -22,6 +22,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Composer +import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider import io.element.android.features.location.api.live.ActiveLiveLocationShareManager import io.element.android.features.location.impl.common.LocationConstraintsCheck import io.element.android.features.location.impl.common.MapDefaults @@ -72,6 +73,7 @@ class ShareLocationPresenter( private val liveLocationShareManager: ActiveLiveLocationShareManager, private val liveLocationStore: LiveLocationStore, private val userLocationStateFactory: UserLocationState.Factory, + private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider, ) : Presenter { @AssistedFactory fun interface Factory { @@ -92,9 +94,9 @@ class ShareLocationPresenter( var pendingLiveLocationShare by remember { mutableStateOf(false) } val startLiveLocationAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } val currentUser by client.userProfile.collectAsState() - val customMapStyleUrl by produceState(AsyncData.Loading()) { + val customMapConfig by produceState(AsyncData.Loading()) { // Ignore errors - value = AsyncData.Success(client.getMapStyleUrl().getOrNull()) + value = AsyncData.Success(remoteEnterpriseConfigProvider.get(client.sessionId).dataOrNull()?.mapTilerConfig) } val sendLiveLocationPermissions by room.permissionsAsState(SendLiveLocationPermissions.DEFAULT) { perms -> perms.sendLiveLocationPermissions() @@ -199,7 +201,7 @@ class ShareLocationPresenter( } return ShareLocationState( - customMapStyleUrl = customMapStyleUrl, + customMapTilerConfig = customMapConfig, currentUser = currentUser, dialogState = dialogState, trackUserLocation = trackUserPosition, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt index fb1b803435..d2cce94ef8 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt @@ -13,10 +13,11 @@ import io.element.android.features.location.impl.common.userlocation.UserLocatio import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.wellknown.api.MapTilerConfig import kotlinx.collections.immutable.ImmutableList data class ShareLocationState( - val customMapStyleUrl: AsyncData, + val customMapTilerConfig: AsyncData, val currentUser: MatrixUser, val dialogState: Dialog, val trackUserLocation: Boolean, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt index ed8d46afd3..6d7ff5e612 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt @@ -15,6 +15,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.wellknown.api.MapTilerConfig import kotlinx.collections.immutable.persistentListOf import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes @@ -70,13 +71,13 @@ class ShareLocationStateProvider : PreviewParameterProvider canShareLiveLocation = true, ), aShareLocationState( - customMapStyleUrl = AsyncData.Loading(), + customMapTilerConfig = AsyncData.Loading(), ), ) } fun aShareLocationState( - customMapStyleUrl: AsyncData = AsyncData.Success(null), + customMapTilerConfig: AsyncData = AsyncData.Success(null), currentUser: MatrixUser = MatrixUser(UserId("@user:matrix.org")), dialogState: ShareLocationState.Dialog = ShareLocationState.Dialog.None, trackUserPosition: Boolean = false, @@ -87,7 +88,7 @@ fun aShareLocationState( eventSink: (ShareLocationEvent) -> Unit = {}, ): ShareLocationState { return ShareLocationState( - customMapStyleUrl = customMapStyleUrl, + customMapTilerConfig = customMapTilerConfig, currentUser = currentUser, dialogState = dialogState, trackUserLocation = trackUserPosition, @@ -95,6 +96,6 @@ fun aShareLocationState( canShareLiveLocation = canShareLiveLocation, appName = appName, startLiveLocationAction = startLiveLocationAction, - eventSink = eventSink + eventSink = eventSink, ) } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt index 8b7106e939..53e7874936 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt @@ -118,7 +118,7 @@ fun ShareLocationView( } MapBottomSheetScaffold( - customMapStyleUrl = state.customMapStyleUrl, + customMapTilerConfig = state.customMapTilerConfig, cameraState = cameraState, modifier = modifier, scaffoldState = scaffoldState, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt index f187e91c53..2d9a4900dd 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject +import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider import io.element.android.features.location.api.Location import io.element.android.features.location.api.ShowLocationMode import io.element.android.features.location.api.live.ActiveLiveLocationShareManager @@ -46,7 +47,7 @@ import io.element.android.libraries.dateformatter.api.DateFormatter import io.element.android.libraries.dateformatter.api.DateFormatterMode import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.getBestName import io.element.android.libraries.matrix.api.room.joinedRoomMembers @@ -64,10 +65,11 @@ import kotlinx.coroutines.launch private val buildMeta: BuildMeta, private val dateFormatter: DateFormatter, private val stringProvider: StringProvider, - private val client: MatrixClient, + private val sessionId: SessionId, private val joinedRoom: JoinedRoom, private val liveLocationShareManager: ActiveLiveLocationShareManager, private val userLocationStateFactory: UserLocationState.Factory, + private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider, ) : Presenter { @AssistedFactory fun interface Factory { fun create(mode: ShowLocationMode): ShowLocationPresenter @@ -85,9 +87,9 @@ import kotlinx.coroutines.launch mutableStateOf(LocationConstraintsDialogState.None) } - val customMapStyleUrl by produceState(AsyncData.Loading()) { + val customMapTilerConfig by produceState(AsyncData.Loading()) { // Ignore errors - value = AsyncData.Success(client.getMapStyleUrl().getOrNull()) + value = AsyncData.Success(remoteEnterpriseConfigProvider.get(sessionId).dataOrNull()?.mapTilerConfig) } fun checkLocationConstraints() { @@ -220,7 +222,7 @@ import kotlinx.coroutines.launch userLocationStateFactory.create(hasLocationPermission = permissionsState.isAnyGranted) } return ShowLocationState( - customMapStyleUrl = customMapStyleUrl, + customMapTilerConfig = customMapTilerConfig, dialogState = dialogState, locationShares = locationShares, focusedLocation = focusedLocation, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt index e5ddf589b0..2ae76ef9e9 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt @@ -17,10 +17,11 @@ import io.element.android.libraries.designsystem.components.PinVariant import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.location.AssetType +import io.element.android.libraries.wellknown.api.MapTilerConfig import kotlinx.collections.immutable.ImmutableList data class ShowLocationState( - val customMapStyleUrl: AsyncData, + val customMapTilerConfig: AsyncData, val isLive: Boolean, val dialogState: LocationConstraintsDialogState, val locationShares: ImmutableList, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt index cd7ea108bb..a1f349d644 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt @@ -18,6 +18,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.location.AssetType +import io.element.android.libraries.wellknown.api.MapTilerConfig import kotlinx.collections.immutable.toImmutableList class ShowLocationStateProvider : PreviewParameterProvider { @@ -36,14 +37,14 @@ class ShowLocationStateProvider : PreviewParameterProvider { constraintsDialogState = LocationConstraintsDialogState.LocationServiceDisabled, ), aShowLocationState(isTrackMyLocation = true), - aShowLocationState(customMapStyleUrl = AsyncData.Loading()), + aShowLocationState(customMapTilerConfig = AsyncData.Loading()), ) } private const val APP_NAME = "ApplicationName" fun aShowLocationState( - customMapStyleUrl: AsyncData = AsyncData.Success(null), + customMapTilerConfig: AsyncData = AsyncData.Success(null), isLive: Boolean = false, constraintsDialogState: LocationConstraintsDialogState = LocationConstraintsDialogState.None, locationShares: List = listOf(aLocationShareItem(isLive = isLive)), @@ -55,7 +56,7 @@ fun aShowLocationState( eventSink: (ShowLocationEvent) -> Unit = {}, ): ShowLocationState { return ShowLocationState( - customMapStyleUrl = customMapStyleUrl, + customMapTilerConfig = customMapTilerConfig, dialogState = constraintsDialogState, locationShares = locationShares.toImmutableList(), focusedLocation = focusedLocation, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 9fdbe3aac8..45e4c7f4cc 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -102,7 +102,7 @@ fun ShowLocationView( } } MapBottomSheetScaffold( - customMapStyleUrl = state.customMapStyleUrl, + customMapTilerConfig = state.customMapTilerConfig, sheetDragHandle = if (state.isSheetDraggable) { { BottomSheetDefaults.DragHandle() } } else { diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt index 49df684666..9be168e5a2 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt @@ -11,6 +11,7 @@ package io.element.android.features.location.impl.share import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider import io.element.android.features.location.impl.common.FakeUserLocationStateFactory import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter @@ -58,6 +59,7 @@ class DefaultShareLocationEntryPointTest { sessionId = room.sessionId, ), userLocationStateFactory = FakeUserLocationStateFactory(), + remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(), ) }, analyticsService = FakeAnalyticsService(), diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt index f4cc135e9e..6748c5a675 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt @@ -19,6 +19,8 @@ import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Composer +import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider +import io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider import io.element.android.features.location.api.Location import io.element.android.features.location.impl.aPermissionsState import io.element.android.features.location.impl.common.FakeUserLocationStateFactory @@ -30,6 +32,8 @@ import io.element.android.features.location.impl.common.ui.LocationConstraintsDi import io.element.android.features.location.impl.live.LiveLocationStore import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.features.messages.test.FakeMessageComposerContext +import io.element.android.features.wellknown.test.aMapTilerConfig +import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.dateformatter.test.FakeDurationFormatter import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -51,6 +55,7 @@ import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermiss import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFactory +import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.assert @@ -60,7 +65,6 @@ import io.element.android.tests.testutils.test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -77,20 +81,18 @@ class ShareLocationPresenterTest { private val fakeMessageComposerContext = FakeMessageComposerContext() private val fakeLocationActions = FakeLocationActions() private val fakeBuildMeta = aBuildMeta(applicationName = "app name") - private val fakeMatrixClient = FakeMatrixClient( - sessionId = A_USER_ID, - getMapStyleUrlResult = { Result.success(null) }, - ) + private val fakeMatrixClient = FakeMatrixClient(sessionId = A_USER_ID) private val durationFormatter = FakeDurationFormatter() - private fun TestScope.createShareLocationPresenter( + private fun createShareLocationPresenter( joinedRoom: JoinedRoom = FakeJoinedRoom(), timelineMode: Timeline.Mode = Timeline.Mode.Live, locationActions: FakeLocationActions = fakeLocationActions, liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(), liveLocationStore: LiveLocationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId), client: FakeMatrixClient = fakeMatrixClient, + remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(), ): ShareLocationPresenter = ShareLocationPresenter( permissionsPresenterFactory = { fakePermissionsPresenter }, room = joinedRoom, @@ -104,6 +106,7 @@ class ShareLocationPresenterTest { liveLocationShareManager = liveLocationShareManager, liveLocationStore = liveLocationStore, userLocationStateFactory = FakeUserLocationStateFactory(), + remoteEnterpriseConfigProvider = remoteEnterpriseConfigProvider, ) @Test @@ -118,7 +121,7 @@ class ShareLocationPresenterTest { val shareLocationPresenter = createShareLocationPresenter() shareLocationPresenter.test { val state = awaitFirstItem() - assertThat(state.customMapStyleUrl.isLoading()).isFalse() + assertThat(state.customMapTilerConfig.isLoading()).isFalse() assertThat(state.trackUserLocation).isTrue() assertThat(state.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None)) } @@ -126,18 +129,20 @@ class ShareLocationPresenterTest { @Test fun `present - non-null customMapStyleUrl`() = runTest { + val mapTilerConfig = aMapTilerConfig(apiKey = "A KEY") val shareLocationPresenter = createShareLocationPresenter( client = FakeMatrixClient( sessionId = A_USER_ID, - getMapStyleUrlResult = { Result.success("aUrl") }, + ), + remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider( + getResult = { WellknownRetrieverResult.Success(anElementWellKnown(mapTilerConfig = mapTilerConfig)) } ) ) shareLocationPresenter.test { - skipItems(1) val state = awaitItem() - assertThat(state.customMapStyleUrl.isLoading()).isTrue() + assertThat(state.customMapTilerConfig.isLoading()).isTrue() val finalState = awaitItem() - assertThat(finalState.customMapStyleUrl.dataOrNull()).isEqualTo("aUrl") + assertThat(finalState.customMapTilerConfig.dataOrNull()).isEqualTo(mapTilerConfig) } } @@ -709,7 +714,7 @@ class ShareLocationPresenterTest { } private suspend fun ReceiveTurbine.awaitFirstItem(): T { - skipItems(2) + skipItems(1) return awaitItem() } } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt index 23b7e9bf4d..69451eedb7 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt @@ -11,6 +11,7 @@ package io.element.android.features.location.impl.show import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider import io.element.android.features.location.api.Location import io.element.android.features.location.api.ShowLocationEntryPoint import io.element.android.features.location.api.ShowLocationMode @@ -20,7 +21,7 @@ import io.element.android.features.location.impl.common.permissions.FakePermissi import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.services.analytics.test.FakeAnalyticsService @@ -41,8 +42,8 @@ class DefaultShowLocationEntryPointTest { ShowLocationNode( buildContext = buildContext, plugins = plugins, - presenterFactory = object : ShowLocationPresenter.Factory { - override fun create(mode: ShowLocationMode) = ShowLocationPresenter( + presenterFactory = { mode -> + ShowLocationPresenter( mode = mode, permissionsPresenterFactory = { FakePermissionsPresenter() }, locationActions = FakeLocationActions(), @@ -50,9 +51,10 @@ class DefaultShowLocationEntryPointTest { dateFormatter = FakeDateFormatter(), stringProvider = FakeStringProvider(), joinedRoom = joinedRoom, - client = FakeMatrixClient(), + sessionId = A_SESSION_ID, liveLocationShareManager = FakeActiveLiveLocationShareManager(), userLocationStateFactory = FakeUserLocationStateFactory(), + remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(), ) }, analyticsService = FakeAnalyticsService(), diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index b579a79e4f..77b26eb66c 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -12,6 +12,7 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider import io.element.android.features.location.api.Location import io.element.android.features.location.api.ShowLocationMode import io.element.android.features.location.impl.aPermissionsState @@ -22,17 +23,20 @@ import io.element.android.features.location.impl.common.permissions.PermissionsE import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.features.location.test.FakeActiveLiveLocationShareManager +import io.element.android.features.wellknown.test.aMapTilerConfig +import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.dateformatter.test.FakeDateFormatter -import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.location.LiveLocationShare +import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID -import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.room.location.aLiveLocationShare +import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test @@ -65,8 +69,9 @@ class ShowLocationPresenterTest { ), locationActions: FakeLocationActions = fakeLocationActions, joinedRoom: JoinedRoom = FakeJoinedRoom(), - client: MatrixClient = FakeMatrixClient(), + sessionId: SessionId = A_SESSION_ID, liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(), + remoteEnterpriseConfigProvider: FakeRemoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(), ) = ShowLocationPresenter( mode = mode, permissionsPresenterFactory = { fakePermissionsPresenter }, @@ -75,24 +80,26 @@ class ShowLocationPresenterTest { dateFormatter = fakeDateFormatter, stringProvider = FakeStringProvider(), joinedRoom = joinedRoom, - client = client, + sessionId = sessionId, liveLocationShareManager = liveLocationShareManager, userLocationStateFactory = FakeUserLocationStateFactory(), + remoteEnterpriseConfigProvider = remoteEnterpriseConfigProvider, ) @Test fun `present - non-null customMapStyleUrl`() = runTest { + val mapTilerConfig = aMapTilerConfig(apiKey = "A KEY") val shareLocationPresenter = createShowLocationPresenter( - client = FakeMatrixClient( - sessionId = A_USER_ID, - getMapStyleUrlResult = { Result.success("aUrl") }, + sessionId = A_SESSION_ID, + remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider( + getResult = { WellknownRetrieverResult.Success(anElementWellKnown(mapTilerConfig = mapTilerConfig)) } ) ) shareLocationPresenter.test { val state = awaitItem() - assertThat(state.customMapStyleUrl.isLoading()).isTrue() + assertThat(state.customMapTilerConfig.isLoading()).isTrue() val finalState = awaitItem() - assertThat(finalState.customMapStyleUrl.dataOrNull()).isEqualTo("aUrl") + assertThat(finalState.customMapTilerConfig.dataOrNull()).isEqualTo(mapTilerConfig) } } @@ -115,6 +122,8 @@ class ShowLocationPresenterTest { initialState.eventSink(ShowLocationEvent.Share(location)) assertThat(fakeLocationActions.sharedLocation).isEqualTo(location) + + cancelAndIgnoreRemainingEvents() } } @@ -123,10 +132,12 @@ class ShowLocationPresenterTest { fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted)) val presenter = createShowLocationPresenter( - client = FakeMatrixClient( - getMapStyleUrlResult = { Result.success(null) } + sessionId = A_SESSION_ID, + remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider( + getResult = { WellknownRetrieverResult.Success(anElementWellKnown(mapTilerConfig = aMapTilerConfig())) } ) ) + presenter.test { val initialState = awaitItem() assertThat(initialState.isTrackMyLocation).isFalse() @@ -281,6 +292,8 @@ class ShowLocationPresenterTest { }.test { val initialState = awaitItem() assertThat(initialState.appName).isEqualTo("app name") + + cancelAndIgnoreRemainingEvents() } } @@ -334,6 +347,8 @@ class ShowLocationPresenterTest { val initialState = awaitItem() assertThat(initialState.locationShares).isEmpty() assertThat(initialState.isSheetDraggable).isFalse() + + cancelAndIgnoreRemainingEvents() } } @@ -408,6 +423,9 @@ class ShowLocationPresenterTest { val initialState = awaitItem() assertThat(initialState.locationShares).isEmpty() + // Wait until we receive the custom map style URL result + skipItems(1) + // Emit a new live share liveSharesFlow.value = listOf( aLiveLocationShare(userId = userId) @@ -447,6 +465,8 @@ class ShowLocationPresenterTest { assertThat(item.avatarData.id).isEqualTo(senderId.value) assertThat(item.avatarData.name).isEqualTo(senderName) assertThat(item.avatarData.url).isEqualTo(avatarUrl) + + cancelAndIgnoreRemainingEvents() } } @@ -456,6 +476,7 @@ class ShowLocationPresenterTest { presenter.test { val state = awaitItem() assertThat(state.isSheetDraggable).isFalse() + cancelAndIgnoreRemainingEvents() } } @@ -465,6 +486,8 @@ class ShowLocationPresenterTest { presenter.test { val state = awaitItem() assertThat(state.hideUserLocationPuck).isFalse() + + cancelAndIgnoreRemainingEvents() } } @@ -476,6 +499,8 @@ class ShowLocationPresenterTest { presenter.test { val state = awaitItem() assertThat(state.hideUserLocationPuck).isFalse() + + cancelAndIgnoreRemainingEvents() } } diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index b40aebab39..0b51a2b80d 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -61,6 +61,7 @@ dependencies { implementation(projects.libraries.push.api) implementation(projects.libraries.uiUtils) implementation(projects.libraries.testtags) + implementation(projects.libraries.wellknown.api) implementation(projects.features.networkmonitor.api) implementation(projects.features.contentscanner.api) implementation(projects.services.analytics.compose) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt index 4ab4ee84a6..a7f7b33989 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.location.api.LocalMapTilerConfig import io.element.android.features.location.api.StaticMapView import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContentProvider @@ -50,6 +51,7 @@ fun TimelineItemLocationView( modifier = Modifier .fillMaxWidth() .heightIn(max = 188.dp), + mapTilerConfig = LocalMapTilerConfig.current, pinVariant = content.pinVariant, location = content.location, zoom = 15.0, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index e3aa2004bf..26d7d02016 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -255,11 +255,6 @@ interface MatrixClient : UrlContentFetcher { */ suspend fun performDatabaseVacuum(): Result - /** - * Returns the URL of the map style configured on the server, if any. - */ - suspend fun getMapStyleUrl(): Result - /** * Resets the cached client `well-known` config by the SDK. */ diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 7aacb8edc6..0ff563a204 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 @@ -886,12 +886,6 @@ class RustMatrixClient( } } - override suspend fun getMapStyleUrl(): Result = withContext(sessionDispatcher) { - runCatchingExceptions { - innerClient.tileServer()?.mapStyleUrl - } - } - override suspend fun resetWellKnownConfig(): Result { return runCatchingExceptions { Timber.d("Resetting well-known config for session $sessionId") diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 60f70c12c5..56d25ed175 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -129,7 +129,6 @@ class FakeMatrixClient( private val markRoomAsFullyReadResult: (RoomId, EventId) -> Result = { _, _ -> lambdaError() }, private val markAllRoomsAsReadResult: () -> Result = { Result.success(Unit) }, private val performDatabaseVacuumLambda: () -> Result = { lambdaError() }, - private val getMapStyleUrlResult: () -> Result = { lambdaError() }, private val getDatabaseSizesLambda: () -> Result = { lambdaError() }, private val resetWellKnownConfigLambda: () -> Result = { lambdaError() }, private val enableAutomaticCallStatusLambda: (Boolean) -> Unit = { }, @@ -435,10 +434,6 @@ class FakeMatrixClient( return performDatabaseVacuumLambda() } - override suspend fun getMapStyleUrl(): Result = simulateLongTask { - getMapStyleUrlResult() - } - override suspend fun canLinkNewDevice(): Result = simulateLongTask { return canLinkNewDeviceResult() } diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt index a9c42b7121..88a89e7a49 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt @@ -18,4 +18,19 @@ data class ElementWellKnown( val customRecoveryPassphrase: CustomRecoveryPassphrase?, val contentScannerUrl: String?, val forceDisableE2EE: Boolean?, + val mapTilerConfig: MapTilerConfig?, +) + +/** + * A `MapTiler` configuration received from the server. This configuration is used to display maps in the app. + */ +data class MapTilerConfig( + /** The API key to use with MapTiler. This is always not null, but can be empty/blank. */ + val apiKey: String, + /** The style ID to use for light mode. This is optional and can be null. */ + val lightStyleId: String?, + /** The style ID to use for light mode. This is optional and can be null. */ + val darkStyleId: String?, + /** The base URL to use for MapTiler. This is optional and can be null. */ + val baseUrl: String?, ) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt index c585264f55..317109a1c0 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt @@ -48,6 +48,8 @@ data class InternalElementWellKnown( val contentScannerUrl: String? = null, @SerialName("force_disable_e2ee") val forceDisableE2EE: Boolean? = null, + @SerialName("map_tiler") + val mapTilerConfig: InternalMapTilerConfig? = null, ) @Serializable @@ -55,3 +57,15 @@ data class InternalCustomRecoveryPassphrase( @SerialName("min_character_count") val minCharacterCount: Int? = null, ) + +@Serializable +data class InternalMapTilerConfig( + @SerialName("api_key") + val apiKey: String, + @SerialName("light_style_id") + val lightStyleId: String? = null, + @SerialName("dark_style_id") + val darkStyleId: String? = null, + @SerialName("base_url") + val baseUrl: String? = null, +) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt index c4d15e7325..254036c5c3 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt @@ -11,6 +11,7 @@ package io.element.android.libraries.wellknown.impl import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase import io.element.android.libraries.wellknown.api.ElementWellKnown +import io.element.android.libraries.wellknown.api.MapTilerConfig import timber.log.Timber private val loggerTag = LoggerTag("Wellknown") @@ -32,6 +33,14 @@ fun InternalElementWellKnown.map() = ElementWellKnown( customRecoveryPassphrase = customRecoveryPassphrase?.toPublic(), contentScannerUrl = contentScannerUrl, forceDisableE2EE = forceDisableE2EE, + mapTilerConfig = mapTilerConfig?.let { + MapTilerConfig( + apiKey = it.apiKey, + lightStyleId = it.lightStyleId, + darkStyleId = it.darkStyleId, + baseUrl = it.baseUrl, + ) + } ) private fun InternalCustomRecoveryPassphrase.toPublic(): CustomRecoveryPassphrase { diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt index 224c847387..a58aa0a347 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt @@ -13,6 +13,7 @@ package io.element.android.libraries.wellknown.impl import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.wellknown.test.FakeElementWellknownStore +import io.element.android.features.wellknown.test.aMapTilerConfig import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.matrix.api.UrlContentFetcher @@ -204,7 +205,11 @@ class DefaultWellknownRetrieverTest { "notification_sound": "a_notification_sound.flac", "idp_app_scheme": "an_app_scheme", "content_scanner_url": "https://content-scanner.example.com", - "force_disable_e2ee": false + "force_disable_e2ee": false, + "map_tiler": { + "api_key": "test_api_key", + "base_url": "https://tile-server.example.com" + } }""" private val expectedElementWellKnown = ElementWellKnown( @@ -217,6 +222,10 @@ class DefaultWellknownRetrieverTest { contentScannerUrl = "https://content-scanner.example.com", customRecoveryPassphrase = null, forceDisableE2EE = false, + mapTilerConfig = aMapTilerConfig( + apiKey = "test_api_key", + baseUrl = "https://tile-server.example.com", + ) ) } } diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt index 941e5f4cc7..36d9f7bc82 100644 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt +++ b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt @@ -10,6 +10,7 @@ package io.element.android.features.wellknown.test import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase import io.element.android.libraries.wellknown.api.ElementWellKnown +import io.element.android.libraries.wellknown.api.MapTilerConfig fun anElementWellKnown( registrationHelperUrl: String? = null, @@ -21,6 +22,7 @@ fun anElementWellKnown( customRecoveryPassphrase: CustomRecoveryPassphrase? = null, contentScannerUrl: String? = null, forceDisableE2EE: Boolean? = null, + mapTilerConfig: MapTilerConfig? = null, ) = ElementWellKnown( registrationHelperUrl = registrationHelperUrl, enforceElementPro = enforceElementPro, @@ -31,6 +33,7 @@ fun anElementWellKnown( customRecoveryPassphrase = customRecoveryPassphrase, contentScannerUrl = contentScannerUrl, forceDisableE2EE = forceDisableE2EE, + mapTilerConfig = mapTilerConfig, ) fun aCustomRecoveryPassphrase( @@ -38,3 +41,15 @@ fun aCustomRecoveryPassphrase( ) = CustomRecoveryPassphrase( minCharacterCount = minCharacterCount, ) + +fun aMapTilerConfig( + apiKey: String = "test_api_key", + baseUrl: String? = null, + lightStyleId: String? = null, + darkStyleId: String? = null, +) = MapTilerConfig( + apiKey = apiKey, + baseUrl = baseUrl, + lightStyleId = lightStyleId, + darkStyleId = darkStyleId, +) diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 79bfeb7250..147eca01d0 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -230,6 +230,7 @@ Compose: - LocalEventContentValidationState - LocalSnackbarDispatcher - LocalCameraPositionState + - LocalMapTilerConfig - LocalMediaItemPresenterFactories - LocalTimelineItemPresenterFactories - LocalRoomMemberProfilesCache From 99bbd0e29ec11388342353543d748955b42f9146 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 09:16:53 +0200 Subject: [PATCH 159/338] Add KDoc to matrix API notification and pusher interfaces Document NotificationSettingsService and PushersService along with all their members, and complete the NotificationService documentation. Fix the documentation of notificationSettingsChangeFlow, which described a state flow of room notification settings when the member is a SharedFlow that only signals that the push rules changed. That was the sole reference to RoomNotificationSettingsState in the file, so its now unused import is removed. The docs also explain why so many of these methods take isEncrypted and isOneToOne: Matrix keeps a separate default push rule per combination. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/notification/NotificationService.kt | 3 + .../NotificationSettingsService.kt | 90 ++++++++++++++++++- .../matrix/api/pusher/PushersService.kt | 14 +++ 3 files changed, 105 insertions(+), 2 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt index 369587cd10..3a3fbeaa19 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt @@ -24,6 +24,9 @@ typealias GetNotificationDataResult = Result>): GetNotificationDataResult } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt index 7ae1ca4886..7226de9a9d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt @@ -11,28 +11,114 @@ package io.element.android.libraries.matrix.api.notificationsettings import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.RoomNotificationSettings -import io.element.android.libraries.matrix.api.room.RoomNotificationSettingsState import kotlinx.coroutines.flow.SharedFlow +/** + * Reads and writes the push rules of the account, which decide what generates a notification. + * + * Rules are account data shared across clients, so a change made here is visible on the user's other devices. + * Several methods take the encryption and one-to-one nature of a room, because Matrix keeps a separate default rule for each combination. + */ interface NotificationSettingsService { /** - * State of the current room notification settings flow ([RoomNotificationSettingsState.Unknown] if not started). + * Emits every time the push rules change, whether from this client or another one, without saying what changed. + * Callers are expected to re-read the values they display; only the most recent signal is buffered. */ val notificationSettingsChangeFlow: SharedFlow + + /** + * Returns the notification settings that apply to one room, which may come from a room specific rule or from the matching default. + * + * @param roomId the room to read the settings of. + * @param isEncrypted whether that room is encrypted, which selects the relevant default rule. + * @param isOneToOne whether that room is a direct message, which selects the relevant default rule. + */ suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result + + /** + * Returns the default notification mode used by rooms that have no rule of their own. + * + * @param isEncrypted whether to read the default for encrypted rooms. + * @param isOneToOne whether to read the default for direct messages. + */ suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result + + /** + * Changes the default notification mode for a whole category of rooms. + * + * @param isEncrypted whether to change the default for encrypted rooms. + * @param mode the new default mode. + * @param isDM whether to change the default for direct messages. + */ suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isDM: Boolean): Result + + /** + * Sets a room specific notification mode, which from then on overrides the default for that room. + * + * @param roomId the room to change. + * @param mode the new mode for that room. + */ suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result + + /** + * Removes the room specific rule so the room follows the matching default again. + * + * @param roomId the room to reset. + */ suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result + + /** + * Mutes a room, which is a room specific rule like any other and is undone by [unmuteRoom]. + * + * @param roomId the room to mute. + */ suspend fun muteRoom(roomId: RoomId): Result + + /** + * Unmutes a room, restoring either its previous room specific mode or the matching default. + * + * @param roomId the room to unmute. + * @param isEncrypted whether that room is encrypted, which selects the relevant default rule. + * @param isOneToOne whether that room is a direct message, which selects the relevant default rule. + */ suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result + + /** Whether being mentioned through `@room` generates a notification. */ suspend fun isRoomMentionEnabled(): Result + + /** + * Sets whether being mentioned through `@room` generates a notification. + * + * @param enabled true to be notified of `@room` mentions. + */ suspend fun setRoomMentionEnabled(enabled: Boolean): Result + + /** Whether incoming calls generate a notification. */ suspend fun isCallEnabled(): Result + + /** + * Sets whether incoming calls generate a notification. + * + * @param enabled true to be notified of incoming calls. + */ suspend fun setCallEnabled(enabled: Boolean): Result + + /** Whether room invitations generate a notification. */ suspend fun isInviteForMeEnabled(): Result + + /** + * Sets whether room invitations generate a notification. + * + * @param enabled true to be notified of invitations. + */ suspend fun setInviteForMeEnabled(enabled: Boolean): Result + + /** The rooms that have an enabled rule of their own rather than following the defaults. */ suspend fun getRoomsWithUserDefinedRules(): Result> + + /** Whether the homeserver is able to push encrypted events to the device, which decides how much a notification can show before decryption. */ suspend fun canHomeServerPushEncryptedEventsToDevice(): Result + + /** The push rules as raw JSON, for debugging and bug reports; `null` when the server returned none. */ suspend fun getRawPushRules(): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/pusher/PushersService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/pusher/PushersService.kt index 6764598235..1eafe8ab49 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/pusher/PushersService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/pusher/PushersService.kt @@ -8,7 +8,21 @@ package io.element.android.libraries.matrix.api.pusher +/** + * Registers and removes the pusher of this session, i.e. the HTTP gateway the homeserver sends push notifications to. + */ interface PushersService { + /** + * Registers or updates the pusher; calling it again with the same push key replaces the existing registration. + * + * @param setHttpPusherData the gateway URL, the push key and the other details the homeserver needs to reach the device. + */ suspend fun setHttpPusher(setHttpPusherData: SetHttpPusherData): Result + + /** + * Removes the pusher, so the homeserver stops sending push notifications to this device. + * + * @param unsetHttpPusherData the push key and app id identifying the registration to remove. + */ suspend fun unsetHttpPusher(unsetHttpPusherData: UnsetHttpPusherData): Result } From 69e466a8e122081606224529e630314f05ed3401 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 09:27:01 +0200 Subject: [PATCH 160/338] Add KDoc to the remaining matrix API interfaces Document the permalink, widget, tracing, core, platform, server and analytics interfaces along with their members, which completes libraries/matrix/api: all 69 in-scope interfaces and their 398 members now carry documentation. Notable points recorded here: PermalinkParser reports an unrecognised uri as a fallback link rather than failing, MatrixWidgetDriver.run is a no-op while it is already running and send is silently ignored once closed, and every member of CallAnalyticCredentialsProvider is null when the service is unconfigured or the user has not consented to analytics. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/analytics/GetDatabaseSizesUseCase.kt | 8 ++++++++ .../libraries/matrix/api/core/ProgressCallback.kt | 9 +++++++++ .../libraries/matrix/api/core/SendHandle.kt | 4 ++++ .../matrix/api/permalink/MatrixToConverter.kt | 3 +++ .../matrix/api/permalink/PermalinkBuilder.kt | 14 ++++++++++++++ .../matrix/api/permalink/PermalinkParser.kt | 3 +++ .../matrix/api/server/UserServerResolver.kt | 4 ++++ .../matrix/api/tracing/TracingService.kt | 13 +++++++++++++ .../api/widget/CallAnalyticCredentialsProvider.kt | 14 ++++++++++++++ .../api/widget/CallWidgetSettingsProvider.kt | 11 +++++++++++ .../matrix/api/widget/MatrixWidgetDriver.kt | 15 +++++++++++++++ 11 files changed, 98 insertions(+) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/GetDatabaseSizesUseCase.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/GetDatabaseSizesUseCase.kt index fde02fb2c6..1ca9adfaca 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/GetDatabaseSizesUseCase.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/GetDatabaseSizesUseCase.kt @@ -9,6 +9,14 @@ package io.element.android.libraries.matrix.api.analytics import io.element.android.libraries.matrix.api.core.SessionId +/** + * Reads the on-disk size of a session's SDK stores, so they can be reported as analytics. + */ fun interface GetDatabaseSizesUseCase { + /** + * Returns the size of each store of the given session. + * + * @param sessionId the session whose stores are measured. + */ operator fun invoke(sessionId: SessionId): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/ProgressCallback.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/ProgressCallback.kt index a7abc0f797..5c8a75e384 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/ProgressCallback.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/ProgressCallback.kt @@ -8,6 +8,15 @@ package io.element.android.libraries.matrix.api.core +/** + * Reports the progress of a long running transfer, such as a media upload or download. + */ interface ProgressCallback { + /** + * Called as the transfer advances, possibly many times and from a background thread. + * + * @param current the number of bytes transferred so far. + * @param total the total number of bytes to transfer. + */ fun onProgress(current: Long, total: Long) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/SendHandle.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/SendHandle.kt index f3b8904fff..02c8a85d58 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/SendHandle.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/SendHandle.kt @@ -8,6 +8,10 @@ package io.element.android.libraries.matrix.api.core +/** + * A handle on an event sitting in a room's send queue, obtained from a timeline item that failed to send. + */ fun interface SendHandle { + /** Puts the event back in the send queue for another attempt. */ suspend fun retry(): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/MatrixToConverter.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/MatrixToConverter.kt index cc3e2b5cf6..d6a2e71b8f 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/MatrixToConverter.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/MatrixToConverter.kt @@ -20,6 +20,9 @@ interface MatrixToConverter { * - https://riot.im/develop/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org * - https://app.element.io/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org * - https://www.example.org/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org + * + * @param uri the uri to convert. + * @return the equivalent matrix.to uri, or `null` when the input is not a permalink this converter recognises. */ fun convert(uri: Uri): Uri? } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt index 878b0e7a69..9140c312c7 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt @@ -11,8 +11,22 @@ package io.element.android.libraries.matrix.api.permalink import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.UserId +/** + * Builds `matrix.to` permalinks, which are the shareable links to a user or a room; see [PermalinkParser] for the other direction. + */ interface PermalinkBuilder { + /** + * Builds a permalink to a user, failing with [PermalinkBuilderError.InvalidData] when the id is malformed. + * + * @param userId the user to link to. + */ fun permalinkForUser(userId: UserId): Result + + /** + * Builds a permalink to a room from its alias, failing with [PermalinkBuilderError.InvalidData] when the alias is malformed. + * + * @param roomAlias the room alias to link to. + */ fun permalinkForRoomAlias(roomAlias: RoomAlias): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt index 5365b8776a..467b147d98 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt @@ -18,7 +18,10 @@ package io.element.android.libraries.matrix.api.permalink interface PermalinkParser { /** * Turns a uri string to a [PermalinkData]. + * A uri that is not a Matrix permalink is reported as a fallback link rather than as an error, so this never throws. * https://github.com/matrix-org/matrix-doc/blob/master/proposals/1704-matrix.to-permalinks.md + * + * @param uriString the uri to parse. */ fun parse(uriString: String): PermalinkData } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/server/UserServerResolver.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/server/UserServerResolver.kt index 3529b17d85..6c3c16b35e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/server/UserServerResolver.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/server/UserServerResolver.kt @@ -8,6 +8,10 @@ package io.element.android.libraries.matrix.api.server +/** + * Resolves the server name of the logged in user, which is the part of their user id after the colon. + */ interface UserServerResolver { + /** Returns the server name, for instance `matrix.org` for `@alice:matrix.org`. */ fun resolve(): String } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt index d4ef27fd0d..f5de427ce0 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt @@ -10,8 +10,21 @@ package io.element.android.libraries.matrix.api.tracing import timber.log.Timber +/** + * Routes the app's own logs into the SDK's tracing subsystem, so that Kotlin and Rust logs end up interleaved in the same files. + */ interface TracingService { + /** + * Creates a Timber tree that forwards what the app logs to the SDK tracing subsystem. + * + * @param target the tracing target name the logs are attributed to, used to tell subsystems apart in the output. + */ fun createTimberTree(target: String): Timber.Tree + /** + * Turns writing logs to files on or off at runtime, which is how the user-facing logging toggle is applied. + * + * @param config where to write the log files, or the disabled configuration to stop writing them. + */ fun updateWriteToFilesConfiguration(config: WriteToFilesConfiguration) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallAnalyticCredentialsProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallAnalyticCredentialsProvider.kt index 5b2a691afc..1b45c4f1a0 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallAnalyticCredentialsProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallAnalyticCredentialsProvider.kt @@ -8,10 +8,24 @@ package io.element.android.libraries.matrix.api.widget +/** + * Supplies the analytics and crash reporting credentials passed to the embedded Element Call widget. + * + * Every member is `null` when the corresponding service is not configured for this build, or when the user has not consented to analytics. + */ interface CallAnalyticCredentialsProvider { + /** The PostHog identifier of the current user, so call analytics can be tied to the same person as app analytics. */ val posthogUserId: String? + + /** The PostHog host the widget should send its events to. */ val posthogApiHost: String? + + /** The PostHog API key the widget should use. */ val posthogApiKey: String? + + /** The URL the widget should submit its rageshakes to. */ val rageshakeSubmitUrl: String? + + /** The Sentry DSN the widget should report its crashes to. */ val sentryDsn: String? } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallWidgetSettingsProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallWidgetSettingsProvider.kt index d0686e8e5f..92510c4b8f 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallWidgetSettingsProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/CallWidgetSettingsProvider.kt @@ -10,7 +10,18 @@ package io.element.android.libraries.matrix.api.widget import java.util.UUID +/** + * Builds the widget settings used to embed Element Call in a room, applying the app's own preferences on top of the SDK defaults. + */ interface CallWidgetSettingsProvider { + /** + * @param baseUrl the URL Element Call is served from. + * @param widgetId a unique identifier for this widget instance. + * @param encrypted whether the room the call takes place in is encrypted. + * @param direct whether the call happens in a direct message room. + * @param isAudioCall whether the call should start without video. + * @param hasActiveCall whether a call is already ongoing in the room, which affects whether the user joins straight away. + */ suspend fun provide( baseUrl: String, widgetId: String = UUID.randomUUID().toString(), diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/MatrixWidgetDriver.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/MatrixWidgetDriver.kt index d8cca95842..3b863279cf 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/MatrixWidgetDriver.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/widget/MatrixWidgetDriver.kt @@ -10,10 +10,25 @@ package io.element.android.libraries.matrix.api.widget import kotlinx.coroutines.flow.Flow +/** + * Bridges a widget running in a WebView with the Matrix room it is embedded in, relaying the widget API messages in both directions. + * + * The driver owns SDK resources and must be closed once the widget goes away; Element Call is the main user of this. + */ interface MatrixWidgetDriver : AutoCloseable { + /** The id of the widget this driver serves, matching the one in its settings. */ val id: String + + /** The messages coming from the room that should be forwarded to the widget. */ val incomingMessages: Flow + /** Starts relaying messages and suspends for as long as the driver runs; calling it again while it is running does nothing. */ suspend fun run() + + /** + * Forwards a message from the widget to the room. Does nothing once the driver has been closed. + * + * @param message the raw widget API message emitted by the WebView. + */ suspend fun send(message: String) } From f01abdc00d4435a29889adf9e8b234e397faa3dd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 09:33:16 +0200 Subject: [PATCH 161/338] Add KDoc to the `libraries/push` API interfaces Document PushService, GetCurrentPushProvider, NotificationCleaner and NotificationBitmapLoader along with their members, and add the missing @param entries across the notification interfaces. The NotificationCleaner docs record the behaviour shared by all its methods, which is not visible in the signatures: each one also removes the grouping summary notification once it has no children left. Co-Authored-By: Claude Opus 5 (1M context) --- .../push/api/GetCurrentPushProvider.kt | 10 +++++ .../android/libraries/push/api/PushService.kt | 31 +++++++++++++ .../notifications/NotificationBitmapLoader.kt | 3 ++ .../api/notifications/NotificationCleaner.kt | 44 +++++++++++++++++++ .../notifications/NotificationSoundUpdater.kt | 12 +++++ .../OnMissedCallNotificationHandler.kt | 4 ++ .../NotificationConversationService.kt | 12 +++++ .../sound/NotificationSoundCopier.kt | 6 +++ 8 files changed, 122 insertions(+) diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/GetCurrentPushProvider.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/GetCurrentPushProvider.kt index a6fbb47173..eb2ce09545 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/GetCurrentPushProvider.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/GetCurrentPushProvider.kt @@ -10,6 +10,16 @@ package io.element.android.libraries.push.api import io.element.android.libraries.matrix.api.core.SessionId +/** + * Reads the name of the push provider a session is currently registered with. + * + * This is the low-level accessor used where depending on the whole push feature would be too much; prefer [PushService.getCurrentPushProvider] otherwise. + */ interface GetCurrentPushProvider { + /** + * Returns the stored provider name, or `null` when the session has no push provider selected yet. + * + * @param sessionId the session to read the provider of. + */ suspend fun getCurrentPushProvider(sessionId: SessionId): String? } diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PushService.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PushService.kt index a43ee36403..4a5037d17f 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PushService.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PushService.kt @@ -16,9 +16,14 @@ import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.PushProvider import kotlinx.coroutines.flow.Flow +/** + * Entry point for push notification setup: choosing a push provider, registering a pusher with the homeserver, and inspecting the push history. + */ interface PushService { /** * Return the current push provider, or null if none. + * + * @param sessionId the session to read the provider of. */ suspend fun getCurrentPushProvider(sessionId: SessionId): PushProvider? @@ -31,6 +36,10 @@ interface PushService { * Will unregister any previous pusher and register a new one with the provided [PushProvider]. * * The method has effect only if the [PushProvider] is different than the current one. + * + * @param matrixClient the session to register the pusher for. + * @param pushProvider the provider to switch to. + * @param distributor the distributor of that provider to use, which matters for providers such as UnifiedPush. */ suspend fun registerWith( matrixClient: MatrixClient, @@ -42,6 +51,8 @@ interface PushService { * Ensure that the pusher with the current push provider and distributor is registered. * If there is no current config, the default push provider with the default distributor will be used. * Error can be [PusherRegistrationFailure]. + * + * @param matrixClient the session to ensure the pusher of. */ suspend fun ensurePusherIsRegistered( matrixClient: MatrixClient, @@ -50,17 +61,35 @@ interface PushService { /** * Store the given push provider as the current one, but do not register. * To be used when there is no distributor available. + * + * @param sessionId the session to store the provider for. + * @param pushProvider the provider to remember. */ suspend fun selectPushProvider( sessionId: SessionId, pushProvider: PushProvider, ) + /** + * Whether the user has asked not to be warned about pusher registration failures for this session. + * + * @param sessionId the session to read the preference of. + */ fun ignoreRegistrationError(sessionId: SessionId): Flow + + /** + * Remembers whether pusher registration failures should be reported to the user for this session. + * + * @param sessionId the session to store the preference for. + * @param ignore true to stop warning the user about registration failures. + */ suspend fun setIgnoreRegistrationError(sessionId: SessionId, ignore: Boolean) /** + * Asks the homeserver to send a test push, so the user can check that the whole chain works. * Return false in case of early error. + * + * @param sessionId the session to test the push setup of. */ suspend fun testPush(sessionId: SessionId): Boolean @@ -86,6 +115,8 @@ interface PushService { /** * Notify the user that the service is un-registered. + * + * @param userId the user whose push service is no longer registered. */ suspend fun onServiceUnregistered(userId: UserId) } diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationBitmapLoader.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationBitmapLoader.kt index bded69991b..9dd53ade09 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationBitmapLoader.kt @@ -14,6 +14,9 @@ import coil3.ImageLoader import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.ui.media.AVATAR_THUMBNAIL_SIZE_IN_PIXEL +/** + * Loads the avatars used to decorate notifications, since the notification APIs need bitmaps rather than Compose images. + */ interface NotificationBitmapLoader { /** * Get icon of a room. diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationCleaner.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationCleaner.kt index 0a4e35c697..95966f8eb1 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationCleaner.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationCleaner.kt @@ -13,12 +13,56 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId +/** + * Dismisses notifications that are already displayed, typically because the user has now seen the events elsewhere. + * + * Every method also removes the grouping summary notification when it is left without children. + */ interface NotificationCleaner { + /** + * Dismisses every message notification of a session. + * + * @param sessionId the session whose notifications are dismissed. + */ fun clearAllMessagesEvents(sessionId: SessionId) + + /** + * Dismisses the message notifications of one room. + * + * @param sessionId the session the room belongs to. + * @param roomId the room whose notifications are dismissed. + */ fun clearMessagesForRoom(sessionId: SessionId, roomId: RoomId) + + /** + * Dismisses the message notifications of one thread, leaving the rest of the room's notifications in place. + * + * @param sessionId the session the room belongs to. + * @param roomId the room the thread belongs to. + * @param threadId the thread whose notifications are dismissed. + */ fun clearMessagesForThread(sessionId: SessionId, roomId: RoomId, threadId: ThreadId) + + /** + * Dismisses the notification of a single event. + * + * @param sessionId the session the event belongs to. + * @param eventId the event whose notification is dismissed. + */ fun clearEvent(sessionId: SessionId, eventId: EventId) + /** + * Dismisses every membership notification of a session, i.e. the invitations and the join requests. + * + * @param sessionId the session whose notifications are dismissed. + */ fun clearMembershipNotificationForSession(sessionId: SessionId) + + /** + * Dismisses the membership notifications of one room. + * + * @param sessionId the session the room belongs to. + * @param roomId the room whose notifications are dismissed. + */ fun clearMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId) } diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationSoundUpdater.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationSoundUpdater.kt index b23c4122ad..91f50abc07 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationSoundUpdater.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationSoundUpdater.kt @@ -14,8 +14,20 @@ import io.element.android.libraries.preferences.api.store.NotificationSound * versioned channel because Android forbids mutating sound after creation. */ interface NotificationSoundUpdater { + /** + * Recreates the message notification channel so it uses [sound]. + * + * @param sound the sound the new channel should play. + * @param version bumped on every change, since a new channel id is the only way to change a channel's sound. + */ fun recreateNoisyChannel(sound: NotificationSound, version: Int) + /** + * Recreates the ringing call notification channel so it uses [sound]; see [recreateNoisyChannel] for why a version is needed. + * + * @param sound the sound the new channel should play. + * @param version bumped on every change to force a new channel id. + */ fun recreateRingingCallChannel(sound: NotificationSound, version: Int) /** Current channel sound classified into [NotificationSound]. Null when the channel doesn't exist. */ diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/OnMissedCallNotificationHandler.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/OnMissedCallNotificationHandler.kt index 2ba61925c5..d4e051ebf2 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/OnMissedCallNotificationHandler.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/OnMissedCallNotificationHandler.kt @@ -18,6 +18,10 @@ import io.element.android.libraries.matrix.api.core.SessionId interface OnMissedCallNotificationHandler { /** * Adds a missed call notification. + * + * @param sessionId the session the call was received on. + * @param roomId the room the call took place in. + * @param eventId the call notification event that went unanswered. */ suspend fun addMissedCallNotification( sessionId: SessionId, diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/conversations/NotificationConversationService.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/conversations/NotificationConversationService.kt index 4ea80e4ce8..b3bffc0147 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/conversations/NotificationConversationService.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/conversations/NotificationConversationService.kt @@ -18,6 +18,12 @@ interface NotificationConversationService { /** * Called when a new message is received in a room. * It should create a new conversation shortcut for this room. + * + * @param sessionId the session the message belongs to. + * @param roomId the room the message was received in. + * @param roomName the name to show on the shortcut, or `null` when the room has none. + * @param roomIsDirect whether the room is a direct message, which changes how the shortcut is presented. + * @param roomAvatarUrl the avatar to show on the shortcut, or `null` when the room has none. */ suspend fun onSendMessage( sessionId: SessionId, @@ -30,12 +36,18 @@ interface NotificationConversationService { /** * Called when a room is left. * It should remove the conversation shortcut for this room. + * + * @param sessionId the session the room belongs to. + * @param roomId the room that was left. */ suspend fun onLeftRoom(sessionId: SessionId, roomId: RoomId) /** * Called when the list of available rooms changes. * It should update the conversation shortcuts accordingly, removing shortcuts for no longer joined rooms. + * + * @param sessionId the session the rooms belong to. + * @param roomIds the rooms the user is currently joined to. */ suspend fun onAvailableRoomsChanged(sessionId: SessionId, roomIds: Set) } diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/sound/NotificationSoundCopier.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/sound/NotificationSoundCopier.kt index 2268198353..ad3d73656f 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/sound/NotificationSoundCopier.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/sound/NotificationSoundCopier.kt @@ -12,6 +12,12 @@ package io.element.android.libraries.push.api.notifications.sound * FileProvider, so the persisted reference outlives the original source. */ interface NotificationSoundCopier { + /** + * Copies the picked sound into app-private storage, replacing any file previously stored for [slot]. + * + * @param sourceUriString the uri of the sound the user picked. + * @param slot which of the two sounds is being set, since each keeps its own copy. + */ suspend fun copyToAppFiles(sourceUriString: String, slot: SoundSlot): CopyResult /** From 82ff94672b071068ead8c0c7b7dfa8fb5b635612 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 09:35:57 +0200 Subject: [PATCH 162/338] Add KDoc to the `libraries/preferences` API interfaces Document AppPreferencesStore, SessionPreferencesStore, their two factories, and each of their 44 members. For a preference store the useful thing to document is the fallback value, so each getter states the default it uses when the preference has never been set, taken from DefaultAppPreferencesStore and DefaultSessionPreferencesStore: developer mode follows the build type, live location updates default to 10 metres, the log packs to an empty set, and the session toggles are all on by default except session verification skipping. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/store/AppPreferencesStore.kt | 66 ++++++++++++++++++- .../api/store/PreferenceDataStoreFactory.kt | 5 ++ .../api/store/SessionPreferencesStore.kt | 47 +++++++++++++ .../store/SessionPreferencesStoreFactory.kt | 15 +++++ 4 files changed, 132 insertions(+), 1 deletion(-) diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt index b1877b3ae5..f35fa88442 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt @@ -13,48 +13,111 @@ import io.element.android.libraries.matrix.api.tracing.LogLevel import io.element.android.libraries.matrix.api.tracing.TraceLogPack import kotlinx.coroutines.flow.Flow +/** + * Local, app-wide user preferences that are not tied to a session, stored on the device only. + * + * Each getter returns a flow that emits the current value straight away and then again on every change. + * The default each preference falls back to when it has never been set is given on the getter. + */ interface AppPreferencesStore { + /** + * @param enabled true to expose the developer options in the settings. + */ suspend fun setDeveloperModeEnabled(enabled: Boolean) + + /** Whether developer mode is on; defaults to `true` on debug builds and `false` otherwise. */ fun isDeveloperModeEnabledFlow(): Flow + /** + * @param string the Element Call deployment to use, or `null` to go back to the one from the homeserver. + */ suspend fun setCustomElementCallBaseUrl(string: String?) + + /** The Element Call URL the user has overridden, or `null` when the homeserver's own value should be used. */ fun getCustomElementCallBaseUrlFlow(): Flow + /** + * @param theme the name of the theme to apply: light, dark, or follow the system. + */ suspend fun setTheme(theme: String) + + /** The name of the chosen theme, or `null` when the user has not picked one and the system setting should be followed. */ fun getThemeFlow(): Flow + /** + * @param value the distance in metres the user must move before a new live location is published. + */ suspend fun setLiveLocationMinimumDistanceInMetersUpdate(value: Int) + + /** The minimum distance in metres between two live location updates; defaults to 10. */ fun getLiveLocationMinimumDistanceInMetersUpdateFlow(): Flow + /** + * Only used to clear the local value once it has been migrated to the server. + * + * @param hide the local setting, or `null` to erase it. + */ @Deprecated("Use MediaPreviewService instead. Kept only for migration.") suspend fun setHideInviteAvatars(hide: Boolean?) + + /** The local setting to migrate to the server, or `null` once there is nothing left to migrate. */ @Deprecated("Use MediaPreviewService instead. Kept only for migration.") fun getHideInviteAvatarsFlow(): Flow + + /** + * Only used to clear the local value once it has been migrated to the server. + * + * @param mediaPreviewValue the local setting, or `null` to erase it. + */ @Deprecated("Use MediaPreviewService instead. Kept only for migration.") suspend fun setTimelineMediaPreviewValue(mediaPreviewValue: MediaPreviewValue?) + + /** The local setting to migrate to the server, or `null` once there is nothing left to migrate. */ @Deprecated("Use MediaPreviewService instead. Kept only for migration.") fun getTimelineMediaPreviewValueFlow(): Flow + /** + * @param logLevel how verbose the SDK and app logs should be. + */ suspend fun setTracingLogLevel(logLevel: LogLevel) + + /** The configured log level; defaults to the one the build was compiled with. */ fun getTracingLogLevelFlow(): Flow + /** + * @param targets the extra log packs to enable on top of the default targets. + */ suspend fun setTracingLogPacks(targets: Set) + + /** The extra log packs enabled by the user; defaults to an empty set. */ fun getTracingLogPacksFlow(): Flow> + /** The sound played for message notifications. */ fun getMessageSoundFlow(): Flow /** * Atomically persists [sound] (with copy-time [title] for Custom; cleared otherwise) and * bumps the channel version. Single transaction so process death can't desync URI and version. + * + * @param sound the sound to persist. + * @param title the display name captured when the sound was copied, or `null` for the built-in choices. + * @return the new channel version, to be passed to the channel recreation. */ suspend fun setMessageSoundAndIncrementVersion(sound: NotificationSound, title: String?): Int /** Title captured at copy time. Null for SystemDefault / Silent or pre-title persisted data. */ fun getMessageSoundDisplayNameFlow(): Flow + /** The sound played for incoming call notifications. */ fun getCallRingtoneFlow(): Flow - /** See [setMessageSoundAndIncrementVersion]. */ + /** + * See [setMessageSoundAndIncrementVersion]. + * + * @param sound the ringtone to persist. + * @param title the display name captured when the sound was copied, or `null` for the built-in choices. + * @return the new channel version, to be passed to the channel recreation. + */ suspend fun setCallRingtoneAndIncrementVersion(sound: NotificationSound, title: String?): Int /** See [getMessageSoundDisplayNameFlow]. */ @@ -63,5 +126,6 @@ interface AppPreferencesStore { /** Single-snapshot read of all sound prefs; used at boot to seed channels without N reads. */ suspend fun getNotificationSoundChannelConfig(): NotificationSoundChannelConfig + /** Erases every app preference, so they all fall back to their defaults. */ suspend fun reset() } diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/PreferenceDataStoreFactory.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/PreferenceDataStoreFactory.kt index 50f6eb2431..a3d609e205 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/PreferenceDataStoreFactory.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/PreferenceDataStoreFactory.kt @@ -17,5 +17,10 @@ import androidx.datastore.preferences.core.Preferences * It's a wrapper around AndroidX's `PreferenceDataStoreFactory` to make testing easier. */ interface PreferenceDataStoreFactory { + /** + * Creates or opens the preference store with the given name. + * + * @param name the store name, which also determines the file it is persisted to. + */ fun create(name: String): DataStore } diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt index d3f3fb4e28..b554fff460 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt @@ -10,30 +10,77 @@ package io.element.android.libraries.preferences.api.store import kotlinx.coroutines.flow.Flow +/** + * Local, per-session user preferences, stored on the device only and never synced to the homeserver. + * + * Each getter returns a flow that emits the current value straight away and then again on every change. + * The default each preference falls back to when it has never been set is given on the getter. + */ interface SessionPreferencesStore { + /** + * @param enabled true to let the homeserver share this user's presence with others. + */ suspend fun setSharePresence(enabled: Boolean) + + /** Whether presence is shared; defaults to `true`. */ fun isSharePresenceEnabled(): Flow + /** + * @param enabled true to send read receipts that other members can see. + */ suspend fun setSendPublicReadReceipts(enabled: Boolean) + + /** Whether read receipts are sent publicly rather than privately; defaults to `true`. */ fun isSendPublicReadReceiptsEnabled(): Flow + /** + * @param enabled true to display the read receipts of other members in the timeline. + */ suspend fun setRenderReadReceipts(enabled: Boolean) + + /** Whether other members' read receipts are displayed; defaults to `true`. */ fun isRenderReadReceiptsEnabled(): Flow + /** + * @param enabled true to tell other members when this user is typing. + */ suspend fun setSendTypingNotifications(enabled: Boolean) + + /** Whether typing notifications are sent; defaults to `true`. */ fun isSendTypingNotificationsEnabled(): Flow + /** + * @param enabled true to display which other members are typing. + */ suspend fun setRenderTypingNotifications(enabled: Boolean) + + /** Whether other members' typing notifications are displayed; defaults to `true`. */ fun isRenderTypingNotificationsEnabled(): Flow + /** + * @param skip true to remember that the user dismissed the session verification prompt. + */ suspend fun setSkipSessionVerification(skip: Boolean) + + /** Whether the user chose to skip verifying this session, so they are not asked again; defaults to `false`. */ fun isSessionVerificationSkipped(): Flow + /** + * @param compress true to downscale images before uploading them. + */ suspend fun setOptimizeImages(compress: Boolean) + + /** Whether images are downscaled before upload; defaults to `true`. */ fun doesOptimizeImages(): Flow + /** + * @param preset the quality and size trade-off to apply when compressing videos. + */ suspend fun setVideoCompressionPreset(preset: VideoCompressionPreset) + + /** The video compression preset; defaults to [VideoCompressionPreset.STANDARD], including when the stored value is unreadable. */ fun getVideoCompressionPreset(): Flow + /** Erases every preference of this session, so they all fall back to their defaults. */ suspend fun clear() } diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStoreFactory.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStoreFactory.kt index 2c0038d06e..05b4845026 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStoreFactory.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStoreFactory.kt @@ -11,7 +11,22 @@ package io.element.android.libraries.preferences.api.store import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.coroutines.CoroutineScope +/** + * Caches one [SessionPreferencesStore] per session, so that every caller of a given session shares the same instance. + */ interface SessionPreferencesStoreFactory { + /** + * Returns the store of a session, creating it on first use. + * + * @param sessionId the session whose store is requested. + * @param sessionCoroutineScope the scope the store uses for its own work, only honoured when the store is actually created. + */ fun get(sessionId: SessionId, sessionCoroutineScope: CoroutineScope): SessionPreferencesStore + + /** + * Drops the cached store of a session, to be called when that session goes away. + * + * @param sessionId the session whose store is dropped. + */ fun remove(sessionId: SessionId) } From 7462c5f366155e54c46fdc475eb9ab4f76c71abb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 09:38:36 +0200 Subject: [PATCH 163/338] Add KDoc to the `services/analytics` API interfaces Document AnalyticsService and ScreenTracker at the interface level, document the members of the cold start and room list watchers, and add the missing @param entries across the analytics interfaces. The AnalyticsService doc states the rule that applies to the whole interface: nothing reaches the providers until the user has given their consent. Co-Authored-By: Claude Opus 5 (1M context) --- .../analytics/api/AnalyticsSdkManager.kt | 7 +++++ .../analytics/api/AnalyticsService.kt | 29 ++++++++++++++++++- .../services/analytics/api/ScreenTracker.kt | 8 +++++ .../api/watchers/AnalyticsColdStartWatcher.kt | 5 ++++ .../watchers/AnalyticsRoomListStateWatcher.kt | 3 ++ 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsSdkManager.kt b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsSdkManager.kt index bf5e04509c..d83690a644 100644 --- a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsSdkManager.kt +++ b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsSdkManager.kt @@ -13,16 +13,23 @@ package io.element.android.services.analytics.api interface AnalyticsSdkManager { /** * Enable or disable SDK analytics. + * + * @param enabled true to let the SDK report analytics, normally following the user consent. */ fun enableSdkAnalytics(enabled: Boolean) /** * Start a new span with the given [name], using [parentTraceId] to optionally attach it to a parent transaction. + * + * @param name the name of the span. + * @param parentTraceId the trace to attach the span to, or `null` to start a root span. */ fun startSpan(name: String, parentTraceId: String? = null): AnalyticsSdkSpan /** * Create a 'bridge' span optionally linking it to a parent trace via [parentTraceId]. + * + * @param parentTraceId the trace to link the bridge to, or `null` to leave it unattached. */ fun bridge(parentTraceId: String? = null): AnalyticsSdkSpan } diff --git a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt index 846d5ce5b1..9f29f43b77 100644 --- a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt +++ b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt @@ -15,6 +15,11 @@ import io.element.android.services.analyticsproviders.api.trackers.AnalyticsTrac import io.element.android.services.analyticsproviders.api.trackers.ErrorTracker import kotlinx.coroutines.flow.Flow +/** + * Central analytics entry point: user consent, the analytics identity, and performance transactions. + * + * Nothing is sent to the providers until the user has given their consent through [setUserConsent]. + */ interface AnalyticsService : AnalyticsTracker, ErrorTracker { /** * Get the available analytics providers. @@ -28,6 +33,8 @@ interface AnalyticsService : AnalyticsTracker, ErrorTracker { /** * Update the user consent value. + * + * @param userConsent true when the user agrees to analytics being collected. */ suspend fun setUserConsent(userConsent: Boolean) @@ -48,16 +55,26 @@ interface AnalyticsService : AnalyticsTracker, ErrorTracker { /** * Update analyticsId from the AccountData. + * + * @param analyticsId the identifier shared across the user's clients, so their events are attributed to one person. */ suspend fun setAnalyticsId(analyticsId: String) /** * Starts a transaction to measure the performance of an operation. + * The caller is responsible for finishing it; see `recordTransaction` for a scoped alternative. + * + * @param name the name the transaction is reported under. + * @param operation the kind of operation being measured. + * @param description a human readable detail shown alongside the measurement. */ fun startTransaction(name: String, operation: String? = null, description: String? = null): AnalyticsTransaction /** * Starts an [AnalyticsLongRunningTransaction], that can be shared with other components. + * + * @param longRunningTransaction which of the known long running operations is starting. + * @param parentTransaction the transaction to attach this one to, or `null` to start a root transaction. */ fun startLongRunningTransaction( longRunningTransaction: AnalyticsLongRunningTransaction, @@ -66,15 +83,25 @@ interface AnalyticsService : AnalyticsTracker, ErrorTracker { /** * Gets an ongoing [AnalyticsLongRunningTransaction], if it exists. + * + * @param longRunningTransaction the long running operation to look up. */ fun getLongRunningTransaction(longRunningTransaction: AnalyticsLongRunningTransaction): AnalyticsTransaction? /** * Removes an ongoing [AnalyticsLongRunningTransaction] so it's no longer shared. + * + * @param longRunningTransaction the long running operation to stop sharing. + * @return the transaction that was removed, or `null` when none was ongoing. */ fun removeLongRunningTransaction(longRunningTransaction: AnalyticsLongRunningTransaction): AnalyticsTransaction? - /** Enter a span inside the Rust SDK tracing system. If a [parentTraceId] is provided, the SDK trace will be added as a child of that trace. */ + /** + * Enter a span inside the Rust SDK tracing system. If a [parentTraceId] is provided, the SDK trace will be added as a child of that trace. + * + * @param name the name of the span. + * @param parentTraceId the trace to attach the span to, or `null` to start a root span. + */ @Discouraged("This method can cause crashes of the app when using debug builds of the Rust SDK.") fun enterSdkSpan(name: String?, parentTraceId: String?): AnalyticsSdkSpan } diff --git a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/ScreenTracker.kt b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/ScreenTracker.kt index 29513f66ff..2a93b2f7aa 100644 --- a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/ScreenTracker.kt +++ b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/ScreenTracker.kt @@ -11,7 +11,15 @@ package io.element.android.services.analytics.api import androidx.compose.runtime.Composable import im.vector.app.features.analytics.plan.MobileScreen +/** + * Reports screen views to analytics from Composable code. + */ interface ScreenTracker { + /** + * Records a view of [screen] when this enters composition, so it is called once per appearance rather than on every recomposition. + * + * @param screen the screen being shown. + */ @Composable fun TrackScreen( screen: MobileScreen.ScreenName, diff --git a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/watchers/AnalyticsColdStartWatcher.kt b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/watchers/AnalyticsColdStartWatcher.kt index 67d3c5498b..905b0dc88f 100644 --- a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/watchers/AnalyticsColdStartWatcher.kt +++ b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/watchers/AnalyticsColdStartWatcher.kt @@ -12,7 +12,12 @@ package io.element.android.services.analytics.api.watchers * until the cached room list is displayed. This check only takes place in a cold app start after the user is authenticated. */ interface AnalyticsColdStartWatcher { + /** Begins the measurement, to be called as early as possible in the cold start. */ fun start() + + /** Restarts the measurement from the login flow, since a user who has to sign in first is not a normal cold start. */ fun whenLoggingIn() + + /** Ends the measurement, to be called once the cached room list is actually on screen. */ fun onRoomListVisible() } diff --git a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/watchers/AnalyticsRoomListStateWatcher.kt b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/watchers/AnalyticsRoomListStateWatcher.kt index 2f6d4723a3..85cb210d8c 100644 --- a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/watchers/AnalyticsRoomListStateWatcher.kt +++ b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/watchers/AnalyticsRoomListStateWatcher.kt @@ -12,6 +12,9 @@ package io.element.android.services.analytics.api.watchers * the app was previously running and we just returned to it. */ interface AnalyticsRoomListStateWatcher { + /** Begins watching the room list state, to be called when the app returns to the foreground. */ fun start() + + /** Stops watching, to be called when the app leaves the foreground. */ fun stop() } From 414d2b5dd08679d87333137bab9c207be023d3c5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 09:39:36 +0200 Subject: [PATCH 164/338] Add KDoc to the `libraries/emoji` API interfaces Document AddRecentEmoji, GetRecentEmojis.invoke, the EmojiPickerRenderer interface and the EmojiPickerPresenter factory, pointing at the no-op renderer and the empty recent-emoji source so callers know how to opt out of each. EmojiPickerPresenter itself is a Presenter and therefore out of scope. Co-Authored-By: Claude Opus 5 (1M context) --- .../libraries/emoji/api/picker/EmojiPickerPresenter.kt | 7 +++++++ .../libraries/emoji/api/picker/EmojiPickerRenderer.kt | 7 +++++++ .../libraries/emoji/api/recentemojis/AddRecentEmoji.kt | 6 ++++++ .../libraries/emoji/api/recentemojis/GetRecentEmojis.kt | 1 + 4 files changed, 21 insertions(+) diff --git a/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/picker/EmojiPickerPresenter.kt b/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/picker/EmojiPickerPresenter.kt index a20b53a480..ac83b49533 100644 --- a/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/picker/EmojiPickerPresenter.kt +++ b/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/picker/EmojiPickerPresenter.kt @@ -11,7 +11,14 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.emoji.api.recentemojis.GetRecentEmojis fun interface EmojiPickerPresenter : Presenter { + /** + * Creates a presenter for one picker instance. + */ fun interface Factory { + /** + * @param getRecentEmojis how the picker sources its recent tab; pass [io.element.android.libraries.emoji.api.recentemojis.EmptyGetRecentEmojis] + * for a picker that should have no recent tab at all. + */ fun create(getRecentEmojis: GetRecentEmojis): EmojiPickerPresenter } } diff --git a/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/picker/EmojiPickerRenderer.kt b/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/picker/EmojiPickerRenderer.kt index b3784b3c42..b273f41dae 100644 --- a/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/picker/EmojiPickerRenderer.kt +++ b/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/picker/EmojiPickerRenderer.kt @@ -13,9 +13,16 @@ import androidx.compose.ui.Modifier import io.element.android.emojibasebindings.Emoji import kotlinx.collections.immutable.ImmutableSet +/** + * Renders the emoji picker, so that callers can embed it without depending on its implementation. + * + * See [NoOpEmojiPickerRenderer] for a renderer that draws nothing, used where the picker is disabled. + */ @Immutable interface EmojiPickerRenderer { /** + * Draws the picker. + * * @param state opaque state produced by [EmojiPickerPresenter.present]. * @param onSelectEmoji invoked when the user taps an emoji (including a skin-tone variant). * @param selectedEmojis emojis to visually mark as already selected. diff --git a/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/recentemojis/AddRecentEmoji.kt b/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/recentemojis/AddRecentEmoji.kt index 5865568511..af2a1dfd7d 100644 --- a/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/recentemojis/AddRecentEmoji.kt +++ b/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/recentemojis/AddRecentEmoji.kt @@ -8,6 +8,12 @@ package io.element.android.libraries.emoji.api.recentemojis +/** + * Records that an emoji was just used, so it surfaces in the picker's recent list; see [GetRecentEmojis] for the read side. + */ fun interface AddRecentEmoji { + /** + * @param emoji the emoji that was just used, as its unicode representation. + */ suspend operator fun invoke(emoji: String): Result } diff --git a/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/recentemojis/GetRecentEmojis.kt b/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/recentemojis/GetRecentEmojis.kt index cf6015d799..276b0e7eba 100644 --- a/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/recentemojis/GetRecentEmojis.kt +++ b/libraries/emoji/api/src/main/kotlin/io/element/android/libraries/emoji/api/recentemojis/GetRecentEmojis.kt @@ -15,6 +15,7 @@ import kotlinx.collections.immutable.persistentListOf * Returns the list of recently used emojis for reactions. */ fun interface GetRecentEmojis { + /** Returns the recently used emojis, most recent first; see [EmptyGetRecentEmojis] for a picker with no recent tab. */ suspend operator fun invoke(): Result> } From 23a346907cde6b3a7bd9d8da0ac1bc4c22ddc329 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 09:44:42 +0200 Subject: [PATCH 165/338] Add KDoc to the `libraries/mediaupload` API interfaces Document MediaSender and all its members, its two factories, MediaPreProcessor, MaxUploadSizeProvider and MediaOptimizationConfigProvider. The MediaSender doc explains the two-step shape of the API, which the individual signatures do not convey: pre-processing and sending are separate so a preview can be shown in between, and the temporary files that pre-processing creates are only released by cleanUp, whether or not anything was ultimately sent. Co-Authored-By: Claude Opus 5 (1M context) --- .../mediaupload/api/MaxUploadSizeProvider.kt | 1 + .../api/MediaOptimizationConfigProvider.kt | 4 ++ .../mediaupload/api/MediaPreProcessor.kt | 8 +++ .../libraries/mediaupload/api/MediaSender.kt | 58 +++++++++++++++++++ 4 files changed, 71 insertions(+) diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MaxUploadSizeProvider.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MaxUploadSizeProvider.kt index a5462a6caf..e6b9c7f026 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MaxUploadSizeProvider.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MaxUploadSizeProvider.kt @@ -12,5 +12,6 @@ package io.element.android.libraries.mediaupload.api * Provides the maximum upload size allowed by the Matrix server. */ fun interface MaxUploadSizeProvider { + /** Returns the maximum size in bytes a single upload may have, as advertised by the homeserver. */ suspend fun getMaxUploadSize(): Result } diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaOptimizationConfigProvider.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaOptimizationConfigProvider.kt index 976a7057f2..60dbf353eb 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaOptimizationConfigProvider.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaOptimizationConfigProvider.kt @@ -8,6 +8,10 @@ package io.element.android.libraries.mediaupload.api +/** + * Resolves how aggressively media should be compressed, combining the user's own preferences with the app defaults. + */ fun interface MediaOptimizationConfigProvider { + /** Returns the configuration to pass to the pre-processing and sending calls. */ suspend fun get(): MediaOptimizationConfig } diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt index c2bf1e5f5d..a51ebc17b9 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaPreProcessor.kt @@ -10,10 +10,18 @@ package io.element.android.libraries.mediaupload.api import android.net.Uri +/** + * Prepares a media file for upload: resizing, transcoding and stripping metadata that would leak information about the sender. + */ interface MediaPreProcessor { /** * Given a [uri] and [mimeType], pre-processes the media before it's uploaded, resizing, transcoding, and removing sensitive info from its metadata. * If [deleteOriginal] is `true`, the file reference by the [uri] will be automatically deleted too when this process finishes. + * + * @param uri the media to process. + * @param mimeType the MIME type of that media. + * @param deleteOriginal whether to delete the source file once processing is done. + * @param mediaOptimizationConfig how aggressively the media should be compressed. * @return a [Result] with the [MediaUploadInfo] containing all the info needed to begin the upload. */ suspend fun process( diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt index 6cca73935e..808575f9e6 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt @@ -13,31 +13,62 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.timeline.Timeline +/** + * Creates a [MediaSender] for the room of the current room scope; see [MediaSenderRoomFactory] when the room is not in scope. + */ fun interface MediaSenderFactory { /** * Create a [MediaSender] for the given [Timeline.Mode], in the Room Scope. + * + * @param timelineMode the timeline the media should be sent to, which matters for threads in particular. */ fun create( timelineMode: Timeline.Mode, ): MediaSender } +/** + * Creates a [MediaSender] for an explicitly given room, used outside the room scope, for instance when sharing into a room. + */ fun interface MediaSenderRoomFactory { /** * Create a [MediaSender] for the given [JoinedRoom], with timeline mode Live. + * + * @param room the room the media should be sent to. */ fun create( room: JoinedRoom, ): MediaSender } +/** + * Sends media to a room, taking care of the pre-processing the timeline API does not do: resizing, transcoding and stripping metadata. + * + * Sending is a two-step process, and the steps can be used separately so the UI can show a preview between them. + * The temporary files produced by pre-processing are only released by [cleanUp]. + */ interface MediaSender { + /** + * Resizes, transcodes and strips the metadata of a media file without sending anything yet. + * + * @param uri the media picked by the user. + * @param mimeType the MIME type of that media. + * @param mediaOptimizationConfig how aggressively the media should be compressed. + */ suspend fun preProcessMedia( uri: Uri, mimeType: String, mediaOptimizationConfig: MediaOptimizationConfig, ): Result + /** + * Sends media that has already gone through [preProcessMedia]. + * + * @param mediaUploadInfo the result of the pre-processing step. + * @param caption the text shown along with the media, or `null` for none. + * @param formattedCaption the caption as HTML, or `null` to send it unformatted. + * @param inReplyToEventId the event this message replies to, or `null` if it is not a reply. + */ suspend fun sendPreProcessedMedia( mediaUploadInfo: MediaUploadInfo, caption: String?, @@ -45,6 +76,16 @@ interface MediaSender { inReplyToEventId: EventId?, ): Result + /** + * Pre-processes and sends a media file in one go, suspending until the upload finishes. + * + * @param uri the media picked by the user. + * @param mimeType the MIME type of that media. + * @param caption the text shown along with the media, or `null` for none. + * @param formattedCaption the caption as HTML, or `null` to send it unformatted. + * @param inReplyToEventId the event this message replies to, or `null` if it is not a reply. + * @param mediaOptimizationConfig how aggressively the media should be compressed. + */ suspend fun sendMedia( uri: Uri, mimeType: String, @@ -54,6 +95,14 @@ interface MediaSender { mediaOptimizationConfig: MediaOptimizationConfig, ): Result + /** + * Sends a recorded voice message, which is never compressed and carries a waveform instead of a caption. + * + * @param uri the recorded audio file. + * @param mimeType the MIME type of that recording. + * @param waveForm the amplitudes used to draw the waveform. + * @param inReplyToEventId the event this message replies to, or `null` if it is not a reply. + */ suspend fun sendVoiceMessage( uri: Uri, mimeType: String, @@ -61,6 +110,14 @@ interface MediaSender { inReplyToEventId: EventId? = null, ): Result + /** + * Sends several already pre-processed media items as a single gallery message. + * + * @param mediaUploadInfos the pre-processed items, in the order they should appear. + * @param caption the text shown along with the gallery, or `null` for none. + * @param formattedCaption the caption as HTML, or `null` to send it unformatted. + * @param inReplyToEventId the event this message replies to, or `null` if it is not a reply. + */ suspend fun sendGallery( mediaUploadInfos: List, caption: String?, @@ -68,5 +125,6 @@ interface MediaSender { inReplyToEventId: EventId?, ): Result + /** Deletes the temporary files produced by pre-processing; must be called even when nothing was ultimately sent. */ fun cleanUp() } From c6af196bf0ca6008e9c0a0f750b902791b265a40 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 09:50:08 +0200 Subject: [PATCH 166/338] Add KDoc to the `libraries/wellknown` API interfaces Document WellknownRetriever, ElementWellknownStore, ElementWellKnownParser and their factories, along with every member. The caching semantics are the part callers need and cannot see in the signatures, so they are recorded here: the store reports entries older than a day as outdated rather than missing, the retriever returns such a value immediately and refreshes in the background, an enterprise build with a hardcoded configuration short-circuits entirely, and the parser ignores unknown JSON fields. Co-Authored-By: Claude Opus 5 (1M context) --- .../wellknown/api/ElementWellKnownParser.kt | 8 +++++ .../wellknown/api/ElementWellknownStore.kt | 29 +++++++++++++++++++ .../wellknown/api/WellknownRetriever.kt | 17 +++++++++++ 3 files changed, 54 insertions(+) diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnownParser.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnownParser.kt index db0a323a5f..1466a5b422 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnownParser.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnownParser.kt @@ -7,6 +7,14 @@ package io.element.android.libraries.wellknown.api +/** + * Parses the raw JSON of an Element `.well-known` document. + */ fun interface ElementWellKnownParser { + /** + * Parses the document, failing when it is not valid JSON; unknown fields are ignored so a newer server does not break an older client. + * + * @param json the raw document as served by the server. + */ fun parse(json: String): Result } diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt index abb1cf53c7..93256fa6f1 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt @@ -7,12 +7,41 @@ package io.element.android.libraries.wellknown.api +/** + * On-disk cache of the Element `.well-known` documents already fetched, so the app has a configuration to work with while offline. + */ interface ElementWellknownStore { + /** + * Reads the cached document of a domain. + * Entries older than a day are reported as outdated rather than missing, so the caller can use them while refreshing; + * a document that no longer parses is reported as an error. + * + * @param domain the server whose cached document is requested. + */ suspend fun get(domain: String): WellknownRetrieverResult + + /** + * Stores the document of a domain and stamps it as fetched now, replacing any previous entry. + * + * @param domain the server the document belongs to. + * @param wellknown the raw document, stored as-is rather than parsed. + */ suspend fun update(domain: String, wellknown: String): Result + + /** + * Removes the cached document of a domain, used when the stored value turns out to be unusable. + * + * @param domain the server whose entry is removed. + */ suspend fun delete(domain: String): Result + /** + * Creates a store, one per configuration source so that their entries never collide. + */ fun interface Factory { + /** + * @param prefix namespaces the cache keys, or `null` for the default well-known endpoint. + */ fun create(prefix: String?): ElementWellknownStore } } diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt index 56d251426a..9e265bc213 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt @@ -10,13 +10,30 @@ package io.element.android.libraries.wellknown.api import io.element.android.libraries.matrix.api.UrlContentFetcher +/** + * Retrieves the Element specific `.well-known` configuration of a server, which is how a deployment advertises its own settings. + */ interface WellknownRetriever { + /** + * Returns the configuration of a host, served from the local cache when possible. + * A cached value that has gone stale is returned as outdated while a refresh runs in the background, so this rarely waits on the network. + * An enterprise build with a hardcoded configuration always short-circuits and returns that instead. + * + * @param host the server to look up; a full URL is accepted and reduced to its host. + * @param source which endpoint to read the configuration from, which also selects the cache it is stored in. + */ suspend fun getElementWellKnown( host: String, source: EnterpriseRemoteConfigSource ): WellknownRetrieverResult + /** + * Creates a retriever, which needs a fetcher because the configuration is read over HTTP. + */ interface Factory { + /** + * @param urlContentFetcher used to perform the request; a client that is not authenticated yet also works. + */ fun create(urlContentFetcher: UrlContentFetcher): WellknownRetriever } } From a90801bcecde0c7b428bce6514ad33c3668ab84d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 09:56:28 +0200 Subject: [PATCH 167/338] Add KDoc to the `libraries/pushstore` API interfaces Document UserPushStore, UserPushStoreFactory, PushClientSecret, PushClientSecretStore and PushClientSecretFactory along with all their members. The client secret interfaces now explain why they exist at all: a push gateway delivers notifications without saying which account they belong to, so the secret is what lets an incoming push be attributed back to a session. Co-Authored-By: Claude Opus 5 (1M context) --- .../libraries/pushstore/api/UserPushStore.kt | 22 +++++++++++++++ .../pushstore/api/UserPushStoreFactory.kt | 5 ++++ .../api/clientsecret/PushClientSecret.kt | 9 ++++++ .../clientsecret/PushClientSecretFactory.kt | 4 +++ .../api/clientsecret/PushClientSecretStore.kt | 28 +++++++++++++++++++ 5 files changed, 68 insertions(+) diff --git a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStore.kt b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStore.kt index 8a1b2f9f21..cec03e3c78 100644 --- a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStore.kt +++ b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStore.kt @@ -13,15 +13,36 @@ import kotlinx.coroutines.flow.Flow * Store data related to push about a user. */ interface UserPushStore { + /** The push provider selected for this user, or `null` when none has been selected yet. */ suspend fun getPushProviderName(): String? + + /** + * @param value the name of the push provider to remember for this user. + */ suspend fun setPushProviderName(value: String) + + /** The push key last registered with the homeserver, used to detect that a re-registration is needed; `null` when never registered. */ suspend fun getCurrentRegisteredPushKey(): String? + + /** + * @param value the push key that has just been registered, or `null` to forget it after unregistering. + */ suspend fun setCurrentRegisteredPushKey(value: String?) + /** Whether the user has enabled notifications on this device. */ fun getNotificationEnabledForDevice(): Flow + + /** + * @param enabled true to enable notifications on this device. + */ suspend fun setNotificationEnabledForDevice(enabled: Boolean) + /** Whether the user has asked not to be warned about pusher registration failures. */ fun ignoreRegistrationError(): Flow + + /** + * @param ignore true to stop warning the user about registration failures. + */ suspend fun setIgnoreRegistrationError(ignore: Boolean) /** @@ -29,5 +50,6 @@ interface UserPushStore { */ fun useCompleteNotificationFormat(): Boolean + /** Erases every push preference of this user, to be called when their session is removed. */ suspend fun reset() } diff --git a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStoreFactory.kt b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStoreFactory.kt index 78571d8b30..5210602e6c 100644 --- a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStoreFactory.kt +++ b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStoreFactory.kt @@ -14,5 +14,10 @@ import io.element.android.libraries.matrix.api.core.SessionId * Store data related to push about a user. */ interface UserPushStoreFactory { + /** + * Returns the push store of a user, creating it on first use. + * + * @param userId the user whose store is requested. + */ fun getOrCreate(userId: SessionId): UserPushStore } diff --git a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecret.kt b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecret.kt index da3ea946f0..a2f4c0ce3b 100644 --- a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecret.kt +++ b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecret.kt @@ -10,15 +10,24 @@ package io.element.android.libraries.pushstore.api.clientsecret import io.element.android.libraries.matrix.api.core.SessionId +/** + * Manages the per-session client secret sent along with a pusher registration. + * + * Push gateways deliver notifications without saying which account they belong to, so the secret is what lets an incoming push be attributed to a session. + */ interface PushClientSecret { /** * To call when registering a pusher. It will return the existing secret or create a new one. + * + * @param userId the user the pusher is being registered for. */ suspend fun getSecretForUser(userId: SessionId): String /** * To call when receiving a push containing a client secret. * Return null if not found. + * + * @param clientSecret the secret carried by the incoming push. */ suspend fun getUserIdFromSecret(clientSecret: String): SessionId? } diff --git a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretFactory.kt b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretFactory.kt index d7ea7d5239..0b425b0115 100644 --- a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretFactory.kt +++ b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretFactory.kt @@ -8,6 +8,10 @@ package io.element.android.libraries.pushstore.api.clientsecret +/** + * Generates the client secrets used to attribute an incoming push to a session; see [PushClientSecret] for how they are used. + */ interface PushClientSecretFactory { + /** Returns a new random secret. */ fun create(): String } diff --git a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretStore.kt b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretStore.kt index ecb87b2b14..c325042ff8 100644 --- a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretStore.kt +++ b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretStore.kt @@ -10,9 +10,37 @@ package io.element.android.libraries.pushstore.api.clientsecret import io.element.android.libraries.matrix.api.core.SessionId +/** + * Persists the mapping between sessions and their push client secrets, in both directions. + */ interface PushClientSecretStore { + /** + * Stores the secret of a user, replacing any previous one. + * + * @param userId the user the secret belongs to. + * @param clientSecret the secret to store. + */ suspend fun storeSecret(userId: SessionId, clientSecret: String) + + /** + * Returns the stored secret of a user, or `null` when they have none yet. + * + * @param userId the user whose secret is requested. + */ suspend fun getSecret(userId: SessionId): String? + + /** + * Forgets the secret of a user, to be called when their session is removed. + * + * @param userId the user whose secret is erased. + */ suspend fun resetSecret(userId: SessionId) + + /** + * Reverse lookup used when a push arrives carrying only a secret. + * + * @param clientSecret the secret to resolve. + * @return the user it belongs to, or `null` when no session matches. + */ suspend fun getUserIdFromSecret(clientSecret: String): SessionId? } From 0a47aefdf70597334ffbbb6d1aebdc4ca3175966 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 09:57:38 +0200 Subject: [PATCH 168/338] Add KDoc to the `libraries/permissions` API interfaces Document PermissionsStore, PermissionStateProvider, LocalNetworkPermissionAdvisor and the PermissionsPresenter factory, along with their members and parameters. The store doc explains why the module exists: Android does not let an app distinguish "never asked" from "asked and denied", so the app has to remember that itself in order to decide whether it may show a rationale. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/PermissionStateProvider.kt | 29 ++++++++++++++++++ .../permissions/api/PermissionsPresenter.kt | 6 ++++ .../permissions/api/PermissionsStore.kt | 30 +++++++++++++++++++ .../LocalNetworkPermissionAdvisor.kt | 5 ++++ 4 files changed, 70 insertions(+) diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionStateProvider.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionStateProvider.kt index beeef0b234..98fb3edbe0 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionStateProvider.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionStateProvider.kt @@ -10,11 +10,40 @@ package io.element.android.libraries.permissions.api import kotlinx.coroutines.flow.Flow +/** + * Answers permission questions by combining the live system state with what [PermissionsStore] remembers about past requests. + */ interface PermissionStateProvider { + /** + * Whether the permission is granted right now, read from the system rather than from the store. + * + * @param permission the Android permission name. + */ fun isPermissionGranted(permission: String): Boolean + + /** + * @param permission the Android permission name. + * @param value true once the user has refused this permission. + */ suspend fun setPermissionDenied(permission: String, value: Boolean) + + /** + * Whether the user has refused this permission before. + * + * @param permission the Android permission name. + */ fun isPermissionDenied(permission: String): Flow + /** + * @param permission the Android permission name. + * @param value true once the system dialog for this permission has been shown. + */ suspend fun setPermissionAsked(permission: String, value: Boolean) + + /** + * Whether the system dialog for this permission has already been shown. + * + * @param permission the Android permission name. + */ fun isPermissionAsked(permission: String): Flow } diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsPresenter.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsPresenter.kt index f2ccb2bfc3..07918d0c86 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsPresenter.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsPresenter.kt @@ -11,7 +11,13 @@ package io.element.android.libraries.permissions.api import io.element.android.libraries.architecture.Presenter interface PermissionsPresenter : Presenter { + /** + * Creates a presenter dedicated to one permission. + */ interface Factory { + /** + * @param permission the Android permission name this presenter will request. + */ fun create(permission: String): PermissionsPresenter } } diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStore.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStore.kt index 3bb7d2c679..0bf7f0aefd 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStore.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStore.kt @@ -10,15 +10,45 @@ package io.element.android.libraries.permissions.api import kotlinx.coroutines.flow.Flow +/** + * Remembers what the app has already asked the user about, since Android itself does not let an app tell + * "never asked" apart from "asked and denied". + */ interface PermissionsStore { + /** + * @param permission the Android permission name. + * @param value true once the user has refused this permission. + */ suspend fun setPermissionDenied(permission: String, value: Boolean) + + /** + * Whether the user has refused this permission before. + * + * @param permission the Android permission name. + */ fun isPermissionDenied(permission: String): Flow + /** + * @param permission the Android permission name. + * @param value true once the system dialog for this permission has been shown. + */ suspend fun setPermissionAsked(permission: String, value: Boolean) + + /** + * Whether the system dialog for this permission has already been shown, which decides whether the app may show a rationale instead. + * + * @param permission the Android permission name. + */ fun isPermissionAsked(permission: String): Flow + /** + * Forgets what was recorded about one permission, so the app behaves as if it had never been requested. + * + * @param permission the Android permission name. + */ suspend fun resetPermission(permission: String) + /** Forgets every recorded permission; exposed for debugging from the developer options. */ // To debug suspend fun resetStore() } diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/localnetwork/LocalNetworkPermissionAdvisor.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/localnetwork/LocalNetworkPermissionAdvisor.kt index 9863dbb751..8d801cfe67 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/localnetwork/LocalNetworkPermissionAdvisor.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/localnetwork/LocalNetworkPermissionAdvisor.kt @@ -7,10 +7,15 @@ package io.element.android.libraries.permissions.api.localnetwork +/** + * Decides whether the local network permission is needed to reach a homeserver, which is the case for a server on the user's own network. + */ interface LocalNetworkPermissionAdvisor { /** * Returns true when the app should request the ACCESS_LOCAL_NETWORK permission before making * network requests to [homeserverUrl]. + * + * @param homeserverUrl the server the app is about to contact. */ suspend fun shouldRequestPermissionFor(homeserverUrl: String): Boolean } From 7f3d896a3e449ee207c83a286c86d4491fa49df5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:01:14 +0200 Subject: [PATCH 169/338] Add KDoc to the `features/enterprise` API interfaces Document EnterpriseService, SessionEnterpriseService and RemoteEnterpriseConfigProvider along with all their members and parameters. Two points that callers get wrong are stated explicitly: these services return permissive defaults on a standard build, so there is no need to branch on the build type, and SessionEnterpriseService only answers meaningfully once init has run. The many nullable getters now say what null means in each case, which is generally "no override, use the app default". Co-Authored-By: Claude Opus 5 (1M context) --- .../enterprise/api/EnterpriseService.kt | 54 +++++++++++++++++++ .../api/SessionEnterpriseService.kt | 14 +++++ .../RemoteEnterpriseConfigProvider.kt | 12 +++++ 3 files changed, 80 insertions(+) diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt index a435b53e48..7d23ad0ea1 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt @@ -15,11 +15,41 @@ import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.wellknown.api.ElementWellKnown import kotlinx.coroutines.flow.Flow +/** + * Exposes the restrictions and customisations an enterprise deployment can impose: allowed homeservers, branding, push gateways and more. + * + * On a standard build this reports permissive defaults, so callers do not need to branch on the build type themselves. + */ interface EnterpriseService { + /** Whether this build is an enterprise one; `false` for the public app. */ val isEnterpriseBuild: Boolean + + /** + * Whether the given session belongs to an enterprise deployment, which can be true even on a standard build. + * + * @param sessionId the session to check. + */ suspend fun isEnterpriseUser(sessionId: SessionId): Boolean + + /** + * Rewrites the authentication server URL when the deployment requires a different one from the one advertised. + * + * @param url the URL to rewrite. + * @param homeserver the homeserver the URL belongs to. + * @param urlContentFetcher used to read the deployment configuration; a client that is not authenticated yet also works. + */ suspend fun tweakMasUrl(url: String, homeserver: String, urlContentFetcher: UrlContentFetcher): String + + /** + * The homeservers the user is allowed to sign in to; an empty list, or one containing [ANY_ACCOUNT_PROVIDER], means no restriction. + */ fun defaultHomeserverList(): List + + /** + * Whether the user is allowed to sign in to a given homeserver, according to [defaultHomeserverList]. + * + * @param homeserverUrl the server the user is trying to use. + */ suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean /** @@ -29,17 +59,38 @@ interface EnterpriseService { */ suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?) + /** + * The brand color to apply, or `null` when the default theme color should be used. + * + * @param sessionId the session to read the color of, or `null` for the color used when no session is active. + */ fun brandColorsFlow(sessionId: SessionId?): Flow + /** + * The full light and dark color palettes derived from the brand color. + * + * @param sessionId the session to read the colors of, or `null` for the colors used when no session is active. + */ fun semanticColorsFlow(sessionId: SessionId?): Flow + /** The push gateway to register with when using Firebase, or `null` to use the app default. */ fun firebasePushGateway(): String? + + /** The push gateway to register with when using UnifiedPush, or `null` to use the app default. */ fun unifiedPushDefaultPushGateway(): String? + /** + * Where bug reports should be submitted, which an enterprise deployment can redirect or disable entirely. + * + * @param sessionId the session to read the setting of, or `null` for the setting used when no session is active. + */ fun bugReportUrlFlow(sessionId: SessionId?): Flow /** * Gets Notification Channel to use for the noisy notifications of the provided session. + * Returns `null` when the session has no dedicated channel and the app-wide one should be used. + * + * @param sessionId the session whose channel is requested. */ fun getNoisyNotificationChannelId(sessionId: SessionId): String? @@ -50,6 +101,9 @@ interface EnterpriseService { /** * Gets the Element Server Suite (ESS) config endpoint URL for the given domain. + * Returns `null` when this build does not read its configuration from an ESS deployment. + * + * @param domain the server whose configuration endpoint is requested. */ fun essConfigEndpointUrl(domain: String): String? diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt index ee005a0b6b..84067ce56f 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt @@ -8,11 +8,25 @@ package io.element.android.features.enterprise.api +/** + * The session-scoped counterpart of [EnterpriseService], answering the same kind of questions for the session it belongs to. + * + * [init] must have run before the other members return meaningful answers. + */ interface SessionEnterpriseService { + /** Whether Element Call may be used on this session, which a deployment can turn off. */ suspend fun isElementCallAvailable(): Boolean + + /** + * Rewrites the authentication server URL for this session; see [EnterpriseService.tweakMasUrl]. + * + * @param url the URL to rewrite. + */ suspend fun tweakMasUrl(url: String): String + /** Whether the homeserver forbids creating encrypted rooms, in which case the app must not offer the option. */ suspend fun isEncryptionDisabledByHomeserver(): Boolean + /** Loads the deployment configuration of this session; to be called once when the session starts. */ suspend fun init() } diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt index adbcc5b31a..4507b1a72a 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt @@ -15,9 +15,21 @@ import io.element.android.libraries.wellknown.api.WellknownRetrieverResult * Wrapper to fetch the remote enterprise configuration for a given [SessionId], either from a local cache or from a remote source. */ interface RemoteEnterpriseConfigProvider { + /** + * Returns the configuration of a session, served from the cache when possible. + * A stale entry is reported as outdated while a refresh runs in the background, so this rarely waits on the network. + * + * @param sessionId the session whose configuration is requested. + */ suspend fun get(sessionId: SessionId): WellknownRetrieverResult + /** + * Creates a provider, which needs a fetcher because the configuration is read over HTTP. + */ interface Factory { + /** + * @param urlContentFetcher used to perform the request; a client that is not authenticated yet also works. + */ fun create(urlContentFetcher: UrlContentFetcher): RemoteEnterpriseConfigProvider } } From 186d5b65b936b600d749b0565cc14fac25d461d8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:02:48 +0200 Subject: [PATCH 170/338] Add KDoc to the `features/messages` API interfaces Document HtmlConverterProvider, MessageComposerContext, PinnedEventsTimelineProvider, the MessagesEntryPoint node proxy and the voice message composer factory. HtmlConverterProvider carries the ordering constraint that matters: the converter is rebuilt from the Compose theme, so Update has to be composed before provide is called. MessagesEntryPoint and its Callback are navigation plumbing and out of scope. Co-Authored-By: Claude Opus 5 (1M context) --- .../features/messages/api/MessageComposerContext.kt | 1 + .../android/features/messages/api/MessagesEntryPoint.kt | 9 +++++++++ .../messages/api/pinned/PinnedEventsTimelineProvider.kt | 5 +++++ .../messages/api/timeline/HtmlConverterProvider.kt | 7 +++++++ .../composer/VoiceMessageComposerPresenter.kt | 6 ++++++ 5 files changed, 28 insertions(+) diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessageComposerContext.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessageComposerContext.kt index 534af8a5f9..12af1b32f2 100644 --- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessageComposerContext.kt +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessageComposerContext.kt @@ -17,5 +17,6 @@ import io.element.android.libraries.textcomposer.model.MessageComposerMode * the composer is in a thread, if it's editing a message, etc. */ interface MessageComposerContext { + /** What the composer is currently doing: writing a new message, editing one, replying, or composing in a thread. */ val composerMode: MessageComposerMode } diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt index 3eecd54f3e..dd49dd0465 100644 --- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt @@ -50,7 +50,16 @@ interface MessagesEntryPoint : FeatureEntryPoint { callback: Callback, ): Node + /** + * Lets callers outside this feature drive an already created messages node, for navigation that has to happen after it is attached. + */ interface NodeProxy { + /** + * Opens a thread on top of the messages screen, suspending until the thread node is attached. + * + * @param threadId the thread to open. + * @param focusedEventId the event to scroll to within the thread, or `null` to open at the latest message. + */ suspend fun attachThread(threadId: ThreadId, focusedEventId: EventId?) } } diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/pinned/PinnedEventsTimelineProvider.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/pinned/PinnedEventsTimelineProvider.kt index 849b97479e..550373ed00 100644 --- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/pinned/PinnedEventsTimelineProvider.kt +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/pinned/PinnedEventsTimelineProvider.kt @@ -10,4 +10,9 @@ package io.element.android.features.messages.api.pinned import io.element.android.libraries.matrix.api.timeline.TimelineProvider +/** + * A [TimelineProvider] whose active timeline holds the pinned events of the room, rather than its live events. + * + * It exists as its own type so that it can be injected where only the pinned timeline is wanted. + */ interface PinnedEventsTimelineProvider : TimelineProvider diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/HtmlConverterProvider.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/HtmlConverterProvider.kt index f44c996cac..baeb37e417 100644 --- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/HtmlConverterProvider.kt +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/HtmlConverterProvider.kt @@ -11,9 +11,16 @@ package io.element.android.features.messages.api.timeline import androidx.compose.runtime.Composable import io.element.android.wysiwyg.utils.HtmlConverter +/** + * Provides the converter that turns message HTML into styled text. + * + * The converter depends on the current theme and typography, so [Update] must be composed before [provide] is called. + */ interface HtmlConverterProvider { + /** Rebuilds the converter from the current Compose theme; call this once, high in the composition of a screen that renders messages. */ @Composable fun Update() + /** Returns the converter built by the last [Update] call. */ fun provide(): HtmlConverter } diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerPresenter.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerPresenter.kt index 848f9fe82d..832d029e40 100644 --- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerPresenter.kt +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerPresenter.kt @@ -12,7 +12,13 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.timeline.Timeline fun interface VoiceMessageComposerPresenter : Presenter { + /** + * Creates a presenter for one voice message composer. + */ interface Factory { + /** + * @param timelineMode the timeline the recording will be sent to, which matters for threads in particular. + */ fun create(timelineMode: Timeline.Mode): VoiceMessageComposerPresenter } } From bc6f237507b38fcf5e91769f3f60664fe04dc5b8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:04:31 +0200 Subject: [PATCH 171/338] Add KDoc to the `features/invitepeople` API interfaces Document InvitePeopleState and each of its properties, InvitePeopleEvents, InvitePeopleRenderer and the presenter factory. The state properties now say what drives them, in particular that createRoomFromDmAction covers the case of inviting a third person into an existing direct message, and that the factory accepts a null room for that flow. Co-Authored-By: Claude Opus 5 (1M context) --- .../features/invitepeople/api/InvitePeopleEvents.kt | 3 +++ .../invitepeople/api/InvitePeoplePresenter.kt | 7 +++++++ .../invitepeople/api/InvitePeopleRenderer.kt | 9 +++++++++ .../features/invitepeople/api/InvitePeopleState.kt | 12 ++++++++++++ 4 files changed, 31 insertions(+) diff --git a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvents.kt b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvents.kt index 0422fac4f1..9570ab8152 100644 --- a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvents.kt +++ b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvents.kt @@ -8,6 +8,9 @@ package io.element.android.features.invitepeople.api +/** + * Events the invite people UI sends to its presenter through [InvitePeopleState.eventSink]. + */ interface InvitePeopleEvents { data object SendInvites : InvitePeopleEvents data object CloseSearch : InvitePeopleEvents diff --git a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeoplePresenter.kt b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeoplePresenter.kt index 0be0798680..1863397e48 100644 --- a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeoplePresenter.kt +++ b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeoplePresenter.kt @@ -13,7 +13,14 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.JoinedRoom interface InvitePeoplePresenter : Presenter { + /** + * Creates a presenter for one invite flow. + */ interface Factory { + /** + * @param joinedRoom the room to invite into, or `null` when the room does not exist yet, as when inviting from a direct message. + * @param roomId the id of the room to invite into. + */ fun create( joinedRoom: JoinedRoom?, roomId: RoomId, diff --git a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleRenderer.kt b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleRenderer.kt index 7bad81ab65..0bbaa45a2f 100644 --- a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleRenderer.kt +++ b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleRenderer.kt @@ -11,7 +11,16 @@ package io.element.android.features.invitepeople.api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +/** + * Renders the invite people UI, so that other screens can embed it without depending on its implementation. + */ interface InvitePeopleRenderer { + /** + * Draws the invite list and its search field. + * + * @param state the state produced by [InvitePeoplePresenter]. + * @param modifier layout modifier for the container. + */ @Composable fun Render( state: InvitePeopleState, diff --git a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt index d14042cff7..0863ec7212 100644 --- a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt +++ b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt @@ -11,10 +11,22 @@ package io.element.android.features.invitepeople.api import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId +/** + * State of the invite people UI, produced by an [InvitePeoplePresenter] and consumed by an [InvitePeopleRenderer]. + */ interface InvitePeopleState { + /** Whether the send button should be enabled, i.e. at least one user is selected and the user has the permission. */ val canInvite: Boolean + + /** Whether the search field currently has focus, which the host screen uses to adapt its own layout. */ val isSearchActive: Boolean + + /** Progress of the invite request, so the UI can show a loader and surface failures. */ val sendInvitesAction: AsyncAction + + /** Progress of turning a direct message into a room, which happens when inviting a third person into a DM. */ val createRoomFromDmAction: AsyncAction + + /** Where the UI sends its events. */ val eventSink: (InvitePeopleEvents) -> Unit } From 76411b07fb26551f7ebfaf211671265e547df2bb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:05:31 +0200 Subject: [PATCH 172/338] Add KDoc to the `features/leaveroom` API interfaces Document LeaveRoomState, LeaveRoomEvent and LeaveRoomRenderer. The renderer doc records the two things a host screen needs to know, both taken from LeaveRoomPresenter and LeaveRoomView: it draws nothing until a leave event is sent, and onSelectNewOwners fires specifically when the user is the last owner of a room that still has other members. Co-Authored-By: Claude Opus 5 (1M context) --- .../android/features/leaveroom/api/LeaveRoomEvent.kt | 3 +++ .../features/leaveroom/api/LeaveRoomRenderer.kt | 10 ++++++++++ .../android/features/leaveroom/api/LeaveRoomState.kt | 4 ++++ 3 files changed, 17 insertions(+) diff --git a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomEvent.kt b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomEvent.kt index 6d228ee887..f0c3eaf7bf 100644 --- a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomEvent.kt +++ b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomEvent.kt @@ -10,6 +10,9 @@ package io.element.android.features.leaveroom.api import io.element.android.libraries.matrix.api.core.RoomId +/** + * Events sent to the leave room presenter through [LeaveRoomState.eventSink]. + */ interface LeaveRoomEvent { data class LeaveRoom(val roomId: RoomId, val needsConfirmation: Boolean) : LeaveRoomEvent } diff --git a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomRenderer.kt b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomRenderer.kt index 852193d874..8472e098ec 100644 --- a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomRenderer.kt +++ b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomRenderer.kt @@ -12,7 +12,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import io.element.android.libraries.matrix.api.core.RoomId +/** + * Renders the confirmation dialogs of the leave room flow; it draws nothing until the host screen sends a leave event. + */ fun interface LeaveRoomRenderer { + /** + * Draws whichever dialog the current state calls for. + * + * @param state the state produced by the leave room presenter. + * @param onSelectNewOwners called when the user is the last owner of a room that still has other members, and must promote someone before leaving. + * @param modifier layout modifier for the container. + */ @Composable fun Render( state: LeaveRoomState, diff --git a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomState.kt b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomState.kt index 92a5916858..b9150b59f0 100644 --- a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomState.kt +++ b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomState.kt @@ -10,7 +10,11 @@ package io.element.android.features.leaveroom.api import androidx.compose.runtime.Immutable +/** + * State of the leave room flow, which is only the confirmation dialogs: the screen it is hosted in owns the rest of the UI. + */ @Immutable interface LeaveRoomState { + /** Where the host screen sends its events to start or confirm leaving a room. */ val eventSink: (LeaveRoomEvent) -> Unit } From 50214fe01a634e2e1b250fcc8cc963b476d585cb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:06:47 +0200 Subject: [PATCH 173/338] Add KDoc to the `features/poll` API interfaces Document PollContentStateFactory, SendPollResponseAction and EndPollAction along with their members and parameters. The two action interfaces exist to wrap a plain timeline call, so their docs say what they add: both capture an analytics event on success, which is the reason callers should go through them rather than calling the timeline directly. Co-Authored-By: Claude Opus 5 (1M context) --- .../features/poll/api/actions/EndPollAction.kt | 7 +++++++ .../poll/api/actions/SendPollResponseAction.kt | 8 ++++++++ .../api/pollcontent/PollContentStateFactory.kt | 15 +++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/EndPollAction.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/EndPollAction.kt index a46a2db781..b33a00c165 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/EndPollAction.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/EndPollAction.kt @@ -11,6 +11,13 @@ package io.element.android.features.poll.api.actions import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.Timeline +/** + * Closes a poll so no further answers are accepted, wrapping the timeline call so the analytics are captured in one place. + */ interface EndPollAction { + /** + * @param timeline the timeline the poll lives in. + * @param pollStartId the poll start event to close. + */ suspend fun execute(timeline: Timeline, pollStartId: EventId): Result } diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/SendPollResponseAction.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/SendPollResponseAction.kt index 00b60ed5ae..cbaace50fb 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/SendPollResponseAction.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/actions/SendPollResponseAction.kt @@ -11,7 +11,15 @@ package io.element.android.features.poll.api.actions import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.Timeline +/** + * Sends the current user's answer to a poll, wrapping the timeline call so the analytics are captured in one place. + */ interface SendPollResponseAction { + /** + * @param timeline the timeline the poll lives in. + * @param pollStartId the poll start event being answered. + * @param answerId the id of the chosen answer. + */ suspend fun execute( timeline: Timeline, pollStartId: EventId, diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFactory.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFactory.kt index c9bd31ea4f..87e3f5eece 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFactory.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFactory.kt @@ -12,7 +12,16 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +/** + * Turns the poll content of a timeline event into the state the poll UI renders. + */ interface PollContentStateFactory { + /** + * Convenience overload that reads the editability and ownership from the timeline item itself. + * + * @param eventTimelineItem the timeline item carrying the poll. + * @param content the poll content of that item. + */ suspend fun create(eventTimelineItem: EventTimelineItem, content: PollContent): PollContentState { return create( eventId = eventTimelineItem.eventId, @@ -21,5 +30,11 @@ interface PollContentStateFactory { content = content, ) } + /** + * @param eventId the poll start event, or `null` while the poll is still a local echo. + * @param isEditable whether the current user may still edit the poll. + * @param isOwn whether the poll was created by the current user. + * @param content the poll question, answers and votes. + */ suspend fun create(eventId: EventId?, isEditable: Boolean, isOwn: Boolean, content: PollContent): PollContentState } From 055fe408a64d045579838015b0045ccfc8c8d749 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:06:53 +0200 Subject: [PATCH 174/338] Add KDoc to the `features/roommembermoderation` API interfaces Document RoomMemberModerationState, RoomMemberModerationEvents and RoomMemberModerationRenderer along with their members and parameters. The renderer doc states that it draws nothing until the host screen asks for a member's actions, and that onSelectAction exists so the host can navigate for the actions that need a screen of their own. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/RoomMemberModerationEvents.kt | 3 +++ .../api/RoomMemberModerationRenderer.kt | 11 +++++++++++ .../api/RoomMemberModerationState.kt | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationEvents.kt b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationEvents.kt index 2238ff41df..d452ff4e66 100644 --- a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationEvents.kt +++ b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationEvents.kt @@ -10,6 +10,9 @@ package io.element.android.features.roommembermoderation.api import io.element.android.libraries.matrix.api.user.MatrixUser +/** + * Events sent to the moderation presenter through [RoomMemberModerationState.eventSink]. + */ interface RoomMemberModerationEvents { data class ShowActionsForUser(val user: MatrixUser) : RoomMemberModerationEvents data class ProcessAction(val action: ModerationAction, val targetUser: MatrixUser) : RoomMemberModerationEvents diff --git a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationRenderer.kt b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationRenderer.kt index efc87d8a40..7696f826a1 100644 --- a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationRenderer.kt +++ b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationRenderer.kt @@ -12,7 +12,18 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import io.element.android.libraries.matrix.api.user.MatrixUser +/** + * Renders the moderation action sheet and its confirmation dialogs; it draws nothing until the host screen asks for a member's actions. + */ interface RoomMemberModerationRenderer { + /** + * Draws whichever sheet or dialog the current state calls for. + * + * @param state the state produced by the moderation presenter. + * @param onSelectAction called when the user picks an action, so the host screen can navigate if that action needs its own screen. + * @param onAvatarClick called when the user taps the member's avatar, or `null` to make it non-interactive. + * @param modifier layout modifier for the container. + */ @Composable fun Render( state: RoomMemberModerationState, diff --git a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationState.kt b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationState.kt index c9ee958f6f..9227c77edf 100644 --- a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationState.kt +++ b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationState.kt @@ -10,9 +10,15 @@ package io.element.android.features.roommembermoderation.api import androidx.compose.runtime.Immutable +/** + * State of the member moderation flow, which is the action sheet and the confirmation dialogs shown for a room member. + */ @Immutable interface RoomMemberModerationState { + /** Which moderation actions the current user is allowed to perform, so the UI can hide or disable the rest. */ val permissions: RoomMemberModerationPermissions + + /** Where the host screen sends its events to open the actions of a member or run one of them. */ val eventSink: (RoomMemberModerationEvents) -> Unit } From c0848cee9d7b8a2ef9670dc3ddf0e5ccd2d8a2b7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:28:47 +0200 Subject: [PATCH 175/338] Add KDoc to the `libraries/deeplink` API interfaces Document DeeplinkParser, DeepLinkCreator and InviteFriendsUseCase, including that DeepLinkCreator.create silently ignores the deeper arguments once an outer one is null. Co-Authored-By: Claude Opus 5 (1M context) --- .../android/libraries/deeplink/api/DeepLinkCreator.kt | 11 +++++++++++ .../android/libraries/deeplink/api/DeeplinkParser.kt | 7 +++++++ .../deeplink/api/usecase/InviteFriendsUseCase.kt | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/DeepLinkCreator.kt b/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/DeepLinkCreator.kt index 2b46b15e46..938939c6f0 100644 --- a/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/DeepLinkCreator.kt +++ b/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/DeepLinkCreator.kt @@ -13,6 +13,17 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId +/** + * Builds the app's internal deep links, used to point a notification or a shortcut at a precise place in the app. + */ fun interface DeepLinkCreator { + /** + * Builds a link to the most precise target the arguments allow, ignoring the deeper ones once an outer one is `null`. + * + * @param sessionId the session to open. + * @param roomId the room to open, or `null` to stop at the room list. + * @param threadId the thread to open within that room, or `null` for the main timeline. + * @param eventId the event to focus on, or `null` to open at the latest message. + */ fun create(sessionId: SessionId, roomId: RoomId?, threadId: ThreadId?, eventId: EventId?): String } diff --git a/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/DeeplinkParser.kt b/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/DeeplinkParser.kt index f0f8640a2d..7a711d8a3a 100644 --- a/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/DeeplinkParser.kt +++ b/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/DeeplinkParser.kt @@ -10,6 +10,13 @@ package io.element.android.libraries.deeplink.api import android.content.Intent +/** + * Extracts the app's own deep link data from an incoming intent; see [DeepLinkCreator] for the other direction. + */ fun interface DeeplinkParser { + /** + * @param intent the intent the app was started or resumed with. + * @return the session, room and event to open, or `null` when the intent is not one of the app's deep links. + */ fun getFromIntent(intent: Intent): DeeplinkData? } diff --git a/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/usecase/InviteFriendsUseCase.kt b/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/usecase/InviteFriendsUseCase.kt index 64287f918c..2c4406a482 100644 --- a/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/usecase/InviteFriendsUseCase.kt +++ b/libraries/deeplink/api/src/main/kotlin/io/element/android/libraries/deeplink/api/usecase/InviteFriendsUseCase.kt @@ -10,6 +10,12 @@ package io.element.android.libraries.deeplink.api.usecase import android.app.Activity +/** + * Opens the system share sheet with an invitation to install the app. + */ fun interface InviteFriendsUseCase { + /** + * @param activity the activity the share sheet is shown from. + */ fun execute(activity: Activity) } From 6446b30255c173a0a8a0b2bdeb2a2b58789ed687 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:28:48 +0200 Subject: [PATCH 176/338] Add KDoc to the `libraries/eventformatter` API interfaces Document RoomLatestEventFormatter, TimelineEventFormatter and PinnedMessagesBannerFormatter along with their members. The formatters return a nullable CharSequence, so each one now states what null means: the event is not rendered as text at all. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/PinnedMessagesBannerFormatter.kt | 6 ++++++ .../api/RoomLatestEventFormatter.kt | 18 ++++++++++++++++++ .../api/TimelineEventFormatter.kt | 16 ++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/PinnedMessagesBannerFormatter.kt b/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/PinnedMessagesBannerFormatter.kt index 67994f18b9..986e5dfe32 100644 --- a/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/PinnedMessagesBannerFormatter.kt +++ b/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/PinnedMessagesBannerFormatter.kt @@ -10,6 +10,12 @@ package io.element.android.libraries.eventformatter.api import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem +/** + * Formats the short summary of a pinned event shown in the banner at the top of a room. + */ interface PinnedMessagesBannerFormatter { + /** + * @param event the pinned event to describe. + */ fun format(event: EventTimelineItem): CharSequence } diff --git a/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/RoomLatestEventFormatter.kt b/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/RoomLatestEventFormatter.kt index abb5cc929d..7ddc65f57b 100644 --- a/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/RoomLatestEventFormatter.kt +++ b/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/RoomLatestEventFormatter.kt @@ -10,7 +10,25 @@ package io.element.android.libraries.eventformatter.api import io.element.android.libraries.matrix.api.roomlist.LatestEventValue +/** + * Formats the one-line preview of a room's latest event, as shown in the room list. + */ interface RoomLatestEventFormatter { + /** + * Formats an event that is still local, i.e. queued or being sent. + * + * @param latestEvent the local event to describe. + * @param isDmRoom whether the room is a direct message, which lets the sender name be left out. + * @return the preview text, or `null` when this kind of event should not be previewed. + */ fun format(latestEvent: LatestEventValue.Local, isDmRoom: Boolean): CharSequence? + + /** + * Formats an event that has been received from the server. + * + * @param latestEvent the remote event to describe. + * @param isDmRoom whether the room is a direct message, which lets the sender name be left out. + * @return the preview text, or `null` when this kind of event should not be previewed. + */ fun format(latestEvent: LatestEventValue.Remote, isDmRoom: Boolean): CharSequence? } diff --git a/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/TimelineEventFormatter.kt b/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/TimelineEventFormatter.kt index be601fb82a..37081be242 100644 --- a/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/TimelineEventFormatter.kt +++ b/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/TimelineEventFormatter.kt @@ -13,7 +13,16 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventContent import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName +/** + * Turns a state or membership event into the human readable sentence shown in the timeline, such as "Alice joined the room". + */ interface TimelineEventFormatter { + /** + * Convenience overload that reads the sender and ownership from the timeline item itself. + * + * @param event the timeline item to describe. + * @return the sentence to display, or `null` when this kind of event is not rendered as text. + */ fun format(event: EventTimelineItem): CharSequence? { return format( content = event.content, @@ -22,5 +31,12 @@ interface TimelineEventFormatter { senderDisambiguatedDisplayName = event.senderProfile.getDisambiguatedDisplayName(event.sender), ) } + /** + * @param content the event content to describe. + * @param isOutgoing whether the event was sent by the current user, which selects the first-person wording. + * @param sender the user who sent the event. + * @param senderDisambiguatedDisplayName the sender's display name, already suffixed with their id when it is ambiguous in the room. + * @return the sentence to display, or `null` when this kind of event is not rendered as text. + */ fun format(content: EventContent, isOutgoing: Boolean, sender: UserId, senderDisambiguatedDisplayName: String): CharSequence? } From 1f3c73d3c6f0c4623df1af9836b6918202584618 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:28:56 +0200 Subject: [PATCH 177/338] Add KDoc to the `libraries/usersearch` API interfaces Document UserRepository and UserListDataSource along with their members. Two behaviours from MatrixUserRepository and MatrixUserListDataSource are recorded because callers rely on them: the data source swallows a failed directory request into an empty list, and a query that is a well-formed user id other than the current user's yields that user even when the directory does not know them. Co-Authored-By: Claude Opus 5 (1M context) --- .../usersearch/api/UserListDataSource.kt | 16 ++++++++++++++++ .../libraries/usersearch/api/UserRepository.kt | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserListDataSource.kt b/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserListDataSource.kt index 0f7e02c7a8..9446668502 100644 --- a/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserListDataSource.kt +++ b/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserListDataSource.kt @@ -11,8 +11,24 @@ package io.element.android.libraries.usersearch.api import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser +/** + * Low-level access to the homeserver's user directory; see [UserRepository] for the search used by the UI. + */ interface UserListDataSource { + /** + * Searches the user directory, returning an empty list rather than failing when the request does not succeed. + * + * @param query the text to look for in user ids and display names. + * @param count the maximum number of results to return. + */ // TODO should probably have a flow suspend fun search(query: String, count: Long): List + + /** + * Fetches one user's profile. + * + * @param userId the user whose profile is requested. + * @return the profile, or `null` when it could not be retrieved. + */ suspend fun getProfile(userId: UserId): MatrixUser? } diff --git a/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserRepository.kt b/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserRepository.kt index f7e5367a9a..b3be565ca5 100644 --- a/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserRepository.kt +++ b/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserRepository.kt @@ -10,6 +10,15 @@ package io.element.android.libraries.usersearch.api import kotlinx.coroutines.flow.Flow +/** + * Searches for users to start a conversation with or invite, combining the homeserver directory with what the app already knows. + */ interface UserRepository { + /** + * Runs a search and emits its progress, so the UI can show local results before the directory answers. + * A query that is a well-formed user id other than the current user's also yields that user, even when the directory does not know them. + * + * @param query the text typed by the user. + */ fun search(query: String): Flow } From c561674221be81617255251c4c6d1ef397236b6a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:28:56 +0200 Subject: [PATCH 178/338] Add KDoc to the `libraries/oauth` API interfaces Document OAuthIntentResolver and OAuthActionFlow along with their members. OAuthActionFlow now explains why it exists rather than the result simply being returned: the activity that receives the redirect and the login screen waiting for it are not in the same navigation subtree. Co-Authored-By: Claude Opus 5 (1M context) --- .../libraries/oauth/api/OAuthActionFlow.kt | 18 ++++++++++++++++++ .../libraries/oauth/api/OAuthIntentResolver.kt | 7 +++++++ 2 files changed, 25 insertions(+) diff --git a/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthActionFlow.kt b/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthActionFlow.kt index c6791e3197..7cb35de176 100644 --- a/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthActionFlow.kt +++ b/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthActionFlow.kt @@ -10,8 +10,26 @@ package io.element.android.libraries.oauth.api import kotlinx.coroutines.flow.FlowCollector +/** + * Carries the OAuth redirect from the activity that receives it to the login screen waiting for it. + * + * The two are not in the same navigation subtree, so the result cannot simply be returned. + */ interface OAuthActionFlow { + /** + * Publishes the action just received, to be picked up by whoever is collecting. + * + * @param oAuthAction the action resolved from the redirect intent. + */ fun post(oAuthAction: OAuthAction) + + /** + * Observes the published actions; `null` is emitted when there is nothing pending. + * + * @param collector the collector to feed. + */ suspend fun collect(collector: FlowCollector) + + /** Clears the pending action, so it is not handled twice. */ fun reset() } diff --git a/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthIntentResolver.kt b/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthIntentResolver.kt index 2091a86db4..5187649298 100644 --- a/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthIntentResolver.kt +++ b/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthIntentResolver.kt @@ -10,6 +10,13 @@ package io.element.android.libraries.oauth.api import android.content.Intent +/** + * Recognises the redirect the authentication server sends the user back with once they have signed in or cancelled. + */ interface OAuthIntentResolver { + /** + * @param intent the intent the app was resumed with. + * @return the action to carry out, or `null` when the intent is not an OAuth redirect. + */ fun resolve(intent: Intent): OAuthAction? } From aa7dc4fd0d85c49c3a39302119e049dd4f1bdc6b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:31:33 +0200 Subject: [PATCH 179/338] Add KDoc to the `services/toolbox` API interfaces Add the interface level docs that StringProvider, SystemClock and BuildVersionSdkIntProvider were missing, document ExternalIntentLauncher.launch, and add the remaining @param entries. Each doc says why the abstraction exists rather than restating the signature: these wrap Context, Build.VERSION and the clock so that presenters can be tested without them. Co-Authored-By: Claude Opus 5 (1M context) --- .../toolbox/api/intent/ExternalIntentLauncher.kt | 5 +++++ .../toolbox/api/sdk/BuildVersionSdkIntProvider.kt | 11 +++++++++++ .../services/toolbox/api/strings/StringProvider.kt | 7 +++++++ .../services/toolbox/api/systemclock/SystemClock.kt | 4 ++++ 4 files changed, 27 insertions(+) diff --git a/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/intent/ExternalIntentLauncher.kt b/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/intent/ExternalIntentLauncher.kt index ba34a6a716..acd58c9f64 100644 --- a/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/intent/ExternalIntentLauncher.kt +++ b/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/intent/ExternalIntentLauncher.kt @@ -14,5 +14,10 @@ import android.content.Intent * Used to launch external intents from anywhere in the app. */ interface ExternalIntentLauncher { + /** + * Starts the given intent, doing nothing when no installed app can handle it. + * + * @param intent the intent to hand over to the system. + */ fun launch(intent: Intent) } diff --git a/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/sdk/BuildVersionSdkIntProvider.kt b/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/sdk/BuildVersionSdkIntProvider.kt index 26bbe23548..40b2b2aac8 100644 --- a/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/sdk/BuildVersionSdkIntProvider.kt +++ b/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/sdk/BuildVersionSdkIntProvider.kt @@ -10,6 +10,9 @@ package io.element.android.services.toolbox.api.sdk import androidx.annotation.ChecksSdkIntAtLeast +/** + * Exposes the running Android API level, injected rather than read from `Build.VERSION` so tests can pretend to be on any version. + */ interface BuildVersionSdkIntProvider { /** * Return the current version of the Android SDK. @@ -18,6 +21,9 @@ interface BuildVersionSdkIntProvider { /** * Checks the if the current OS version is equal or greater than [version]. + * + * @param version the minimum API level required. + * @param result computed and returned only when the requirement is met. * @return A `non-null` result if true, `null` otherwise. */ @ChecksSdkIntAtLeast(parameter = 0, lambda = 1) @@ -29,6 +35,11 @@ interface BuildVersionSdkIntProvider { } } + /** + * Whether the current OS version is equal or greater than [version]. + * + * @param version the minimum API level to test against. + */ @ChecksSdkIntAtLeast(parameter = 0) fun isAtLeast(version: Int) = get() >= version } diff --git a/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/strings/StringProvider.kt b/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/strings/StringProvider.kt index 4570e09921..f82d542f7e 100644 --- a/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/strings/StringProvider.kt +++ b/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/strings/StringProvider.kt @@ -11,6 +11,9 @@ package io.element.android.services.toolbox.api.strings import androidx.annotation.PluralsRes import androidx.annotation.StringRes +/** + * Resolves Android string resources outside of Composable and Context-aware code, so presenters stay testable. + */ interface StringProvider { /** * Returns a localized string from the application's package's @@ -39,6 +42,10 @@ interface StringProvider { * Returns a localized formatted string from the application's package's * default string table, substituting the format arguments as defined in * [java.util.Formatter] and [java.lang.String.format], based on the given quantity. + * + * @param resId Resource id for the plurals. + * @param quantity The quantity used to select the plural form. + * @param formatArgs The format arguments that will be used for substitution. */ fun getQuantityString(@PluralsRes resId: Int, quantity: Int, vararg formatArgs: Any?): String diff --git a/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/systemclock/SystemClock.kt b/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/systemclock/SystemClock.kt index 5436b44354..52a189f607 100644 --- a/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/systemclock/SystemClock.kt +++ b/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/systemclock/SystemClock.kt @@ -8,6 +8,10 @@ package io.element.android.services.toolbox.api.systemclock +/** + * Reads the current time, injected rather than called statically so that tests can control it. + */ fun interface SystemClock { + /** The current wall-clock time in milliseconds since the epoch. */ fun epochMillis(): Long } From 4a5d0d2540f2c3ec6fce8303a184b80c768f0b79 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:31:34 +0200 Subject: [PATCH 180/338] Add KDoc to the `services/analyticsproviders` API interfaces Document AnalyticsProvider, AnalyticsTransaction, AnalyticsTracker and ErrorTracker at the interface level, and add the missing @param entries. The provider doc records that providers are driven by the analytics service and only receive data once the user has consented, and that startTransaction returns null on a provider with no performance support. Co-Authored-By: Claude Opus 5 (1M context) --- .../analyticsproviders/api/AnalyticsProvider.kt | 14 ++++++++++++++ .../api/AnalyticsTransaction.kt | 16 ++++++++++++++++ .../api/trackers/AnalyticsTracker.kt | 9 +++++++++ .../api/trackers/ErrorTracker.kt | 8 ++++++++ 4 files changed, 47 insertions(+) diff --git a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt index 593d62f40f..7b6278740c 100644 --- a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt +++ b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt @@ -11,15 +11,29 @@ package io.element.android.services.analyticsproviders.api import io.element.android.services.analyticsproviders.api.trackers.AnalyticsTracker import io.element.android.services.analyticsproviders.api.trackers.ErrorTracker +/** + * One analytics backend, such as PostHog or Sentry. + * + * Providers are driven by the analytics service rather than used directly, and only receive data once the user has consented. + */ interface AnalyticsProvider : AnalyticsTracker, ErrorTracker { /** * User friendly name. */ val name: String + /** Starts the backend, to be called when the user consents to analytics. */ fun init() + /** Stops the backend and drops any pending data, to be called when the user withdraws their consent. */ fun stop() + /** + * Starts a performance transaction, returning `null` when this provider does not support them. + * + * @param name the name the transaction is reported under. + * @param operation the kind of operation being measured. + * @param description a human readable detail shown alongside the measurement. + */ fun startTransaction(name: String, operation: String? = null, description: String? = null): AnalyticsTransaction? } diff --git a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsTransaction.kt b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsTransaction.kt index 82d337914a..f54cadbda8 100644 --- a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsTransaction.kt +++ b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsTransaction.kt @@ -9,6 +9,11 @@ package io.element.android.services.analyticsproviders.api import kotlin.time.Duration +/** + * A performance measurement in progress, which reports its timing once [finish] is called. + * + * Transactions can nest through [startChild]; see `recordChildTransaction` for a scoped alternative that always finishes the child. + */ interface AnalyticsTransaction { /** * The time elapsed since the transaction started until now if the transaction is ongoing or the time it finished. @@ -17,11 +22,17 @@ interface AnalyticsTransaction { /** * Start a child span from this transaction. + * + * @param operation the kind of operation the child measures. + * @param description a human readable detail shown alongside the measurement. */ fun startChild(operation: String, description: String? = null): AnalyticsTransaction /** * Adds extra data to the transaction. This data is not indexed, it's just listed. + * + * @param key the name of the field. + * @param value the value to attach. */ fun putExtraData(key: String, value: String) @@ -29,6 +40,9 @@ interface AnalyticsTransaction { * Similar to [putExtraData], adds extra data that *will be indexed* and can be used for filtering in the analytics portal. * * **Do not add numerical values using this function, use [putExtraData] instead.** + * + * @param key the name of the indexed field. + * @param value the value to attach. */ fun putIndexableData(key: String, value: String) @@ -44,6 +58,8 @@ interface AnalyticsTransaction { /** * Attach a throwable to the transaction, so we can know it failed. + * + * @param throwable the failure that occurred during the measured operation. */ fun attachError(throwable: Throwable) diff --git a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/trackers/AnalyticsTracker.kt b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/trackers/AnalyticsTracker.kt index 5bb63338a8..db4a8a5517 100644 --- a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/trackers/AnalyticsTracker.kt +++ b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/trackers/AnalyticsTracker.kt @@ -14,19 +14,28 @@ import im.vector.app.features.analytics.plan.Interaction import im.vector.app.features.analytics.plan.SuperProperties import im.vector.app.features.analytics.plan.UserProperties +/** + * Records analytics events; implemented by every provider and by the analytics service that fans out to them. + */ interface AnalyticsTracker { /** * Capture an Event. + * + * @param event the event to record, taken from the shared analytics plan. */ fun capture(event: VectorAnalyticsEvent) /** * Track a displayed screen. + * + * @param screen the screen that was shown. */ fun screen(screen: VectorAnalyticsScreen) /** * Update user specific properties. + * + * @param userProperties the properties to merge into the current user's profile. */ fun updateUserProperties(userProperties: UserProperties) diff --git a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/trackers/ErrorTracker.kt b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/trackers/ErrorTracker.kt index 5277493b64..94a04b842b 100644 --- a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/trackers/ErrorTracker.kt +++ b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/trackers/ErrorTracker.kt @@ -8,6 +8,14 @@ package io.element.android.services.analyticsproviders.api.trackers +/** + * Reports non-fatal errors to the crash reporting backend. + */ interface ErrorTracker { + /** + * Records an error that was handled but is still worth knowing about. + * + * @param throwable the failure to report; never pass one whose message can contain user content. + */ fun trackError(throwable: Throwable) } From c32f81f6965183ed9eaa00683411ddea8885f648 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 10:49:24 +0200 Subject: [PATCH 181/338] Revert "Keep manually revealed media visible when re-entering a room" --- .../protection/TimelineProtectionPresenter.kt | 9 ++++-- .../protection/TimelineProtectionStore.kt | 32 ------------------- .../TimelineProtectionPresenterTest.kt | 20 ------------ 3 files changed, 6 insertions(+), 55 deletions(-) delete mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionStore.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenter.kt index 6110a2c4db..22be4acb2d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenter.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateSetOf import androidx.compose.runtime.remember import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn @@ -19,6 +20,7 @@ import io.element.android.features.contentscanner.api.ContentScannerService import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.mapState import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaPreviewService import io.element.android.libraries.matrix.api.media.isPreviewEnabled import io.element.android.libraries.matrix.api.room.BaseRoom @@ -30,8 +32,9 @@ class TimelineProtectionPresenter( private val mediaPreviewService: MediaPreviewService, private val room: BaseRoom, private val contentScannerService: ContentScannerService, - private val timelineProtectionStore: TimelineProtectionStore, ) : Presenter { + private val allowedEvents = mutableStateSetOf() + @Composable override fun present(): TimelineProtectionState { val mediaPreviewValue = remember { @@ -45,7 +48,7 @@ class TimelineProtectionPresenter( if (isPreviewEnabled) { ProtectionState.RenderAll } else { - ProtectionState.RenderOnly(eventIds = timelineProtectionStore.allowedEventIds.toImmutableSet()) + ProtectionState.RenderOnly(eventIds = allowedEvents.toImmutableSet()) } } } @@ -53,7 +56,7 @@ class TimelineProtectionPresenter( fun handleEvent(event: TimelineProtectionEvent) { when (event) { is TimelineProtectionEvent.ShowContent -> { - event.eventId?.let(timelineProtectionStore::allowEvent) + allowedEvents += setOfNotNull(event.eventId) } is TimelineProtectionEvent.ValidateContent -> { contentScannerService.scan(event.mediaSources, event.validationState) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionStore.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionStore.kt deleted file mode 100644 index aa6655cb86..0000000000 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionStore.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.messages.impl.timeline.protection - -import androidx.compose.runtime.mutableStateSetOf -import androidx.compose.runtime.snapshots.SnapshotStateSet -import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.SingleIn -import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.core.EventId - -interface TimelineProtectionStore { - val allowedEventIds: SnapshotStateSet - fun allowEvent(eventId: EventId) -} - -@ContributesBinding(SessionScope::class) -@SingleIn(SessionScope::class) -class DefaultTimelineProtectionStore : TimelineProtectionStore { - private val allowedEvents = mutableStateSetOf() - - override val allowedEventIds: SnapshotStateSet = allowedEvents - - override fun allowEvent(eventId: EventId) { - allowedEvents += eventId - } -} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenterTest.kt index 161b350c96..11142cc9a2 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenterTest.kt @@ -95,24 +95,6 @@ class TimelineProtectionPresenterTest { } } - @Test - fun `present - shown content is restored when the presenter is recreated`() = runTest { - val mediaPreviewConfig = MediaPreviewConfig(mediaPreviewValue = MediaPreviewValue.Off, hideInviteAvatar = false) - val mediaPreviewService = FakeMediaPreviewService(mediaPreviewConfigFlow = MutableStateFlow(mediaPreviewConfig)) - val timelineProtectionStore = DefaultTimelineProtectionStore() - createPresenter(mediaPreviewService = mediaPreviewService, timelineProtectionStore = timelineProtectionStore).test { - val initialState = awaitItem() - assertThat(initialState.protectionState).isEqualTo(ProtectionState.RenderOnly(persistentSetOf())) - initialState.eventSink(TimelineProtectionEvent.ShowContent(eventId = AN_EVENT_ID)) - val finalState = awaitItem() - assertThat(finalState.protectionState).isEqualTo(ProtectionState.RenderOnly(persistentSetOf(AN_EVENT_ID))) - } - createPresenter(mediaPreviewService = mediaPreviewService, timelineProtectionStore = timelineProtectionStore).test { - val initialState = awaitItem() - assertThat(initialState.protectionState).isEqualTo(ProtectionState.RenderOnly(persistentSetOf(AN_EVENT_ID))) - } - } - @OptIn(ExperimentalCoroutinesApi::class) @Test fun `present - validate media scans the media source`() = runTest { @@ -152,11 +134,9 @@ class TimelineProtectionPresenterTest { room: BaseRoom = FakeBaseRoom(), mediaPreviewService: MediaPreviewService = FakeMediaPreviewService(), contentScannerService: ContentScannerService = ContentScannerService { _: List, _: ContentValidationState -> }, - timelineProtectionStore: TimelineProtectionStore = DefaultTimelineProtectionStore(), ) = TimelineProtectionPresenter( mediaPreviewService = mediaPreviewService, room = room, contentScannerService = contentScannerService, - timelineProtectionStore = timelineProtectionStore, ) } From 012d43665fc980fb6c96fde4084ae7bb1cab16f6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 11:24:04 +0200 Subject: [PATCH 182/338] Add KDoc to the `libraries/session-storage` API interfaces Document SessionStore, SessionObserver and SessionListener at the interface level and add the missing @param entries. The store doc records that sessions are ordered by last usage, which is what makes "the latest session" meaningful across the rest of the interface, and the listener doc explains that wasLastSession is the signal to clean up app-wide data too. Co-Authored-By: Claude Opus 5 (1M context) --- .../sessionstorage/api/SessionStore.kt | 20 +++++++++++++++++++ .../api/observer/SessionListener.kt | 15 ++++++++++++++ .../api/observer/SessionObserver.kt | 10 ++++++++++ 3 files changed, 45 insertions(+) diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt index fb60311418..53aa0c9516 100644 --- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt @@ -11,6 +11,11 @@ package io.element.android.libraries.sessionstorage.api import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +/** + * Persists the logged in sessions and their access tokens, so the app can restore them after a restart. + * + * Sessions are ordered by last usage, which is what makes "the latest session" meaningful across the interface. + */ interface SessionStore { /** * A flow emitting the current logged in state. @@ -28,21 +33,32 @@ interface SessionStore { * Add a new session. If other sessions exist, the new one will be set as the latest used one, and * the added session position will be set to a value higher than the other session positions. */ + /** + * @param sessionData the session to store, including its tokens. + */ suspend fun addSession(sessionData: SessionData) /** * Will update the session data matching the userId, except the value of loginTimestamp. * No op if userId is not found in DB. + * + * @param sessionData the new content of the session. */ suspend fun updateData(sessionData: SessionData) /** * Update the user profile info of the session matching the userId. + * + * @param sessionId the session to update. + * @param displayName the new display name, or `null` when the user has none. + * @param avatarUrl the new avatar, or `null` when the user has none. */ suspend fun updateUserProfile(sessionId: String, displayName: String?, avatarUrl: String?) /** * Get the session data matching the userId, or null if not found. + * + * @param sessionId the session to read. */ suspend fun getSession(sessionId: String): SessionData? @@ -63,11 +79,15 @@ interface SessionStore { /** * Set the session with [sessionId] as the latest used one. + * + * @param sessionId the session to bring to the front of the ordering. */ suspend fun setLatestSession(sessionId: String) /** * Remove the session matching the sessionId. + * + * @param sessionId the session to forget, which also discards its tokens. */ suspend fun removeSession(sessionId: String) } diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt index 8dadcafc7d..8d94af5b13 100644 --- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt @@ -8,7 +8,22 @@ package io.element.android.libraries.sessionstorage.api.observer +/** + * Reacts to sessions being created or deleted; both methods have an empty default so a listener only overrides what it cares about. + */ interface SessionListener { + /** + * Called after a session has been stored, for instance following a login. + * + * @param userId the session that was created. + */ suspend fun onSessionCreated(userId: String) {} + + /** + * Called after a session has been removed, which is where per-session data should be cleaned up. + * + * @param userId the session that was deleted. + * @param wasLastSession true when no session remains, so app-wide data can be cleaned up too. + */ suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {} } diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionObserver.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionObserver.kt index fb120acd56..08925ee688 100644 --- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionObserver.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionObserver.kt @@ -8,7 +8,17 @@ package io.element.android.libraries.sessionstorage.api.observer +/** + * Notifies interested components when a session is created or deleted, so they can set up or clean up their own per-session data. + */ interface SessionObserver { + /** + * @param listener the listener to notify from now on. + */ fun addListener(listener: SessionListener) + + /** + * @param listener the listener to stop notifying. + */ fun removeListener(listener: SessionListener) } From 13aad55af84d8104fef69b30008d04b2f81f8f17 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 11:24:04 +0200 Subject: [PATCH 183/338] Add KDoc to the `libraries/pushproviders` API interfaces Document PusherSubscriber and PushHandler at the interface level, document the PushProvider members that had none, and add the missing @param entries. Notable points: getCurrentDistributor also returns null when the stored distributor is no longer installed, rotateToken throws unless canRotateToken is true, and handleInvalid records the payload in the push history for diagnosis. Co-Authored-By: Claude Opus 5 (1M context) --- .../pushproviders/api/PushHandler.kt | 8 +++++- .../pushproviders/api/PushProvider.kt | 25 +++++++++++++++++-- .../pushproviders/api/PusherSubscriber.kt | 11 ++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushHandler.kt b/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushHandler.kt index 49b578070b..1e53575717 100644 --- a/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushHandler.kt +++ b/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushHandler.kt @@ -8,6 +8,9 @@ package io.element.android.libraries.pushproviders.api +/** + * Entry point for the pushes coming from any provider, which resolves them into notifications. + */ interface PushHandler { /** * Handle a push received from the provider. @@ -22,7 +25,10 @@ interface PushHandler { ): Boolean /** - * Handle an invalid push received from the provider. + * Handle an invalid push received from the provider, which is recorded in the push history so it can be diagnosed. + * + * @param providerInfo an identifier of the provider that sent the push, for logging and debugging purposes. + * @param data the raw payload that could not be understood. */ suspend fun handleInvalid( providerInfo: String, diff --git a/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt b/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt index 405a83b9b8..ed13d0ab37 100644 --- a/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt +++ b/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt @@ -37,33 +37,54 @@ interface PushProvider { /** * Register the pusher to the homeserver. + * + * @param matrixClient the session to register the pusher for. + * @param distributor the distributor to route the notifications through. */ suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor): Result /** - * Return the current distributor, or null if none. + * Return the current distributor as its raw stored value, or null if none. + * + * @param sessionId the session to read the distributor of. */ suspend fun getCurrentDistributorValue(sessionId: SessionId): String? /** - * Return the current distributor, or null if none. + * Return the current distributor, or null if none, which also happens when the stored one is no longer installed. + * + * @param sessionId the session to read the distributor of. */ suspend fun getCurrentDistributor(sessionId: SessionId): Distributor? /** * Unregister the pusher. + * + * @param matrixClient the session to unregister the pusher of. */ suspend fun unregister(matrixClient: MatrixClient): Result /** * To invoke when the session is deleted. + * + * @param sessionId the session that has been removed, whose provider-side data should be cleaned up. */ suspend fun onSessionDeleted(sessionId: SessionId) + /** + * The gateway and push key currently in use, for display in the troubleshooting screens; `null` when not registered. + * + * @param sessionId the session to read the configuration of. + */ suspend fun getPushConfig(sessionId: SessionId): Config? + /** Whether this provider can renew its push token on demand, which not every provider supports. */ fun canRotateToken(): Boolean + /** + * Renews the push token, which is one of the recovery steps offered when notifications stop arriving. + * Throws when [canRotateToken] is `false`. + */ suspend fun rotateToken(): Result { error("rotateToken() not implemented, you need to override this method in your implementation") } diff --git a/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PusherSubscriber.kt b/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PusherSubscriber.kt index 989170c975..e1d885fca2 100644 --- a/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PusherSubscriber.kt +++ b/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PusherSubscriber.kt @@ -11,14 +11,25 @@ package io.element.android.libraries.pushproviders.api import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.exception.ClientException +/** + * Registers and removes a pusher on the homeserver on behalf of a push provider, so every provider agrees on the parameters sent. + */ interface PusherSubscriber { /** * Register a pusher. Note that failure will be a [RegistrationFailure]. + * + * @param matrixClient the session to register the pusher for. + * @param pushKey the token identifying this device with the gateway. + * @param gateway the URL the homeserver should send the notifications to. */ suspend fun registerPusher(matrixClient: MatrixClient, pushKey: String, gateway: String): Result /** * Unregister a pusher. + * + * @param matrixClient the session to unregister the pusher of. + * @param pushKey the token of the registration to remove. + * @param gateway the gateway of the registration to remove. */ suspend fun unregisterPusher(matrixClient: MatrixClient, pushKey: String, gateway: String): Result } From d80aaf6be4959c453d1f5f77e6f800f090d68ad1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 11:24:05 +0200 Subject: [PATCH 184/338] Add KDoc to the `libraries/mediapickers` API interfaces Document PickerProvider and each of its register methods. The interface doc carries the two rules that apply to all of them: they must be called during composition rather than from a click handler, and a cancelled pick arrives as a null uri or an empty list rather than as an error. Co-Authored-By: Claude Opus 5 (1M context) --- .../mediapickers/api/PickerProvider.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/libraries/mediapickers/api/src/main/kotlin/io/element/android/libraries/mediapickers/api/PickerProvider.kt b/libraries/mediapickers/api/src/main/kotlin/io/element/android/libraries/mediapickers/api/PickerProvider.kt index 1bab693609..185b2f58a0 100644 --- a/libraries/mediapickers/api/src/main/kotlin/io/element/android/libraries/mediapickers/api/PickerProvider.kt +++ b/libraries/mediapickers/api/src/main/kotlin/io/element/android/libraries/mediapickers/api/PickerProvider.kt @@ -12,37 +12,79 @@ import android.net.Uri import androidx.activity.result.PickVisualMediaRequest import androidx.compose.runtime.Composable +/** + * Registers the system media and file pickers from Composable code, returning launchers the UI can trigger later. + * + * Every method must be called during composition, not from a click handler; a cancelled pick reports a `null` uri or an empty list. + */ interface PickerProvider { + /** + * Registers a picker for a single photo or video from the gallery. + * + * @param onResult called with the picked media and its MIME type, both `null` when the user cancelled. + */ @Composable fun registerGalleryPicker( onResult: (uri: Uri?, mimeType: String?) -> Unit ): PickerLauncher + /** + * Registers a picker restricted to a single image, used where a video would not be valid, such as for an avatar. + * + * @param onResult called with the picked image, `null` when the user cancelled. + */ @Composable fun registerGalleryImagePicker( onResult: (Uri?) -> Unit ): PickerLauncher + /** + * Registers a picker for several photos or videos at once. + * + * @param onResult called with the picked media, empty when the user cancelled. + */ @Composable fun registerGalleryMultiPicker( onResult: (uris: List) -> Unit ): PickerLauncher> + /** + * Registers a picker for a single file of any kind. + * + * @param mimeType the MIME type to filter on, `*` / `*` to accept anything. + * @param onResult called with the picked file and its MIME type, both `null` when the user cancelled. + */ @Composable fun registerFilePicker( mimeType: String, onResult: (uri: Uri?, mimeType: String?) -> Unit, ): PickerLauncher + /** + * Registers a picker for several files at once. + * + * @param mimeType the MIME type to filter on. + * @param onResult called with the picked files, empty when the user cancelled. + */ @Composable fun registerFileMultiPicker( mimeType: String, onResult: (uris: List) -> Unit, ): PickerLauncher, List> + /** + * Registers a launcher that opens the camera to take a photo. + * + * @param onResult called with the captured photo, `null` when the user cancelled or the capture failed. + */ @Composable fun registerCameraPhotoPicker(onResult: (Uri?) -> Unit): PickerLauncher + /** + * Registers a launcher that opens the camera to record a video. + * + * @param onResult called with the recorded video, `null` when the user cancelled or the capture failed. + */ @Composable fun registerCameraVideoPicker(onResult: (Uri?) -> Unit): PickerLauncher } From 2284cff25ae39fb35a3f7f676d4dcfc179d468a5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 11:43:40 +0200 Subject: [PATCH 185/338] Add KDoc to six small library API modules Cover the remaining interfaces of libraries/troubleshoot, libraries/matrixmedia, libraries/dateformatter, libraries/featureflag, libraries/workmanager and libraries/mediaviewer, along with their members and parameters. Behaviours worth recording: NotificationTroubleshootTest reports its outcome through its state flow rather than by returning, and its quickFix throws unless the test offers one; DateFormatter.format turns a null timestamp into an empty string so callers need no null check; ImageLoaderHolder keeps one loader per session because decrypting room media needs that session's client. Co-Authored-By: Claude Opus 5 (1M context) --- .../dateformatter/api/DateFormatter.kt | 10 ++++++++ .../dateformatter/api/DurationFormatter.kt | 5 ++++ .../libraries/featureflag/api/Feature.kt | 3 +++ .../featureflag/api/FeatureFlagService.kt | 3 +++ .../matrix/ui/media/ImageLoaderHolder.kt | 16 ++++++++++++ .../ui/media/InitialsAvatarBitmapGenerator.kt | 9 +++++++ .../api/local/LocalMediaFactory.kt | 11 ++++++++ .../api/local/LocalMediaRenderer.kt | 8 ++++++ .../api/util/FileExtensionExtractor.kt | 7 ++++++ .../test/NotificationTroubleshootNavigator.kt | 4 +++ .../api/test/NotificationTroubleshootTest.kt | 25 +++++++++++++++++++ .../workmanager/api/WorkManagerScheduler.kt | 11 ++++++++ .../workmanager/api/di/MetroWorkerFactory.kt | 6 +++++ 13 files changed, 118 insertions(+) diff --git a/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/DateFormatter.kt b/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/DateFormatter.kt index 1f5c88f008..84cbf5efdd 100644 --- a/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/DateFormatter.kt +++ b/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/DateFormatter.kt @@ -8,7 +8,17 @@ package io.element.android.libraries.dateformatter.api +/** + * Formats timestamps for display, in the user's locale and time zone. + */ interface DateFormatter { + /** + * Formats a timestamp according to [mode]. + * + * @param timestamp milliseconds since the epoch; a `null` value yields an empty string, so callers need no null check of their own. + * @param mode how much of the date and time to include; see [DateFormatterMode] for examples of each. + * @param useRelative true to prefer wording relative to today, such as "Today" or "This month", where the mode allows it. + */ fun format( timestamp: Long?, mode: DateFormatterMode = DateFormatterMode.Full, diff --git a/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/DurationFormatter.kt b/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/DurationFormatter.kt index ac297d10e9..712355c423 100644 --- a/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/DurationFormatter.kt +++ b/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/DurationFormatter.kt @@ -21,6 +21,11 @@ import kotlin.time.Duration * - 30 seconds → "30 seconds" */ interface DurationFormatter { + /** + * Formats the duration using its largest appropriate unit, rounding rather than listing several units. + * + * @param duration the duration to describe. + */ fun format(duration: Duration): String } diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt index 395fd04c6c..4fd8706d89 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt @@ -10,6 +10,9 @@ package io.element.android.libraries.featureflag.api import io.element.android.libraries.core.meta.BuildMeta +/** + * Describes one feature flag: how it is identified, how it is presented in the developer options, and what it defaults to. + */ interface Feature { /** * Unique key to identify the feature. diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlagService.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlagService.kt index 6a8cb2fd9d..3dc791317f 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlagService.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlagService.kt @@ -11,6 +11,9 @@ package io.element.android.libraries.featureflag.api import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first +/** + * Reads and, where a mutable provider is registered, changes the state of the feature flags. + */ interface FeatureFlagService { /** * @param feature the feature to check for diff --git a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt index 62b169744b..228fbd2cff 100644 --- a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt +++ b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt @@ -11,8 +11,24 @@ import coil3.ImageLoader import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.SessionId +/** + * Caches one Coil image loader per session, since loading Matrix media requires the session's own client to fetch and decrypt it. + */ interface ImageLoaderHolder { + /** Returns a loader that can only fetch ordinary URLs, for images that do not come from a Matrix room. */ fun get(): ImageLoader + + /** + * Returns the loader of a session, creating it on first use. + * + * @param client the session whose media the loader will fetch. + */ fun get(client: MatrixClient): ImageLoader + + /** + * Drops the cached loader of a session, to be called when that session goes away. + * + * @param sessionId the session whose loader is dropped. + */ fun remove(sessionId: SessionId) } diff --git a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/InitialsAvatarBitmapGenerator.kt b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/InitialsAvatarBitmapGenerator.kt index e6ff5d762d..f145383e87 100644 --- a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/InitialsAvatarBitmapGenerator.kt +++ b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/InitialsAvatarBitmapGenerator.kt @@ -14,6 +14,15 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData * Generates a bitmap for an initials avatar based on the provided [io.element.android.libraries.designsystem.components.avatar.AvatarData]. */ interface InitialsAvatarBitmapGenerator { + /** + * Draws the initials avatar, used where a bitmap is required rather than a Composable, such as in notifications and shortcuts. + * + * @param size the width and height of the bitmap in pixels. + * @param avatarData the name and id the initials and the background colour are derived from. + * @param useDarkTheme whether to draw the dark theme variant. + * @param fontSizePercentage the text height as a fraction of [size]. + * @return the bitmap, or `null` when it could not be drawn. + */ fun generateBitmap( size: Int, avatarData: AvatarData, diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt index 383f985c4f..5a218f9e1e 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaFactory.kt @@ -12,9 +12,15 @@ import android.net.Uri import io.element.android.libraries.matrix.api.media.MediaFile import io.element.android.libraries.mediaviewer.api.MediaInfo +/** + * Builds the [LocalMedia] the media viewer renders, from either a downloaded Matrix media file or an arbitrary content uri. + */ interface LocalMediaFactory { /** * This method will create a [LocalMedia] with the given [MediaFile] and [MediaInfo]. + * + * @param mediaFile the file already downloaded from the homeserver. + * @param mediaInfo what is known about the media from the event that carried it. */ fun createFromMediaFile( mediaFile: MediaFile, @@ -24,6 +30,11 @@ interface LocalMediaFactory { /** * This method will create a [LocalMedia] with the given mimeType, name and formattedFileSize * If any of those params are null, it'll try to read them from the content. + * + * @param uri the content to display, for instance one just picked by the user. + * @param mimeType the MIME type, or `null` to read it from the content. + * @param name the file name, or `null` to read it from the content. + * @param formattedFileSize the size to display, or `null` to read it from the content. */ fun createFromUri( uri: Uri, diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaRenderer.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaRenderer.kt index fb7f26101e..5bc84919be 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaRenderer.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaRenderer.kt @@ -10,7 +10,15 @@ package io.element.android.libraries.mediaviewer.api.local import androidx.compose.runtime.Composable +/** + * Renders a media file held on the device, picking the right player or viewer for its type. + */ interface LocalMediaRenderer { + /** + * Draws the media, filling the space it is given. + * + * @param localMedia the file to display, along with what is known about its type. + */ @Composable fun Render(localMedia: LocalMedia) } diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt index 094b4513d8..e29188e984 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/util/FileExtensionExtractor.kt @@ -8,6 +8,13 @@ package io.element.android.libraries.mediaviewer.api.util +/** + * Extracts the extension of a file name, so it can be shown to the user and used to pick an icon. + */ interface FileExtensionExtractor { + /** + * @param name the file name to inspect. + * @return the extension without its dot, or an empty string when the name has none. + */ fun extractFromName(name: String): String } diff --git a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt index a4be22e70b..69a0561d25 100644 --- a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt +++ b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt @@ -8,6 +8,10 @@ package io.element.android.libraries.troubleshoot.api.test +/** + * Lets a troubleshooting test's quick fix send the user to another screen, without the test itself knowing about navigation. + */ interface NotificationTroubleshootNavigator { + /** Opens the blocked users screen, so the user can unblock someone whose messages were being filtered out. */ fun navigateToBlockedUsers() } diff --git a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootTest.kt b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootTest.kt index 27f3bcf8ed..98898ad483 100644 --- a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootTest.kt +++ b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootTest.kt @@ -20,11 +20,36 @@ import kotlinx.coroutines.flow.StateFlow * the component they're injected into is bound to [SessionScope] and so should these (https://github.com/ZacSweers/metro/issues/1932). */ interface NotificationTroubleshootTest { + /** Position of this test in the list shown to the user; tests run in ascending order. */ val order: Int + + /** The current state of this test, which the UI observes to show it as idle, running, successful or failed. */ val state: StateFlow + + /** + * Whether this test applies to the current setup, so that irrelevant tests are hidden rather than shown as failing. + * + * @param data the current push provider and distributor, used to decide relevance. + */ fun isRelevant(data: TestFilterData): Boolean = true + + /** + * Runs the test, reporting its outcome through [state] rather than by returning or throwing. + * + * @param coroutineScope the scope to launch any work that must outlive the call itself. + */ suspend fun run(coroutineScope: CoroutineScope) + + /** Returns the test to its idle state so it can be run again. */ suspend fun reset() + + /** + * Attempts to fix what this test detected, for the tests that can offer it. Throws by default, so check the state's quick fix + * flag before calling. + * + * @param coroutineScope the scope to launch any work that must outlive the call itself. + * @param navigator used by the fixes that have to send the user to another screen. + */ suspend fun quickFix( coroutineScope: CoroutineScope, navigator: NotificationTroubleshootNavigator, diff --git a/libraries/workmanager/api/src/main/kotlin/io/element/android/libraries/workmanager/api/WorkManagerScheduler.kt b/libraries/workmanager/api/src/main/kotlin/io/element/android/libraries/workmanager/api/WorkManagerScheduler.kt index a616e01573..b4c2950cd4 100644 --- a/libraries/workmanager/api/src/main/kotlin/io/element/android/libraries/workmanager/api/WorkManagerScheduler.kt +++ b/libraries/workmanager/api/src/main/kotlin/io/element/android/libraries/workmanager/api/WorkManagerScheduler.kt @@ -10,20 +10,31 @@ package io.element.android.libraries.workmanager.api import io.element.android.libraries.matrix.api.core.SessionId +/** + * Schedules background work, wrapping `WorkManager` so the rest of the app does not depend on it directly. + */ interface WorkManagerScheduler { /** * Submits a new work request built from [workManagerRequestBuilder] to run in `WorkManager`. + * + * @param workManagerRequestBuilder describes the work to schedule. */ suspend fun submit(workManagerRequestBuilder: WorkManagerRequestBuilder) /** * Checks if there are any pending requests scheduled for the provided [sessionId] and [requestType]. + * + * @param sessionId the session the work belongs to. + * @param requestType the kind of work to look for. */ fun hasPendingWork(sessionId: SessionId, requestType: WorkManagerRequestType): Boolean /** * Cancel pending work requests for the session [SessionId]. * If [requestType] is provided, it will only cancel requests for that type, otherwise it will cancel all requests. + * + * @param sessionId the session whose work is cancelled. + * @param requestType the kind of work to cancel, or `null` to cancel every kind. */ fun cancel(sessionId: SessionId, requestType: WorkManagerRequestType? = null) } diff --git a/libraries/workmanager/api/src/main/kotlin/io/element/android/libraries/workmanager/api/di/MetroWorkerFactory.kt b/libraries/workmanager/api/src/main/kotlin/io/element/android/libraries/workmanager/api/di/MetroWorkerFactory.kt index 086d3baafa..d97f66f966 100644 --- a/libraries/workmanager/api/src/main/kotlin/io/element/android/libraries/workmanager/api/di/MetroWorkerFactory.kt +++ b/libraries/workmanager/api/src/main/kotlin/io/element/android/libraries/workmanager/api/di/MetroWorkerFactory.kt @@ -28,7 +28,13 @@ class MetroWorkerFactory( return workerProviders[Class.forName(workerClassName).kotlin]?.create(workerParameters) } + /** + * Creates one worker type with its dependencies injected, since `WorkManager` instantiates workers itself and cannot use the graph. + */ interface WorkerInstanceFactory { + /** + * @param params the parameters `WorkManager` built the worker with. + */ fun create(params: WorkerParameters): T } } From 57658640e1a5116ff9b676f812518ab13f3167b6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 11:48:58 +0200 Subject: [PATCH 186/338] Add KDoc to the `features/location` API interfaces Document LocationService, StaticMapUrlBuilder, TileServerStyleUriBuilder and the members of ActiveLiveLocationShareManager that had none. The two URL builders now say what happens when no map provider is configured for the build, which is the case LocationService.isServiceAvailable exists to guard. Co-Authored-By: Claude Opus 5 (1M context) --- .../android/features/location/api/LocationService.kt | 4 ++++ .../location/api/internal/StaticMapUrlBuilder.kt | 12 ++++++++++++ .../api/internal/TileServerStyleUriBuilder.kt | 6 ++++++ .../api/live/ActiveLiveLocationShareManager.kt | 10 ++++++++++ 4 files changed, 32 insertions(+) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/LocationService.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/LocationService.kt index 22049c9548..41c58c396f 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/LocationService.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/LocationService.kt @@ -8,6 +8,10 @@ package io.element.android.features.location.api +/** + * Tells whether the location features can be used at all, which depends on the build having a map provider configured. + */ interface LocationService { + /** Whether location sharing and map rendering are available; `false` disables the whole feature in the UI. */ fun isServiceAvailable(): Boolean } diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt index 6636976828..c03fb8a838 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt @@ -12,6 +12,17 @@ package io.element.android.features.location.api.internal * Builds an URL for a 3rd party service provider static maps API. */ interface StaticMapUrlBuilder { + /** + * Builds the URL of a map image centred on the given coordinates. + * + * @param lat latitude of the centre of the map. + * @param lon longitude of the centre of the map. + * @param zoom zoom level to render at. + * @param darkMode whether to request the dark map style. + * @param width width of the image in density independent pixels. + * @param height height of the image in density independent pixels. + * @param density screen density, used to request a matching pixel size. + */ fun build( lat: Double, lon: Double, @@ -22,6 +33,7 @@ interface StaticMapUrlBuilder { density: Float, ): String + /** Whether a map provider is configured for this build; the URLs are unusable when it is not. */ fun isServiceAvailable(): Boolean } diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt index 762d580f8e..08a40d2a7b 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt @@ -18,6 +18,12 @@ import io.element.android.compound.theme.ElementTheme * Used for rendering dynamic maps. */ interface TileServerStyleUriBuilder { + /** + * Builds the style URI the map view loads its tiles from. + * + * @param customMapStyleUrl the style advertised by the homeserver, or `null` to use the one built into the app. + * @param darkMode whether to request the dark map style. + */ fun build( customMapStyleUrl: String?, darkMode: Boolean, diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt index cd6b8731c1..74c9089c30 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt @@ -12,6 +12,9 @@ import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.coroutines.flow.StateFlow import kotlin.time.Duration +/** + * Owns the live location shares running on this device, across every room, and the foreground service that feeds them GPS updates. + */ interface ActiveLiveLocationShareManager { /** All rooms currently sharing live location on this device. */ val sharingRoomIds: StateFlow> @@ -28,6 +31,10 @@ interface ActiveLiveLocationShareManager { * Calls room.startLiveLocationShare() on the SDK, registers the share, * and starts the foreground GPS service if not already running. */ + /** + * @param roomId the room to start sharing the location in. + * @param duration how long the share should last before it expires on its own. + */ suspend fun startShare(roomId: RoomId, duration: Duration): Result /** @@ -35,6 +42,9 @@ interface ActiveLiveLocationShareManager { * Calls room.stopLiveLocationShare() on the SDK, removes the share, * and stops the foreground service if no shares remain. */ + /** + * @param roomId the room to stop sharing the location in. + */ suspend fun stopShare(roomId: RoomId): Result } From c4c7dac9cf8d9c66535c67dc1ae1cbf6e551f5f7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 11:48:59 +0200 Subject: [PATCH 187/338] Add KDoc to the `features/rageshake` API interfaces Document RageshakeFeatureAvailability, LogFilesRemover and the BugReporter interface, and complete the docs of its remaining members. The BugReporter doc records that progress and the outcome arrive through the listener passed to sendBugReport rather than through a return value or an exception. Co-Authored-By: Claude Opus 5 (1M context) --- .../rageshake/api/RageshakeFeatureAvailability.kt | 4 ++++ .../features/rageshake/api/logs/LogFilesRemover.kt | 3 +++ .../features/rageshake/api/reporter/BugReporter.kt | 13 +++++++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/RageshakeFeatureAvailability.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/RageshakeFeatureAvailability.kt index bc72e7021b..c4205c1575 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/RageshakeFeatureAvailability.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/RageshakeFeatureAvailability.kt @@ -10,6 +10,10 @@ package io.element.android.features.rageshake.api import kotlinx.coroutines.flow.Flow +/** + * Tells whether bug reporting is available, which an enterprise deployment can turn off. + */ fun interface RageshakeFeatureAvailability { + /** Whether the rageshake and bug report entry points should be offered to the user. */ fun isAvailable(): Flow } diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/logs/LogFilesRemover.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/logs/LogFilesRemover.kt index f8eee31675..a7b2961ab6 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/logs/LogFilesRemover.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/logs/LogFilesRemover.kt @@ -10,6 +10,9 @@ package io.element.android.features.rageshake.api.logs import java.io.File +/** + * Deletes the log files the app writes, used to keep them from growing without bound and to clear them on request. + */ interface LogFilesRemover { /** * Perform the log files removal. diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt index 631a51a686..c1611f30b5 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt @@ -10,6 +10,11 @@ package io.element.android.features.rageshake.api.reporter import java.io.File +/** + * Uploads bug reports, including the log files the app has been writing. + * + * Progress and the outcome are reported through the listener passed to [sendBugReport] rather than by returning or throwing. + */ interface BugReporter { /** * Send a bug report. @@ -35,17 +40,21 @@ interface BugReporter { ) /** - * Provide the log directory. + * Provide the log directory, i.e. where the rotating log files are written. */ fun logDirectory(): File /** - * Set the current tracing log level. + * Set the current tracing log level, so it can be reported alongside the bug. + * + * @param logLevel the log level currently in effect. */ fun setCurrentTracingLogLevel(logLevel: String) /** * Save the logcat. + * + * @return the file the logcat was written to, or `null` when it could not be captured. */ fun saveLogCat(): File? } From b683c62d8f62e4b3c58f680b0461bde8556f0d9e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 11:50:13 +0200 Subject: [PATCH 188/338] Add KDoc to the `services/appnavstate` API interfaces Document the members of AppNavigationStateService and AppForegroundStateService that had none, and add the missing @param entries. The recurring `owner` parameter is now explained, from DefaultAppNavigationStateService: the navigation state is a stack, pushing a level without its parent logs an error and does nothing, and a leave call whose owner does not match the current level is silently ignored so that a node which has already been replaced cannot pop someone else's state. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/AppForegroundStateService.kt | 16 +++++++- .../api/AppNavigationStateService.kt | 41 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppForegroundStateService.kt b/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppForegroundStateService.kt index 33c2ee133b..8bfa747583 100644 --- a/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppForegroundStateService.kt +++ b/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppForegroundStateService.kt @@ -34,27 +34,41 @@ interface AppForegroundStateService { */ val isSyncingNotificationEvent: StateFlow + /** + * Updates to whether the app is publishing a live location will be emitted here. + */ val isSharingLiveLocation: StateFlow /** - * Start observing the foreground state. + * Start observing the foreground state; to be called once when the app process starts. */ fun startObservingForeground() /** * Update the in-call state. + * + * @param isInCall true while the user is in a call, which keeps the session syncing even in the background. */ fun updateIsInCallState(isInCall: Boolean) /** * Update the 'has ringing call' state. + * + * @param hasRingingCall true while an incoming call is ringing and has not yet been answered or declined. */ fun updateHasRingingCall(hasRingingCall: Boolean) /** * Update the active state for the syncing notification event flow. + * + * @param isSyncingNotificationEvent true while a push is being resolved, so the sync is not stopped mid-way. */ fun updateIsSyncingNotificationEvent(isSyncingNotificationEvent: Boolean) + /** + * Update the live location sharing state. + * + * @param isSharingLiveLocation true while at least one live location share is active on this device. + */ fun updateIsSharingLiveLocation(isSharingLiveLocation: Boolean) } diff --git a/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppNavigationStateService.kt b/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppNavigationStateService.kt index cf32bb0bf4..d6373f44c3 100644 --- a/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppNavigationStateService.kt +++ b/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppNavigationStateService.kt @@ -17,14 +17,55 @@ import kotlinx.coroutines.flow.StateFlow * A service that tracks the navigation and foreground states of the app. */ interface AppNavigationStateService { + /** + * Where the user currently is, as a stack that deepens from root to session, room and thread. + * Used by the components that need to know what is on screen, notably to suppress the notifications of the room being read. + */ val appNavigationState: StateFlow + /** + * Pushes a session onto the navigation state. + * + * @param owner identifies the navigation node reporting this, so that a later leave call can be matched to it. + * @param sessionId the session that was opened. + */ fun onNavigateToSession(owner: String, sessionId: SessionId) + + /** + * Pops back to the root. Does nothing when [owner] is not the owner of the current state, which is how out-of-order + * calls from a node that has already been replaced are ignored. + * + * @param owner the same value passed to [onNavigateToSession]. + */ fun onLeavingSession(owner: String) + /** + * Pushes a room onto the navigation state. Logs an error and does nothing unless a session was pushed first. + * + * @param owner identifies the navigation node reporting this. + * @param roomId the room that was opened. + */ fun onNavigateToRoom(owner: String, roomId: RoomId) + + /** + * Pops back to the parent session; as with [onLeavingSession], a mismatched [owner] makes this a no-op. + * + * @param owner the same value passed to [onNavigateToRoom]. + */ fun onLeavingRoom(owner: String) + /** + * Pushes a thread onto the navigation state. Logs an error and does nothing unless a room was pushed first. + * + * @param owner identifies the navigation node reporting this. + * @param threadId the thread that was opened. + */ fun onNavigateToThread(owner: String, threadId: ThreadId) + + /** + * Pops back to the parent room; as with [onLeavingSession], a mismatched [owner] makes this a no-op. + * + * @param owner the same value passed to [onNavigateToThread]. + */ fun onLeavingThread(owner: String) } From d713017afe40b649809459d29e2c6a38be92931c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 11:51:32 +0200 Subject: [PATCH 189/338] Add KDoc to the `libraries/cryptography` API interfaces Document the members of SecretKeyRepository and EncryptionDecryptionService. The caveat worth having in the API doc comes from KeyStoreSecretKeyRepository: requiresUserAuthentication only takes effect when the key is created, so an existing key keeps whatever protection it was created with. Co-Authored-By: Claude Opus 5 (1M context) --- .../api/EncryptionDecryptionService.kt | 27 +++++++++++++++++++ .../cryptography/api/SecretKeyRepository.kt | 7 +++++ 2 files changed, 34 insertions(+) diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionDecryptionService.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionDecryptionService.kt index fc882bc54b..1e2866a20f 100644 --- a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionDecryptionService.kt +++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionDecryptionService.kt @@ -15,8 +15,35 @@ import javax.crypto.SecretKey * Simple service to provide encryption and decryption operations. */ interface EncryptionDecryptionService { + /** + * Creates a cipher ready to encrypt, for the callers that need the cipher itself, such as to bind it to a biometric prompt. + * + * @param key the key to encrypt with. + */ fun createEncryptionCipher(key: SecretKey): Cipher + + /** + * Creates a cipher ready to decrypt, for the callers that need the cipher itself. + * + * @param key the key to decrypt with. + * @param initializationVector the vector produced when the data was encrypted. + */ fun createDecryptionCipher(key: SecretKey, initializationVector: ByteArray): Cipher + + /** + * Encrypts data in one call. + * + * @param key the key to encrypt with. + * @param input the plaintext bytes. + * @return the ciphertext along with the initialization vector needed to decrypt it. + */ fun encrypt(key: SecretKey, input: ByteArray): EncryptionResult + + /** + * Decrypts data in one call. + * + * @param key the key the data was encrypted with. + * @param encryptionResult the ciphertext and its initialization vector, as returned by [encrypt]. + */ fun decrypt(key: SecretKey, encryptionResult: EncryptionResult): ByteArray } diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyRepository.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyRepository.kt index b210664d9f..c68f3170b9 100644 --- a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyRepository.kt +++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyRepository.kt @@ -16,10 +16,17 @@ import javax.crypto.SecretKey * Implementation should be able to store the generated key securely. */ interface SecretKeyRepository { + /** + * Whether a key exists for the given alias, re-emitted when it is created or deleted. + * + * @param alias the alias to watch. + */ fun hasKey(alias: String): Flow /** * Get or create a secret key for a given alias. + * Note that [requiresUserAuthentication] only applies when the key is created: an existing key keeps the protection it was created with. + * * @param alias the alias to use * @param requiresUserAuthentication true if the key should be protected by user authentication */ From fa123d941b8542fb8458840495547d9b9c3af157 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 11:51:32 +0200 Subject: [PATCH 190/338] Add KDoc to the `features/invite` API interfaces Document AcceptDeclineInviteEvents, AcceptDeclineInviteView and the SeenInvitesStore interface. The view doc explains why it is a shared component rather than part of one screen: it is embedded everywhere an invitation can be acted on, so the confirmation dialogs behave identically in the invite list and in a room preview. Co-Authored-By: Claude Opus 5 (1M context) --- .../android/features/invite/api/SeenInvitesStore.kt | 5 ++++- .../api/acceptdecline/AcceptDeclineInviteEvents.kt | 3 +++ .../api/acceptdecline/AcceptDeclineInviteView.kt | 13 +++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/SeenInvitesStore.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/SeenInvitesStore.kt index 046303ca4f..d8ab7c71c8 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/SeenInvitesStore.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/SeenInvitesStore.kt @@ -11,6 +11,9 @@ package io.element.android.features.invite.api import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.coroutines.flow.Flow +/** + * Remembers which invitations the user has already looked at, so that only genuinely new ones are highlighted in the room list. + */ interface SeenInvitesStore { /** * Returns a flow of seen room IDs of invitation. @@ -32,7 +35,7 @@ interface SeenInvitesStore { suspend fun markAsUnSeen(roomId: RoomId) /** - * Delete the store. + * Delete the store, so every invitation counts as unseen again. */ suspend fun clear() } diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteEvents.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteEvents.kt index 2caaacab2b..5691b19ef7 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteEvents.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteEvents.kt @@ -10,6 +10,9 @@ package io.element.android.features.invite.api.acceptdecline import io.element.android.features.invite.api.InviteData +/** + * Events sent to the accept/decline invite presenter, from whichever screen is showing the invitation. + */ interface AcceptDeclineInviteEvents { data class AcceptInvite(val invite: InviteData) : AcceptDeclineInviteEvents data class DeclineInvite(val invite: InviteData, val blockUser: Boolean, val shouldConfirm: Boolean) : AcceptDeclineInviteEvents diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteView.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteView.kt index b6eec03a6f..6e9d78082b 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteView.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteView.kt @@ -12,7 +12,20 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import io.element.android.libraries.matrix.api.core.RoomId +/** + * Renders the confirmation dialogs and error states of accepting or declining an invitation; it draws nothing until such an event is sent. + * + * Embedded by every screen that can act on an invitation, so the behaviour stays identical between the invite list and a room preview. + */ fun interface AcceptDeclineInviteView { + /** + * Draws whichever dialog the current state calls for. + * + * @param state the state produced by the accept/decline presenter. + * @param onAcceptInviteSuccess called once the room has been joined, so the host screen can navigate into it. + * @param onDeclineInviteSuccess called once the invitation has been declined. + * @param modifier layout modifier for the container. + */ @Composable fun Render( state: AcceptDeclineInviteState, From 1b4d6c07fafbfc7edfffb6cf865e070e0031da8d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 11:53:37 +0200 Subject: [PATCH 191/338] Add KDoc to the authentication, logout, preferences and share API interfaces Cover the remaining interfaces of features/login, features/logout, features/preferences and features/share, along with their members and parameters. Points recorded here: AccountProviderAccessControl returns true on a build with no enterprise restriction, ExtraDeveloperOptionsRenderer is bound to a no-op in the standard build, DirectLogoutView draws nothing until a logout event is sent, and CacheService exists because clearing a cache destroys the client and the app has to react. Co-Authored-By: Claude Opus 5 (1M context) --- .../android/features/login/api/LoginIntentResolver.kt | 7 +++++++ .../api/accesscontrol/AccountProviderAccessControl.kt | 8 ++++++++ .../features/logout/api/direct/DirectLogoutView.kt | 10 ++++++++++ .../android/features/preferences/api/CacheService.kt | 3 +++ .../preferences/api/ExtraDeveloperOptionsRenderer.kt | 5 +++++ .../element/android/features/share/api/OnSharedData.kt | 3 +++ .../android/features/share/api/ShareIntentHandler.kt | 4 ++++ 7 files changed, 40 insertions(+) diff --git a/features/login/api/src/main/kotlin/io/element/android/features/login/api/LoginIntentResolver.kt b/features/login/api/src/main/kotlin/io/element/android/features/login/api/LoginIntentResolver.kt index 79cfc35fbd..b2b7378be4 100644 --- a/features/login/api/src/main/kotlin/io/element/android/features/login/api/LoginIntentResolver.kt +++ b/features/login/api/src/main/kotlin/io/element/android/features/login/api/LoginIntentResolver.kt @@ -8,6 +8,13 @@ package io.element.android.features.login.api +/** + * Extracts the login parameters from a link, so that a deployment can hand out a URL that pre-fills the sign-in screen. + */ interface LoginIntentResolver { + /** + * @param uriString the link the app was opened with. + * @return the account provider and login hint to pre-fill, or `null` when the link is not a login link. + */ fun parse(uriString: String): LoginParams? } diff --git a/features/login/api/src/main/kotlin/io/element/android/features/login/api/accesscontrol/AccountProviderAccessControl.kt b/features/login/api/src/main/kotlin/io/element/android/features/login/api/accesscontrol/AccountProviderAccessControl.kt index fc804c14ff..0c35c1655f 100644 --- a/features/login/api/src/main/kotlin/io/element/android/features/login/api/accesscontrol/AccountProviderAccessControl.kt +++ b/features/login/api/src/main/kotlin/io/element/android/features/login/api/accesscontrol/AccountProviderAccessControl.kt @@ -8,6 +8,14 @@ package io.element.android.features.login.api.accesscontrol +/** + * Enforces the restriction an enterprise deployment can put on which account providers the user may sign in to. + */ interface AccountProviderAccessControl { + /** + * Whether sign-in to this provider is permitted; `true` on a build with no such restriction. + * + * @param accountProviderUrl the server the user is trying to use. + */ suspend fun isAllowedToConnectToAccountProvider(accountProviderUrl: String): Boolean } diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutView.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutView.kt index 7841b85a5a..affd2be94d 100644 --- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutView.kt +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutView.kt @@ -10,7 +10,17 @@ package io.element.android.features.logout.api.direct import androidx.compose.runtime.Composable +/** + * Renders the confirmation dialogs of logging out directly, in particular the warning shown when the user would lose their room keys. + * + * It draws nothing until the host screen sends a logout event. + */ fun interface DirectLogoutView { + /** + * Draws whichever dialog the current state calls for. + * + * @param state the state produced by the direct logout presenter. + */ @Composable fun Render(state: DirectLogoutState) } diff --git a/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/CacheService.kt b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/CacheService.kt index 82fcd1053f..9dc3eb1877 100644 --- a/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/CacheService.kt +++ b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/CacheService.kt @@ -11,6 +11,9 @@ package io.element.android.features.preferences.api import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.coroutines.flow.Flow +/** + * Announces that a session's cache has been cleared, since clearing it destroys the client and the app has to react. + */ interface CacheService { /** * A flow of [SessionId], can let the app to know when the diff --git a/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/ExtraDeveloperOptionsRenderer.kt b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/ExtraDeveloperOptionsRenderer.kt index 7d7eec9d8b..3acb2c9057 100644 --- a/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/ExtraDeveloperOptionsRenderer.kt +++ b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/ExtraDeveloperOptionsRenderer.kt @@ -14,6 +14,11 @@ import androidx.compose.ui.Modifier * Component for rendering extra developer options in the preferences screen. */ interface ExtraDeveloperOptionsRenderer { + /** + * Draws the extra options. The standard build binds a no-op implementation, so only the builds that actually add options provide a real one. + * + * @param modifier layout modifier for the container. + */ @Composable fun Render(modifier: Modifier) } diff --git a/features/share/api/src/main/kotlin/io/element/android/features/share/api/OnSharedData.kt b/features/share/api/src/main/kotlin/io/element/android/features/share/api/OnSharedData.kt index d7985e8b06..f635cc0cce 100644 --- a/features/share/api/src/main/kotlin/io/element/android/features/share/api/OnSharedData.kt +++ b/features/share/api/src/main/kotlin/io/element/android/features/share/api/OnSharedData.kt @@ -11,5 +11,8 @@ package io.element.android.features.share.api * Post-processing to be done once a [ShareIntentData] has been consumed. */ fun interface OnSharedData { + /** + * @param data the shared content that has just been handled, so any temporary copy of it can be released. + */ operator fun invoke(data: ShareIntentData) } diff --git a/features/share/api/src/main/kotlin/io/element/android/features/share/api/ShareIntentHandler.kt b/features/share/api/src/main/kotlin/io/element/android/features/share/api/ShareIntentHandler.kt index d689c57398..d85ae9792a 100644 --- a/features/share/api/src/main/kotlin/io/element/android/features/share/api/ShareIntentHandler.kt +++ b/features/share/api/src/main/kotlin/io/element/android/features/share/api/ShareIntentHandler.kt @@ -9,10 +9,14 @@ package io.element.android.features.share.api import android.content.Intent +/** + * Recognises the intents another app uses to share content into this one, and resolves what was shared. + */ interface ShareIntentHandler { /** * This methods aims to handle incoming share intents and parse its data. * + * @param intent the intent the app was started with. * @return the [ShareIntentData] if it could be resolved, or null. */ fun handleIncomingShareIntent( From 9822b3d4f75c4bbba56424b134b8da21698362da Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 12:00:42 +0200 Subject: [PATCH 192/338] Add KDoc to the single-interface feature API modules Cover the last remaining interfaces across features/announcement, cachecleaner, call, contentscanner, createroom, knockrequests, lockscreen, rolesandpermissions, startchat, userprofile and viewfolder, with their members and parameters. Grouped into one commit rather than eleven near-empty ones, per the AGENTS.md rule about tiny commits. This completes every features/* api module. Co-Authored-By: Claude Opus 5 (1M context) --- .../announcement/api/AnnouncementService.kt | 19 +++++++++++++++++++ .../features/cachecleaner/api/CacheCleaner.kt | 3 +++ .../features/call/api/CurrentCallService.kt | 3 +++ .../api/ContentScannerService.kt | 6 ++++++ .../createroom/api/CreateRoomEntryPoint.kt | 12 ++++++++++++ .../api/banner/KnockRequestsBannerRenderer.kt | 9 +++++++++ .../lockscreen/api/LockScreenService.kt | 3 +++ .../api/ChangeRoomMemberRolesEntryPoint.kt | 10 ++++++++++ .../features/startchat/api/StartDMAction.kt | 5 +++++ .../api/UserProfilePresenterFactory.kt | 6 ++++++ .../features/viewfolder/api/TextFileViewer.kt | 9 +++++++++ 11 files changed, 85 insertions(+) diff --git a/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/AnnouncementService.kt b/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/AnnouncementService.kt index 42c66aa29d..23c748d6da 100644 --- a/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/AnnouncementService.kt +++ b/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/AnnouncementService.kt @@ -12,15 +12,34 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import kotlinx.coroutines.flow.Flow +/** + * Queues the full-screen announcements shown to the user once, such as the notice about a new feature. + * + * A dismissed announcement is remembered so it is not shown again. + */ interface AnnouncementService { + /** + * Queues an announcement, unless the user has already dismissed it. + * + * @param announcement the announcement to show. + */ suspend fun showAnnouncement(announcement: Announcement) + /** + * Records that the user dismissed an announcement, so it is not queued again. + * + * @param announcement the announcement that was dismissed. + */ suspend fun onAnnouncementDismissed(announcement: Announcement) + /** The announcements still waiting to be shown, in the order they should appear. */ fun announcementsToShowFlow(): Flow> /** * Use this composable to render the announcement UI in Fullscreen. + * Draws nothing while there is no announcement to show. + * + * @param modifier layout modifier for the container. */ @Composable fun Render( diff --git a/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleaner.kt b/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleaner.kt index b5a5396b16..48b464cb21 100644 --- a/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleaner.kt +++ b/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleaner.kt @@ -8,6 +8,9 @@ package io.element.android.features.cachecleaner.api +/** + * Removes the temporary files holding decrypted content, so plaintext media does not linger on disk. + */ interface CacheCleaner { /** * Clear the cache subdirs holding temporarily decrypted content (such as media and voice messages). diff --git a/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallService.kt b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallService.kt index 25720596ba..20ff7ef4d8 100644 --- a/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallService.kt +++ b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallService.kt @@ -10,6 +10,9 @@ package io.element.android.features.call.api import kotlinx.coroutines.flow.StateFlow +/** + * Tracks the call the user is currently in on this device, so unrelated parts of the app can react to it. + */ interface CurrentCallService { /** * The current call state flow, which will be updated when the active call changes. diff --git a/features/contentscanner/api/src/main/kotlin/io/element/android/features/contentscanner/api/ContentScannerService.kt b/features/contentscanner/api/src/main/kotlin/io/element/android/features/contentscanner/api/ContentScannerService.kt index 68f7af28f1..3649768eb7 100644 --- a/features/contentscanner/api/src/main/kotlin/io/element/android/features/contentscanner/api/ContentScannerService.kt +++ b/features/contentscanner/api/src/main/kotlin/io/element/android/features/contentscanner/api/ContentScannerService.kt @@ -18,5 +18,11 @@ import io.element.android.libraries.matrix.ui.media.contentvalidation.ContentVal * This will only process media when a [ContentScanner] is provided when building the client in Pro. */ fun interface ContentScannerService { + /** + * Starts scanning the given media, reporting the verdict of each one through [contentValidationState] rather than by returning. + * + * @param mediaSources the media to scan. + * @param contentValidationState where the outcome of each scan is published for the UI to observe. + */ fun scan(mediaSources: List, contentValidationState: ContentValidationState) } diff --git a/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt b/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt index 271fdb2332..3128b8073a 100644 --- a/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt +++ b/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt @@ -15,9 +15,21 @@ import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.matrix.api.core.RoomId interface CreateRoomEntryPoint : FeatureEntryPoint { + /** + * Configures the create-room flow before it is started, since it serves rooms and spaces from the same screens. + */ interface Builder { + /** + * @param isSpace true to create a space rather than a room. + */ fun setIsSpace(isSpace: Boolean): Builder + + /** + * @param parentSpaceId the space the new room should be added to once created. + */ fun setParentSpace(parentSpaceId: RoomId): Builder + + /** Builds the node with the options configured so far. */ fun build(): Node } diff --git a/features/knockrequests/api/src/main/kotlin/io/element/android/features/knockrequests/api/banner/KnockRequestsBannerRenderer.kt b/features/knockrequests/api/src/main/kotlin/io/element/android/features/knockrequests/api/banner/KnockRequestsBannerRenderer.kt index 34061db335..e6be68b95a 100644 --- a/features/knockrequests/api/src/main/kotlin/io/element/android/features/knockrequests/api/banner/KnockRequestsBannerRenderer.kt +++ b/features/knockrequests/api/src/main/kotlin/io/element/android/features/knockrequests/api/banner/KnockRequestsBannerRenderer.kt @@ -11,7 +11,16 @@ package io.element.android.features.knockrequests.api.banner import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +/** + * Renders the banner shown at the top of a room when people are waiting to join it. + */ interface KnockRequestsBannerRenderer { + /** + * Draws the banner, or nothing when there is no pending request or the user cannot moderate the room. + * + * @param modifier layout modifier for the container. + * @param onViewRequestsClick called when the user asks to see the full list of requests. + */ @Composable fun View(modifier: Modifier, onViewRequestsClick: () -> Unit) } diff --git a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt index bd644e69e9..59c148660e 100644 --- a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt +++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt @@ -17,6 +17,9 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +/** + * Exposes the app lock state: whether a PIN or biometric lock is set up, whether it is mandatory, and whether the app is locked right now. + */ interface LockScreenService { /** * The current lock state of the app. diff --git a/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt index 7cfdfefae0..4f823387f3 100644 --- a/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt +++ b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt @@ -22,8 +22,18 @@ fun interface ChangeRoomMemberRolesEntryPoint : FeatureEntryPoint { listType: ChangeRoomMemberRolesListType, ): Node + /** + * Lets a caller outside this feature wait for the role change to finish, which is needed when leaving a room depends on it. + */ interface NodeProxy { + /** The room whose member roles are being changed. */ val roomId: RoomId + + /** + * Suspends until the user finishes or abandons the flow. + * + * @return true when roles were actually changed, false when the user backed out. + */ suspend fun waitForCompletion(): Boolean } } diff --git a/features/startchat/api/src/main/kotlin/io/element/android/features/startchat/api/StartDMAction.kt b/features/startchat/api/src/main/kotlin/io/element/android/features/startchat/api/StartDMAction.kt index 6aa2ad5d98..2e5a92f18e 100644 --- a/features/startchat/api/src/main/kotlin/io/element/android/features/startchat/api/StartDMAction.kt +++ b/features/startchat/api/src/main/kotlin/io/element/android/features/startchat/api/StartDMAction.kt @@ -13,6 +13,11 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.user.MatrixUser +/** + * Opens a direct message with a user, reusing the existing room when there is one. + * + * Progress is reported into the state passed in, so the caller only has to render it. + */ interface StartDMAction { /** * Try to find an existing DM with the given user, or create one if none exists. diff --git a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfilePresenterFactory.kt b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfilePresenterFactory.kt index 4bf68ae324..80a21505f5 100644 --- a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfilePresenterFactory.kt +++ b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfilePresenterFactory.kt @@ -11,6 +11,12 @@ package io.element.android.features.userprofile.api import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.UserId +/** + * Creates the user profile presenter, so that other features can show a profile without depending on this one's implementation. + */ fun interface UserProfilePresenterFactory { + /** + * @param userId the user whose profile is shown. + */ fun create(userId: UserId): Presenter } diff --git a/features/viewfolder/api/src/main/kotlin/io/element/android/features/viewfolder/api/TextFileViewer.kt b/features/viewfolder/api/src/main/kotlin/io/element/android/features/viewfolder/api/TextFileViewer.kt index 29a7c9f9e8..5abc27546d 100644 --- a/features/viewfolder/api/src/main/kotlin/io/element/android/features/viewfolder/api/TextFileViewer.kt +++ b/features/viewfolder/api/src/main/kotlin/io/element/android/features/viewfolder/api/TextFileViewer.kt @@ -12,7 +12,16 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import kotlinx.collections.immutable.ImmutableList +/** + * Renders the contents of a text file, used by the log viewer in the developer options. + */ fun interface TextFileViewer { + /** + * Draws the file, one row per line. + * + * @param lines the already-read contents of the file. + * @param modifier layout modifier for the container. + */ @Composable fun Render( lines: ImmutableList, From b74b146917b4daeb07f7a8275c33982297e04fcc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 12:04:40 +0200 Subject: [PATCH 193/338] Add KDoc to the single-interface library and service API modules Cover the last remaining interfaces across libraries/audio, libraries/cachestore, libraries/indicator, libraries/slashcommands, libraries/voiceplayer and services/apperror, with their members and parameters. This completes the effort: all 222 in-scope interfaces and their 820 members in the 60 api modules that hold any now carry documentation. Notable points: CacheStore applies no expiry of its own and only records when each entry was written, leaving staleness to the caller; SlashCommandService.parse never throws, returning NotACommand for ordinary text and an error case for a malformed command. Co-Authored-By: Claude Opus 5 (1M context) --- .../android/libraries/audio/api/AudioFocus.kt | 3 +++ .../libraries/cachestore/api/CacheStore.kt | 24 +++++++++++++++++++ .../indicator/api/IndicatorService.kt | 2 ++ .../slashcommands/api/SlashCommandService.kt | 24 ++++++++++++++++++- .../api/VoiceMessagePresenterFactory.kt | 10 ++++++++ .../apperror/api/AppErrorStateService.kt | 16 +++++++++++++ 6 files changed, 78 insertions(+), 1 deletion(-) diff --git a/libraries/audio/api/src/main/kotlin/io/element/android/libraries/audio/api/AudioFocus.kt b/libraries/audio/api/src/main/kotlin/io/element/android/libraries/audio/api/AudioFocus.kt index aeccaa9b8a..de2e5844d5 100644 --- a/libraries/audio/api/src/main/kotlin/io/element/android/libraries/audio/api/AudioFocus.kt +++ b/libraries/audio/api/src/main/kotlin/io/element/android/libraries/audio/api/AudioFocus.kt @@ -15,6 +15,9 @@ enum class AudioFocusRequester { MediaViewer, } +/** + * Claims and releases system audio focus, so that the app's own players do not talk over each other or over other apps. + */ interface AudioFocus { /** * Request audio focus for the given requester. diff --git a/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt b/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt index 5df446f688..bbfb7d3405 100644 --- a/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt +++ b/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt @@ -7,9 +7,33 @@ package io.element.android.libraries.cachestore.api +/** + * A generic on-disk key/value cache, where each entry records when it was written so callers can decide for themselves when it is stale. + */ interface CacheStore { + /** + * Stores an entry, replacing any previous one under the same key. + * + * @param key the entry to write. + * @param data the value along with the time it was obtained. + */ suspend fun storeData(key: String, data: CacheData) + + /** + * Reads an entry without applying any expiry of its own; judging staleness is up to the caller. + * + * @param key the entry to read. + * @return the value and its timestamp, or `null` when nothing is stored under that key. + */ suspend fun getData(key: String): CacheData? + + /** + * Removes one entry. + * + * @param key the entry to delete. + */ suspend fun deleteData(key: String) + + /** Empties the whole cache. */ suspend fun deleteAll() } diff --git a/libraries/indicator/api/src/main/kotlin/io/element/android/libraries/indicator/api/IndicatorService.kt b/libraries/indicator/api/src/main/kotlin/io/element/android/libraries/indicator/api/IndicatorService.kt index 0409a0f846..d2fde82d8a 100644 --- a/libraries/indicator/api/src/main/kotlin/io/element/android/libraries/indicator/api/IndicatorService.kt +++ b/libraries/indicator/api/src/main/kotlin/io/element/android/libraries/indicator/api/IndicatorService.kt @@ -15,9 +15,11 @@ import androidx.compose.runtime.State * A set of State to observe to display or not the indicators in the UI. */ interface IndicatorService { + /** Whether to show the red dot on the room list top bar, which aggregates the reasons the user should open the settings. */ @Composable fun showRoomListTopBarIndicator(): State + /** Whether to show the red dot on the chat backup setting, i.e. when recovery needs the user's attention. */ @Composable fun showSettingChatBackupIndicator(): State } diff --git a/libraries/slashcommands/api/src/main/kotlin/io/element/android/libraries/slashcommands/api/SlashCommandService.kt b/libraries/slashcommands/api/src/main/kotlin/io/element/android/libraries/slashcommands/api/SlashCommandService.kt index 9dfca26078..7ae056e38b 100644 --- a/libraries/slashcommands/api/src/main/kotlin/io/element/android/libraries/slashcommands/api/SlashCommandService.kt +++ b/libraries/slashcommands/api/src/main/kotlin/io/element/android/libraries/slashcommands/api/SlashCommandService.kt @@ -9,7 +9,19 @@ package io.element.android.libraries.slashcommands.api import io.element.android.libraries.matrix.api.timeline.Timeline +/** + * Recognises and runs the slash commands the user can type in the composer, such as `/me` or `/join`. + * + * The composer first [parse]s what was typed, then hands the result to whichever proceed method matches its kind; navigation + * commands and the error cases are handled by the composer itself. + */ interface SlashCommandService { + /** + * Returns the commands whose name starts with what has been typed, so the composer can offer an autocomplete list. + * + * @param text the current composer content. + * @param isInThread whether the composer is in a thread, since not every command is available there. + */ suspend fun getSuggestions( text: String, isInThread: Boolean, @@ -17,6 +29,11 @@ interface SlashCommandService { /** * Parse the message and return a SlashCommand. + * Ordinary text comes back as `NotACommand`, and a malformed or unsupported command as one of the error cases, so this never throws. + * + * @param textMessage the composer content as plain text. + * @param formattedMessage the composer content as HTML, or `null` when unformatted. + * @param isInThreadTimeline whether the composer is in a thread. */ suspend fun parse( textMessage: CharSequence, @@ -26,6 +43,9 @@ interface SlashCommandService { /** * Proceed a SlashCommandSendMessage. + * + * @param slashCommand the parsed command, carrying the content to send. + * @param timeline the timeline to send it to. */ suspend fun proceedSendMessage( slashCommand: SlashCommand.SlashCommandSendMessage, @@ -33,7 +53,9 @@ interface SlashCommandService { ): Result /** - * Proceed a SlashCommandAdmin. + * Proceed a SlashCommandAdmin, i.e. one that acts on the room or its members rather than sending a message. + * + * @param slashCommand the parsed command to run. */ suspend fun proceedAdmin( slashCommand: SlashCommand.SlashCommandAdmin, diff --git a/libraries/voiceplayer/api/src/main/kotlin/io/element/android/libraries/voiceplayer/api/VoiceMessagePresenterFactory.kt b/libraries/voiceplayer/api/src/main/kotlin/io/element/android/libraries/voiceplayer/api/VoiceMessagePresenterFactory.kt index 0229ed991f..1b576a6d72 100644 --- a/libraries/voiceplayer/api/src/main/kotlin/io/element/android/libraries/voiceplayer/api/VoiceMessagePresenterFactory.kt +++ b/libraries/voiceplayer/api/src/main/kotlin/io/element/android/libraries/voiceplayer/api/VoiceMessagePresenterFactory.kt @@ -13,7 +13,17 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource import kotlin.time.Duration +/** + * Creates the presenter of a single voice message player, one per voice message shown in a timeline. + */ interface VoiceMessagePresenterFactory { + /** + * @param eventId the event carrying the voice message, or `null` while it is still a local echo. + * @param mediaSource where the audio is fetched from. + * @param mimeType the MIME type of the audio, or `null` when the event did not state it. + * @param filename the file name to display, or `null` when the event did not state it. + * @param duration the length of the recording, used to render the waveform before the audio is downloaded. + */ fun createVoiceMessagePresenter( eventId: EventId?, mediaSource: MediaSource, diff --git a/services/apperror/api/src/main/kotlin/io/element/android/services/apperror/api/AppErrorStateService.kt b/services/apperror/api/src/main/kotlin/io/element/android/services/apperror/api/AppErrorStateService.kt index 7c592e2102..d9ae37c097 100644 --- a/services/apperror/api/src/main/kotlin/io/element/android/services/apperror/api/AppErrorStateService.kt +++ b/services/apperror/api/src/main/kotlin/io/element/android/services/apperror/api/AppErrorStateService.kt @@ -11,10 +11,26 @@ package io.element.android.services.apperror.api import androidx.annotation.StringRes import kotlinx.coroutines.flow.StateFlow +/** + * Surfaces app-wide error dialogs from anywhere, for the failures that are not tied to the screen the user is on. + */ interface AppErrorStateService { + /** The error to display, if any; observed once at the top of the app rather than per screen. */ val appErrorStateFlow: StateFlow + /** + * Shows an error dialog with already resolved text. + * + * @param title the dialog title. + * @param body the dialog message; never put user content or secrets in it. + */ fun showError(title: String, body: String) + /** + * Shows an error dialog from string resources, which is the preferred form since it stays localised. + * + * @param titleRes resource id of the dialog title. + * @param bodyRes resource id of the dialog message. + */ fun showError(@StringRes titleRes: Int, @StringRes bodyRes: Int) } From 2c94cee5754ceff21550f6c682a715d23d9a6fc0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:18:43 +0200 Subject: [PATCH 194/338] Update dependency org.matrix.rustcomponents:sdk-android to v26.08.13 (#7420) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c0a6746e0a..17eb8ff751 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -185,7 +185,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.08.11" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.08.13" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } From 1ab401482ac177a865bb9a882ed3c051fc437d4a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 12:20:49 +0200 Subject: [PATCH 195/338] Fix formatting issues --- .../location/api/live/ActiveLiveLocationShareManager.kt | 6 ++---- .../poll/api/pollcontent/PollContentStateFactory.kt | 1 + .../libraries/eventformatter/api/TimelineEventFormatter.kt | 1 + .../io/element/android/libraries/matrix/api/MatrixClient.kt | 1 + .../android/libraries/permissions/api/PermissionsStore.kt | 1 - .../android/libraries/sessionstorage/api/SessionStore.kt | 3 +-- .../android/libraries/usersearch/api/UserListDataSource.kt | 2 +- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt index 74c9089c30..64c28209ca 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt @@ -30,8 +30,7 @@ interface ActiveLiveLocationShareManager { * Starts live location sharing in the given room. * Calls room.startLiveLocationShare() on the SDK, registers the share, * and starts the foreground GPS service if not already running. - */ - /** + * * @param roomId the room to start sharing the location in. * @param duration how long the share should last before it expires on its own. */ @@ -41,8 +40,7 @@ interface ActiveLiveLocationShareManager { * Stops live location sharing in the given room. * Calls room.stopLiveLocationShare() on the SDK, removes the share, * and stops the foreground service if no shares remain. - */ - /** + * * @param roomId the room to stop sharing the location in. */ suspend fun stopShare(roomId: RoomId): Result diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFactory.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFactory.kt index 87e3f5eece..f9b4a56efc 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFactory.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentStateFactory.kt @@ -30,6 +30,7 @@ interface PollContentStateFactory { content = content, ) } + /** * @param eventId the poll start event, or `null` while the poll is still a local echo. * @param isEditable whether the current user may still edit the poll. diff --git a/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/TimelineEventFormatter.kt b/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/TimelineEventFormatter.kt index 37081be242..67d263f168 100644 --- a/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/TimelineEventFormatter.kt +++ b/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/TimelineEventFormatter.kt @@ -31,6 +31,7 @@ interface TimelineEventFormatter { senderDisambiguatedDisplayName = event.senderProfile.getDisambiguatedDisplayName(event.sender), ) } + /** * @param content the event content to describe. * @param isOutgoing whether the event was sent by the current user, which selects the first-person wording. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 519ccfe3a4..0f5ed8ebf8 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -135,6 +135,7 @@ interface MatrixClient : UrlContentFetcher { /** Scans media content before it is displayed, or `null` when no content scanner is configured for this session. */ val contentScanner: ContentScanner? + /** * Returns the room with the given id if the user has joined it, along with its live [Timeline]. * A new instance is created on each call and the caller owns it, so it must be closed once no longer needed. diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStore.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStore.kt index 0bf7f0aefd..2d37f016f9 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStore.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStore.kt @@ -49,6 +49,5 @@ interface PermissionsStore { suspend fun resetPermission(permission: String) /** Forgets every recorded permission; exposed for debugging from the developer options. */ - // To debug suspend fun resetStore() } diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt index 53aa0c9516..641a2afdf2 100644 --- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt @@ -32,8 +32,7 @@ interface SessionStore { /** * Add a new session. If other sessions exist, the new one will be set as the latest used one, and * the added session position will be set to a value higher than the other session positions. - */ - /** + * * @param sessionData the session to store, including its tokens. */ suspend fun addSession(sessionData: SessionData) diff --git a/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserListDataSource.kt b/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserListDataSource.kt index 9446668502..3697125922 100644 --- a/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserListDataSource.kt +++ b/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserListDataSource.kt @@ -17,11 +17,11 @@ import io.element.android.libraries.matrix.api.user.MatrixUser interface UserListDataSource { /** * Searches the user directory, returning an empty list rather than failing when the request does not succeed. + * TODO should probably have a flow * * @param query the text to look for in user ids and display names. * @param count the maximum number of results to return. */ - // TODO should probably have a flow suspend fun search(query: String, count: Long): List /** From 7d9f4b9af4b04a0757b5e551323d05d2445d65f2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 12:22:37 +0200 Subject: [PATCH 196/338] Fix outdated documentation. --- .../services/toolbox/api/sdk/BuildVersionSdkIntProvider.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/sdk/BuildVersionSdkIntProvider.kt b/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/sdk/BuildVersionSdkIntProvider.kt index 40b2b2aac8..385109d6c8 100644 --- a/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/sdk/BuildVersionSdkIntProvider.kt +++ b/services/toolbox/api/src/main/kotlin/io/element/android/services/toolbox/api/sdk/BuildVersionSdkIntProvider.kt @@ -20,8 +20,9 @@ interface BuildVersionSdkIntProvider { fun get(): Int /** - * Checks the if the current OS version is equal or greater than [version]. + * Checks if the current OS version is equal or greater than [version], and run [result] if so. * + * @param T the type of the result to compute. * @param version the minimum API level required. * @param result computed and returned only when the requirement is met. * @return A `non-null` result if true, `null` otherwise. From ea076ecc4bddd880ee8019709115454852653f9e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 11:48:17 +0000 Subject: [PATCH 197/338] Update media3 to v1.11.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 17eb8ff751..d1a777d3b5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ constraintlayout = "2.2.2" constraintlayout_compose = "1.1.2" lifecycle = "2.11.0" activity = "1.13.0" -media3 = "1.10.1" +media3 = "1.11.0" camera = "1.6.1" work = "2.11.2" From 67b48ae184556f1600964b436a28f1012c307951 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:55:53 +0000 Subject: [PATCH 198/338] Fix build failure --- .../impl/pinned/list/PinnedMessagesListNode.kt | 3 +++ .../impl/pinned/list/PinnedMessagesListView.kt | 11 ++++++++++- .../pinned/list/PinnedMessagesListPresenterTest.kt | 4 ++++ .../impl/pinned/list/PinnedMessagesListViewTest.kt | 2 ++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt index 06d97f167c..36ae68de9b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.androidutils.system.openUrlInExternalApp import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.emoji.api.picker.EmojiPickerRenderer import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.core.UserId @@ -50,6 +51,7 @@ class PinnedMessagesListNode( actionListPresenterFactory: ActionListPresenter.Factory, private val timelineItemPresenterFactories: TimelineItemPresenterFactories, private val permalinkParser: PermalinkParser, + private val emojiPickerRenderer: EmojiPickerRenderer, ) : Node(buildContext, plugins = plugins), PinnedMessagesListNavigator { interface Callback : Plugin { fun handleEventClick(event: TimelineItem.Event, canUseOverlay: Boolean) @@ -134,6 +136,7 @@ class PinnedMessagesListNode( toastMessage = toastMessage, ) }, + emojiPickerRenderer = emojiPickerRenderer, modifier = modifier ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt index f5d4b402d0..0b76d35d36 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt @@ -54,6 +54,8 @@ 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.lazyColumnContentPadding import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets +import io.element.android.libraries.emoji.api.picker.EmojiPickerRenderer +import io.element.android.libraries.emoji.api.picker.NoOpEmojiPickerRenderer import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.ui.strings.CommonStrings @@ -70,6 +72,7 @@ fun PinnedMessagesListView( onUserDataClick: (MatrixUser) -> Unit, onLinkClick: (Link) -> Unit, onLinkLongClick: (Link) -> Unit, + emojiPickerRenderer: EmojiPickerRenderer, modifier: Modifier = Modifier, ) { Scaffold( @@ -93,6 +96,7 @@ fun PinnedMessagesListView( onLinkClick = onLinkClick, onLinkLongClick = onLinkLongClick, onErrorDismiss = onBackClick, + emojiPickerRenderer = emojiPickerRenderer, modifier = Modifier .padding(padding) .consumeWindowInsets(padding), @@ -125,6 +129,7 @@ private fun PinnedMessagesListContent( onLinkClick: (Link) -> Unit, onLinkLongClick: (Link) -> Unit, onErrorDismiss: () -> Unit, + emojiPickerRenderer: EmojiPickerRenderer, modifier: Modifier = Modifier, ) { Box(modifier.fillMaxSize()) { @@ -145,6 +150,7 @@ private fun PinnedMessagesListContent( onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, onLinkLongClick = onLinkLongClick, + emojiPickerRenderer = emojiPickerRenderer, ) PinnedMessagesListState.Loading -> { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { @@ -184,6 +190,7 @@ private fun PinnedMessagesListLoaded( onUserDataClick: (MatrixUser) -> Unit, onLinkClick: (Link) -> Unit, onLinkLongClick: (Link) -> Unit, + emojiPickerRenderer: EmojiPickerRenderer, modifier: Modifier = Modifier, ) { fun onActionSelected(timelineItemAction: TimelineItemAction, event: TimelineItem.Event) { @@ -295,7 +302,8 @@ private fun PinnedMessagesListLoaded( state = state.customReactionState, onSelectEmoji = { eventOrTransactionId, emoji -> state.eventSink(PinnedMessagesListEvent.ToggleReaction(emoji.unicode, eventOrTransactionId)) - } + }, + emojiPickerRenderer = emojiPickerRenderer, ) ReactionSummaryView(state = state.reactionSummaryState) } @@ -347,5 +355,6 @@ internal fun PinnedMessagesListViewPreview(@PreviewParameter(PinnedMessagesListS onUserDataClick = {}, onLinkClick = {}, onLinkLongClick = {}, + emojiPickerRenderer = NoOpEmojiPickerRenderer, ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt index 3efa707c3d..cafb16c41f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt @@ -10,6 +10,8 @@ package io.element.android.features.messages.impl.pinned.list import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.PinUnpinAction +import io.element.android.features.messages.impl.aCustomReactionState +import io.element.android.features.messages.impl.aReactionSummaryState import io.element.android.features.messages.impl.actionlist.anActionListState import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryCreator @@ -400,6 +402,8 @@ class PinnedMessagesListPresenterTest { snackbarDispatcher = SnackbarDispatcher(), actionListPresenter = { anActionListState() }, linkPresenter = { aLinkState() }, + customReactionPresenter = { aCustomReactionState() }, + reactionSummaryPresenter = { aReactionSummaryState() }, analyticsService = analyticsService, featureFlagService = featureFlagService, sessionCoroutineScope = this, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt index 35e60684a9..856f74e33a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt @@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.actionlist.anActionListState import io.element.android.features.messages.impl.timeline.aTimelineItemList import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemFileContent +import io.element.android.libraries.emoji.api.picker.NoOpEmojiPickerRenderer import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.EnsureNeverCalledWithParam @@ -111,6 +112,7 @@ private fun AndroidComposeUiTest.setPinnedMessagesListView( onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, onLinkLongClick = onLinkLongClick, + emojiPickerRenderer = NoOpEmojiPickerRenderer, ) } } From 54ba43a8ec5646f73bfd903059745b014d3b5695 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Fri, 14 Aug 2026 08:12:51 -0700 Subject: [PATCH 199/338] Use the correct license header on the new account provider history files --- .../login/impl/accountprovider/SaveAccountProviderToHistory.kt | 2 +- .../impl/accountprovider/AccountProviderDataSourceFixture.kt | 2 +- .../impl/accountprovider/SaveAccountProviderToHistoryTest.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt index 46f3281826..b3b99115cc 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceFixture.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceFixture.kt index e8f9dbefb9..25bc67144b 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceFixture.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceFixture.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt index 21fd7efce8..0af8188f3f 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. From f488da890b57c94f4ba6492f1f21d9b12289d300 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Fri, 14 Aug 2026 08:57:37 -0700 Subject: [PATCH 200/338] Update the maestro sign-in flow for the consolidated account provider screen --- .maestro/tests/account/changeServer.yaml | 21 ------------------- .maestro/tests/account/login.yaml | 3 +-- .../assertions/assertLoginDisplayed.yaml | 2 +- 3 files changed, 2 insertions(+), 24 deletions(-) delete mode 100644 .maestro/tests/account/changeServer.yaml diff --git a/.maestro/tests/account/changeServer.yaml b/.maestro/tests/account/changeServer.yaml deleted file mode 100644 index b07fa5cc09..0000000000 --- a/.maestro/tests/account/changeServer.yaml +++ /dev/null @@ -1,21 +0,0 @@ -appId: ${MAESTRO_APP_ID} ---- -- tapOn: - id: "login-change_server" -- takeScreenshot: build/maestro/200-ChangeServer -- tapOn: "matrix.org" -- tapOn: - id: "login-change_server" -- tapOn: "Other" -- tapOn: - id: "change_server-server" -- inputText: "element" -- hideKeyboard -- extendedWaitUntil: - visible: "element.io" - timeout: 10000 -- tapOn: "element.io" -# Revert to matrix.org -- tapOn: - id: "login-change_server" -- tapOn: "matrix.org" diff --git a/.maestro/tests/account/login.yaml b/.maestro/tests/account/login.yaml index 873843b22d..81922c7f38 100644 --- a/.maestro/tests/account/login.yaml +++ b/.maestro/tests/account/login.yaml @@ -3,8 +3,7 @@ appId: ${MAESTRO_APP_ID} - tapOn: "Sign in manually" - runFlow: ../assertions/assertLoginDisplayed.yaml - takeScreenshot: build/maestro/100-SignIn -- runFlow: changeServer.yaml -- runFlow: ../assertions/assertLoginDisplayed.yaml +# The account provider field defaults to matrix.org, so continue straight to the MAS login. - tapOn: id: "login-continue" ## MAS page diff --git a/.maestro/tests/assertions/assertLoginDisplayed.yaml b/.maestro/tests/assertions/assertLoginDisplayed.yaml index 6d8558c38e..c32f23c323 100644 --- a/.maestro/tests/assertions/assertLoginDisplayed.yaml +++ b/.maestro/tests/assertions/assertLoginDisplayed.yaml @@ -1,5 +1,5 @@ appId: ${MAESTRO_APP_ID} --- - extendedWaitUntil: - visible: "Change account provider" + visible: "Enter your account provider" timeout: 10000 From 23b75c342361ab400e3c2ff795507dea82ceac02 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sat, 15 Aug 2026 11:05:43 +0200 Subject: [PATCH 201/338] Fix outdated documentation. (#7430) --- .../features/location/api/internal/TileServerStyleUriBuilder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt index 4cfc8c4a2f..9676005f59 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt @@ -22,7 +22,7 @@ interface TileServerStyleUriBuilder { /** * Builds the style URI the map view loads its tiles from. * - * @param customMapStyleUrl the style advertised by the homeserver, or `null` to use the one built into the app. + * @param customMapTilerConfig the custom MapTiler configuration received from the server, or null if not available. * @param darkMode whether to request the dark map style. */ fun build( From 45057b35a58333810b0ada06ab8bd3d8bc6fe12e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 15 Aug 2026 09:07:16 +0000 Subject: [PATCH 202/338] Update dependency org.maplibre.compose:maplibre-compose to v0.14.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 17eb8ff751..f576c77125 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -217,7 +217,7 @@ telephoto_flick = { module = "me.saket.telephoto:flick-android", version.ref = " statemachine = "com.freeletics.flowredux:compose:1.2.2" maplibre = "org.maplibre.gl:android-sdk:13.4.1" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:3.0.2" -maplibre_compose = "org.maplibre.compose:maplibre-compose:0.13.1" +maplibre_compose = "org.maplibre.compose:maplibre-compose:0.14.0" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.2" opusencoder = "io.element.android:opusencoder:1.2.0" zxing_cpp = "io.github.zxing-cpp:android:3.1.1" From 2c0da1cadf13bbb4a08d0151c2342e0e7aa52b2b Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Sun, 16 Aug 2026 22:44:11 +0300 Subject: [PATCH 203/338] Share a file whose mime type is not one of the supported ones handleIncomingShareIntent only accepted an intent whose resolved mime type started with image/, video/, audio/, application/, file/, text/ or */. An ACTION_SEND carrying EXTRA_STREAM with any other top level type, such as the message/rfc822 that some apps use when attaching a file, fell through to null and the share was dropped before the room picker ever appeared, even though the manifest filter accepts */*. The uris are extracted before that check, so their presence is enough to know the intent carries something to send. Accept the intent when it has uris and keep the mime type list for the case where it does not. --- .../share/impl/DefaultShareIntentHandler.kt | 3 +- .../impl/DefaultShareIntentHandlerTest.kt | 112 ++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandlerTest.kt diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandler.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandler.kt index cfa06c3a97..2ae31fb00c 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandler.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandler.kt @@ -42,7 +42,8 @@ class DefaultShareIntentHandler( val uris = getIncomingUris(intent, type) return when { uris.isEmpty() && type == MimeTypes.PlainText -> handlePlainText(intent) - type.isMimeTypeImage() || + uris.isNotEmpty() || + type.isMimeTypeImage() || type.isMimeTypeVideo() || type.isMimeTypeAudio() || type.isMimeTypeApplication() || diff --git a/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandlerTest.kt b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandlerTest.kt new file mode 100644 index 0000000000..8cde31bd0c --- /dev/null +++ b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandlerTest.kt @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.share.impl + +import android.content.Intent +import android.net.Uri +import androidx.core.net.toUri +import com.google.common.truth.Truth.assertThat +import io.element.android.features.share.api.ShareIntentData +import io.element.android.features.share.api.UriToShare +import io.element.android.tests.testutils.robolectric.RobolectricTest +import org.junit.Test +import org.robolectric.RuntimeEnvironment + +class DefaultShareIntentHandlerTest : RobolectricTest() { + @Test + fun `an image is shared as an uri`() { + val uri = "content://sender/image.jpg".toUri() + val result = createDefaultShareIntentHandler().handleIncomingShareIntent( + aSendIntent(type = "image/jpeg", uri = uri) + ) + assertThat(result).isEqualTo( + ShareIntentData.Uris( + text = null, + uris = listOf(UriToShare(uri = uri, mimeType = "image/jpeg")), + ) + ) + } + + @Test + fun `an uri with an unlisted mime type is shared`() { + val uri = "content://sender/logs.zip".toUri() + val result = createDefaultShareIntentHandler().handleIncomingShareIntent( + aSendIntent(type = "message/rfc822", uri = uri) + ) + assertThat(result).isEqualTo( + ShareIntentData.Uris( + text = null, + uris = listOf(UriToShare(uri = uri, mimeType = "message/rfc822")), + ) + ) + } + + @Test + fun `an uri with an unlisted mime type keeps the text as a caption`() { + val uri = "content://sender/logs.zip".toUri() + val result = createDefaultShareIntentHandler().handleIncomingShareIntent( + aSendIntent(type = "message/rfc822", uri = uri).apply { + putExtra(Intent.EXTRA_TEXT, "a caption") + } + ) + assertThat(result).isEqualTo( + ShareIntentData.Uris( + text = "a caption", + uris = listOf(UriToShare(uri = uri, mimeType = "message/rfc822")), + ) + ) + } + + @Test + fun `several uris with an unlisted mime type are shared`() { + val firstUri = "content://sender/first.zip".toUri() + val secondUri = "content://sender/second.zip".toUri() + val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply { + type = "message/rfc822" + putParcelableArrayListExtra(Intent.EXTRA_STREAM, arrayListOf(firstUri, secondUri)) + } + val result = createDefaultShareIntentHandler().handleIncomingShareIntent(intent) + assertThat(result).isEqualTo( + ShareIntentData.Uris( + text = null, + uris = listOf( + UriToShare(uri = firstUri, mimeType = "message/rfc822"), + UriToShare(uri = secondUri, mimeType = "message/rfc822"), + ), + ) + ) + } + + @Test + fun `a plain text is shared as a text`() { + val intent = Intent(Intent.ACTION_SEND).apply { + type = "text/plain" + putExtra(Intent.EXTRA_TEXT, "a text") + } + val result = createDefaultShareIntentHandler().handleIncomingShareIntent(intent) + assertThat(result).isEqualTo(ShareIntentData.PlainText("a text")) + } + + @Test + fun `an intent with neither uri nor text is not handled`() { + val intent = Intent(Intent.ACTION_SEND).apply { + type = "message/rfc822" + } + val result = createDefaultShareIntentHandler().handleIncomingShareIntent(intent) + assertThat(result).isNull() + } + + private fun aSendIntent(type: String, uri: Uri) = Intent(Intent.ACTION_SEND).apply { + this.type = type + putExtra(Intent.EXTRA_STREAM, uri) + } + + private fun createDefaultShareIntentHandler() = DefaultShareIntentHandler( + context = RuntimeEnvironment.getApplication(), + ) +} From 04dcc14eaaad0b178dfc133431adeb89ca12eb6a Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Sun, 16 Aug 2026 22:44:29 +0300 Subject: [PATCH 204/338] Show an error when the room to leave cannot be found showLeaveRoomAlert ran its whole body inside client.getRoom(roomId)?.use { }. When getRoom returned null, which happens for a room the client can no longer resolve, the body was skipped without writing any state: leaveAction stayed Uninitialized, so no confirmation dialog and no error was displayed and Leave room appeared to do nothing at all. Handle the missing room explicitly and surface it as a failure, which AsyncActionView already renders with the generic error dialog. leaveRoom used a !! for the same lookup, so it reported the miss as a NullPointerException; it now throws the same named exception. --- .../leaveroom/impl/LeaveRoomPresenter.kt | 13 ++++++++-- .../impl/LeaveBaseRoomPresenterTest.kt | 26 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt b/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt index d11dc7e7e8..e2065bca1f 100644 --- a/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt +++ b/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt @@ -60,7 +60,13 @@ class LeaveRoomPresenter( roomId: RoomId, leaveAction: MutableState>, ) = launch(dispatchers.io) { - client.getRoom(roomId)?.use { room -> + val room = client.getRoom(roomId) + if (room == null) { + Timber.e("Unable to find room $roomId to leave it") + leaveAction.value = AsyncAction.Failure(RoomNotFoundException(roomId)) + return@launch + } + room.use { val roomInfo = room.roomInfoFlow.first() leaveAction.value = when { roomInfo.isDm -> Confirmation.Dm(roomId) @@ -78,7 +84,8 @@ class LeaveRoomPresenter( leaveAction: MutableState>, ) = launch(dispatchers.io) { leaveAction.runCatchingUpdatingState { - client.getRoom(roomId)!!.use { room -> + val room = client.getRoom(roomId) ?: throw RoomNotFoundException(roomId) + room.use { room .leave() .onSuccess { notificationConversationService.onLeftRoom(client.sessionId, roomId) } @@ -100,3 +107,5 @@ class LeaveRoomPresenter( } } } + +internal class RoomNotFoundException(roomId: RoomId) : IllegalStateException("Room $roomId not found") diff --git a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveBaseRoomPresenterTest.kt b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveBaseRoomPresenterTest.kt index 90b7f2369f..f8f7a771e1 100644 --- a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveBaseRoomPresenterTest.kt +++ b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveBaseRoomPresenterTest.kt @@ -175,6 +175,32 @@ class LeaveBaseRoomPresenterTest { } } + @Test + fun `present - show error if the room to confirm leaving is not found`() = runTest { + val presenter = createLeaveRoomPresenter(client = FakeMatrixClient()) + presenter.stateFlow().test { + val initialState = awaitItem() + initialState.eventSink(LeaveRoomEvent.LeaveRoom(A_ROOM_ID, needsConfirmation = true)) + val errorState = awaitItem() + assertThat(errorState.leaveAction).isInstanceOf(AsyncAction.Failure::class.java) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - show error if the room to leave is not found`() = runTest { + val presenter = createLeaveRoomPresenter(client = FakeMatrixClient()) + presenter.stateFlow().test { + val initialState = awaitItem() + initialState.eventSink(LeaveRoomEvent.LeaveRoom(A_ROOM_ID, needsConfirmation = false)) + val progressState = awaitItem() + assertThat(progressState.leaveAction).isEqualTo(AsyncAction.Loading) + val errorState = awaitItem() + assertThat(errorState.leaveAction).isInstanceOf(AsyncAction.Failure::class.java) + cancelAndIgnoreRemainingEvents() + } + } + @Test fun `present - reset state after error`() = runTest { val presenter = createLeaveRoomPresenter( From c901b902b14c0db38c593341be7b8543656e216b Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Sun, 16 Aug 2026 22:44:48 +0300 Subject: [PATCH 205/338] Exclude carriage returns from the login and password fields The sanitize() helper applied to the login, password and account deactivation password fields promises to remove "any new line characters" but only removes LF. A value pasted from a source with CRLF endings keeps its CR, which is invisible in the field and is sent to the homeserver as part of the password, so the login is rejected while typing the same characters by hand works. Drop every line separator instead of LF only, in both copies of the helper. Whitespace that can legitimately be part of a password, such as spaces and tabs, is left untouched. --- .../logout/impl/AccountDeactivationView.kt | 4 +-- .../loginpassword/LoginPasswordView.kt | 4 +-- .../loginpassword/LoginPasswordViewTest.kt | 30 +++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt index 738046b3a3..9b5f0cfd99 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt @@ -299,10 +299,10 @@ private fun Content( } /** - * Ensure that the string does not contain any new line characters, which can happen when pasting values. + * Ensure that the string does not contain any line separator, which can happen when pasting values. */ private fun String.sanitize(): String { - return replace("\n", "") + return filterNot { it == '\n' || it == '\r' } } @PreviewsDayNight diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index 06a1641644..9757f5e9ee 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -274,10 +274,10 @@ private fun LoginForm( } /** - * Ensure that the string does not contain any new line characters, which can happen when pasting values. + * Ensure that the string does not contain any line separator, which can happen when pasting values. */ private fun String.sanitize(): String { - return replace("\n", "") + return filterNot { it == '\n' || it == '\r' } } @Composable diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt index 3a128f2e4f..d76c94d5ea 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt @@ -81,6 +81,21 @@ class LoginPasswordViewTest : RobolectricTest() { ) } + @Test + fun `changing login removes carriage returns the expected event`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + setLoginPasswordView( + aLoginPasswordState( + eventSink = eventsRecorder, + ), + ) + val userNameHint = activity!!.getString(CommonStrings.common_username) + onNodeWithText(userNameHint).performTextInput("a\r\nb") + eventsRecorder.assertSingle( + LoginPasswordEvents.SetLogin("ab") + ) + } + @Test fun `clearing login invokes the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() @@ -112,6 +127,21 @@ class LoginPasswordViewTest : RobolectricTest() { ) } + @Test + fun `changing password removes carriage returns the expected event`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + setLoginPasswordView( + aLoginPasswordState( + eventSink = eventsRecorder, + ), + ) + val passwordHint = activity!!.getString(CommonStrings.common_password) + onNodeWithText(passwordHint).performTextInput("a\r\nb") + eventsRecorder.assertSingle( + LoginPasswordEvents.SetPassword("ab") + ) + } + @Test fun `reveal password makes the password visible`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) From 537842bb4065471b8dbf10b5a7c221e03ece6f5e Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 03:40:11 +0300 Subject: [PATCH 206/338] Convert markdown to HTML when a caption is edited Adding or editing a media caption from the plain composer sent the caption as text only: the composer has no HTML to pass, and the media caption edit was built by hand with a null formatted body, so the markdown was never rendered. Sending a caption with the same markdown does render it, because the SDK builds the formatted body there. Build the edit with the SDK's createCaptionEdit, which runs the caption through the same markdown conversion the send path uses. --- .../android/libraries/matrix/impl/timeline/RustTimeline.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt index bd756bc002..64c07304c5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt @@ -75,6 +75,7 @@ import org.matrix.rustcomponents.sdk.PollData import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle import org.matrix.rustcomponents.sdk.UploadParameters import org.matrix.rustcomponents.sdk.UploadSource +import org.matrix.rustcomponents.sdk.createCaptionEdit import org.matrix.rustcomponents.sdk.use import timber.log.Timber import uniffi.matrix_sdk.PaginationStatus @@ -341,7 +342,7 @@ class RustTimeline( formattedCaption: String?, ): Result = withContext(dispatcher) { runCatchingExceptions { - val editedContent = EditedContent.MediaCaption( + val editedContent = createCaptionEdit( caption = caption, formattedCaption = formattedCaption?.let { FormattedBody(body = it, format = MessageFormat.Html) From 999bee2420dfeb215ebb4e7e34873e6ae6c38714 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 03:40:53 +0300 Subject: [PATCH 207/338] Remove the F-Droid dex patching step from the release script fix-pg-map-id.py rewrites the pg_map_id of a dex file, but the dex format changed and its regex no longer matches anything, so the step is a no-op and the F-Droid build is reproducible without it. inplace-fix.py only existed to drive it; none of its other commands are used here. The removed call also re-ran zipalign -P 16 on each APK. AGP aligns the release APKs at packaging time and apksigner is already invoked with --alignment-preserved, so the signed output stays 16 KB aligned. --- tools/release/fix-pg-map-id.py | 227 --------------------------------- tools/release/inplace-fix.py | 199 ----------------------------- tools/release/release.sh | 9 -- 3 files changed, 435 deletions(-) delete mode 100644 tools/release/fix-pg-map-id.py delete mode 100644 tools/release/inplace-fix.py diff --git a/tools/release/fix-pg-map-id.py b/tools/release/fix-pg-map-id.py deleted file mode 100644 index c37fec2528..0000000000 --- a/tools/release/fix-pg-map-id.py +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/python3 -# encoding: utf-8 -# SPDX-FileCopyrightText: 2024 FC (Fay) Stegerman -# SPDX-License-Identifier: GPL-3.0-or-later. - -import hashlib -import os -import re -import struct -import zipfile -import zlib - -from binascii import hexlify -from typing import Any, Dict, Match, Tuple - -DEX_MAGIC = b"dex\n" -DEX_MAGIC_RE = re.compile(rb"dex\n(\d{3})\x00") - -PROF_MAGIC = b"pro\x00" -PROF_010_P = b"010\x00" - -CLASSES_DEX_RE = re.compile(r"classes\d*\.dex") -ASSET_PROF = "assets/dexopt/baseline.prof" - -PG_MAP_ID_RE = re.compile(rb'(~~R8{"backend":"dex".*?"pg-map-id":")([0-9a-f]{7})(")') - -ATTRS = ("compress_type", "create_system", "create_version", "date_time", - "external_attr", "extract_version", "flag_bits") -LEVELS = (9, 6, 4, 1) - - -class Error(RuntimeError): - pass - - -# FIXME: is there a better alternative? -class ReproducibleZipInfo(zipfile.ZipInfo): - """Reproducible ZipInfo hack.""" - - if "_compresslevel" not in zipfile.ZipInfo.__slots__: # type: ignore[attr-defined] - if "compress_level" not in zipfile.ZipInfo.__slots__: # type: ignore[attr-defined] - raise Error("zipfile.ZipInfo has no ._compresslevel") - - _compresslevel: int - _override: Dict[str, Any] = {} - - def __init__(self, zinfo: zipfile.ZipInfo, **override: Any) -> None: - # pylint: disable=W0231 - if override: - self._override = {**self._override, **override} - for k in self.__slots__: - if hasattr(zinfo, k): - setattr(self, k, getattr(zinfo, k)) - - def __getattribute__(self, name: str) -> Any: - if name != "_override": - try: - return self._override[name] - except KeyError: - pass - return object.__getattribute__(self, name) - - -def fix_pg_map_id(input_dir: str, output_dir: str, map_id: str) -> None: - file_data = {} - for filename in [ASSET_PROF] + sorted(os.listdir(input_dir)): - if re.fullmatch(CLASSES_DEX_RE, filename) or filename == ASSET_PROF: - print(f"reading {filename!r}...") - with open(os.path.join(input_dir, *filename.split("/")), "rb") as fh: - file_data[filename] = fh.read() - _fix_pg_map_id(file_data, map_id) - for filename, data in file_data.items(): - print(f"writing {filename!r}...") - if "/" in filename: - os.makedirs(os.path.join(output_dir, *filename.split("/")[:-1]), exist_ok=True) - with open(os.path.join(output_dir, *filename.split("/")), "wb") as fh: - fh.write(data) - - -def fix_pg_map_id_apk(input_apk: str, output_apk: str, map_id: str) -> None: - with open(input_apk, "rb") as fh_raw: - with zipfile.ZipFile(input_apk) as zf_in: - with zipfile.ZipFile(output_apk, "w") as zf_out: - file_data = {} - for info in zf_in.infolist(): - if re.fullmatch(CLASSES_DEX_RE, info.filename) or info.filename == ASSET_PROF: - print(f"reading {info.filename!r}...") - file_data[info.filename] = zf_in.read(info) - _fix_pg_map_id(file_data, map_id) - for info in zf_in.infolist(): - attrs = {attr: getattr(info, attr) for attr in ATTRS} - zinfo = ReproducibleZipInfo(info, **attrs) - if info.compress_type == 8: - fh_raw.seek(info.header_offset) - n, m = struct.unpack(" 0: - ccrc = zlib.crc32(fh_raw.read(min(size, 4096)), ccrc) - size -= 4096 - with zf_in.open(info) as fh_in: - comps = {lvl: zlib.compressobj(lvl, 8, -15) for lvl in LEVELS} - ccrcs = {lvl: 0 for lvl in LEVELS} - while True: - data = fh_in.read(4096) - if not data: - break - for lvl in LEVELS: - ccrcs[lvl] = zlib.crc32(comps[lvl].compress(data), ccrcs[lvl]) - for lvl in LEVELS: - if ccrc == zlib.crc32(comps[lvl].flush(), ccrcs[lvl]): - zinfo._compresslevel = lvl - break - else: - raise Error(f"Unable to determine compresslevel for {info.filename!r}") - elif info.compress_type != 0: - raise Error(f"Unsupported compress_type {info.compress_type}") - if re.fullmatch(CLASSES_DEX_RE, info.filename) or info.filename == ASSET_PROF: - print(f"writing {info.filename!r}...") - zf_out.writestr(zinfo, file_data[info.filename]) - else: - with zf_in.open(info) as fh_in: - with zf_out.open(zinfo, "w") as fh_out: - while True: - data = fh_in.read(4096) - if not data: - break - fh_out.write(data) - - -def _fix_pg_map_id(file_data: Dict[str, bytes], map_id: str) -> None: - crcs = {} - for filename in file_data: - if re.fullmatch(CLASSES_DEX_RE, filename): - print(f"fixing {filename!r}...") - data = _fix_dex_id_checksum(file_data[filename], map_id.encode()) - file_data[filename] = data - crcs[filename] = zlib.crc32(data) - if ASSET_PROF in file_data: - print(f"fixing {ASSET_PROF!r}...") - file_data[ASSET_PROF] = _fix_prof_checksum(file_data[ASSET_PROF], crcs) - - -def _fix_dex_id_checksum(data: bytes, map_id: bytes) -> bytes: - def repl(m: Match[bytes]) -> bytes: - print(f"fixing pg-map-id: {m.group(2)!r} -> {map_id!r}") - return m.group(1) + map_id + m.group(3) - - magic = data[:8] - if magic[:4] != DEX_MAGIC or not DEX_MAGIC_RE.fullmatch(magic): - raise Error(f"Unsupported magic {magic!r}") - print(f"dex version={int(magic[4:7]):03d}") - checksum, signature = struct.unpack(" {hexlify(fixed_sig).decode()}") - fixed_data = fixed_sig + fixed_data - fixed_checksum = zlib.adler32(fixed_data) - print(f"fixing checksum: 0x{checksum:x} -> 0x{fixed_checksum:x}") - return magic + int.to_bytes(fixed_checksum, 4, "little") + fixed_data - - -def _fix_prof_checksum(data: bytes, crcs: Dict[str, int]) -> bytes: - magic, data = _split(data, 4) - version, data = _split(data, 4) - if magic == PROF_MAGIC: - if version == PROF_010_P: - print("prof version=010 P") - return PROF_MAGIC + PROF_010_P + _fix_prof_010_p_checksum(data, crcs) - else: - raise Error(f"Unsupported prof version {version!r}") - else: - raise Error(f"Unsupported magic {magic!r}") - - -def _fix_prof_010_p_checksum(data: bytes, crcs: Dict[str, int]) -> bytes: - num_dex_files, uncompressed_data_size, compressed_data_size, data = _unpack(" 0x{fixed_checksum:x}") - dex_data_headers.append(struct.pack( - " Any: - assert all(c in " Tuple[bytes, bytes]: - return data[:size], data[size:] - - -if __name__ == "__main__": - import argparse - parser = argparse.ArgumentParser(prog="fix-pg-map-id.py") - parser.add_argument("input_dir_or_apk", metavar="INPUT_DIR_OR_APK") - parser.add_argument("output_dir_or_apk", metavar="OUTPUT_DIR_OR_APK") - parser.add_argument("pg_map_id", metavar="PG_MAP_ID") - args = parser.parse_args() - if os.path.isdir(args.input_dir_or_apk): - fix_pg_map_id(args.input_dir_or_apk, args.output_dir_or_apk, args.pg_map_id) - else: - fix_pg_map_id_apk(args.input_dir_or_apk, args.output_dir_or_apk, args.pg_map_id) - -# vim: set tw=80 sw=4 sts=4 et fdm=marker : diff --git a/tools/release/inplace-fix.py b/tools/release/inplace-fix.py deleted file mode 100644 index a791e69140..0000000000 --- a/tools/release/inplace-fix.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/python3 -# encoding: utf-8 -# SPDX-FileCopyrightText: 2024 FC (Fay) Stegerman -# SPDX-License-Identifier: GPL-3.0-or-later. - -import argparse -import os -import shutil -import subprocess -import sys -import tempfile - -from typing import Optional, Tuple - -COMMANDS = ( - "fix-compresslevel", - "fix-files", - "fix-newlines", - "fix-pg-map-id", - "rm-files", - "sort-apk", - "sort-baseline", -) - -BUILD_TOOLS_WITH_BROKEN_ZIPALIGN = ("31.0.0", "32.0.0") -BUILD_TOOLS_WITH_PAGE_SIZE_FROM = "35.0.0-rc1" -SDK_ENV = ("ANDROID_HOME", "ANDROID_SDK", "ANDROID_SDK_ROOT") - - -def _zipalign_cmd(page_align: bool, page_size: Optional[int]) -> Tuple[str, ...]: - if page_align: - if page_size is not None: - return ("zipalign", "-P", str(page_size), "4") - return ("zipalign", "-p", "4") - return ("zipalign", "4") - - -ZIPALIGN = _zipalign_cmd(page_align=False, page_size=None) -ZIPALIGN_P = _zipalign_cmd(page_align=True, page_size=None) - - -class Error(RuntimeError): - pass - - -def inplace_fix(command: str, input_file: str, *args: str, - zipalign: bool = False, page_align: bool = False, - page_size: Optional[int] = None, internal: bool = False) -> None: - if command not in COMMANDS: - raise Error(f"Unknown command {command}") - exe, script = _script_cmd(command) - ext = os.path.splitext(input_file)[1] - with tempfile.TemporaryDirectory() as tdir: - fixed = os.path.join(tdir, "fixed" + ext) - run_command(exe, script, input_file, fixed, *args, trim=2) - if zipalign: - aligned = os.path.join(tdir, "aligned" + ext) - zac = zipalign_cmd(page_align=page_align, page_size=page_size, internal=internal) - run_command(*zac, fixed, aligned, trim=2) - print(f"[MOVE] {aligned} to {input_file}") - shutil.move(aligned, input_file) - else: - print(f"[MOVE] {fixed} to {input_file}") - shutil.move(fixed, input_file) - - -def zipalign_cmd(page_align: bool = False, page_size: Optional[int] = None, - internal: bool = False) -> Tuple[str, ...]: - """ - Find zipalign command using $PATH or $ANDROID_HOME etc. - - >>> zipalign_cmd() - ('zipalign', '4') - >>> zipalign_cmd(page_align=True) - ('zipalign', '-p', '4') - >>> zipalign_cmd(page_align=True, page_size=16) - ('zipalign', '-P', '16', '4') - >>> cmd = zipalign_cmd(page_align=True, page_size=16, internal=True) - >>> [x.split("/")[-1] for x in cmd] - ['python3', 'zipalign.py', '-P', '16', '4'] - >>> os.environ["PATH"] = "" - >>> for k in SDK_ENV: - ... os.environ[k] = "" - >>> cmd = zipalign_cmd() - >>> [x.split("/")[-1] for x in cmd] - ['python3', 'zipalign.py', '4'] - >>> os.environ["ANDROID_HOME"] = "test/fake-sdk" - >>> zipalign_cmd() - [SKIP BROKEN] 31.0.0 - [FOUND] test/fake-sdk/build-tools/30.0.3/zipalign - ('test/fake-sdk/build-tools/30.0.3/zipalign', '4') - >>> cmd = zipalign_cmd(page_align=True, page_size=16) - [SKIP TOO OLD] 31.0.0 - [SKIP TOO OLD] 30.0.3 - [SKIP TOO OLD] 26.0.2 - >>> [x.split("/")[-1] for x in cmd] - ['python3', 'zipalign.py', '-P', '16', '4'] - >>> os.environ["ANDROID_HOME"] = "test/fake-sdk-2" - >>> zipalign_cmd(page_align=True, page_size=16) - [FOUND] test/fake-sdk-2/build-tools/35.0.0-rc1/zipalign - ('test/fake-sdk-2/build-tools/35.0.0-rc1/zipalign', '-P', '16', '4') - - """ - cmd, *args = _zipalign_cmd(page_align, page_size) - if not internal: - if shutil.which(cmd): - return (cmd, *args) - for k in SDK_ENV: - if home := os.environ.get(k): - tools = os.path.join(home, "build-tools") - if os.path.exists(tools): - for vsn in sorted(os.listdir(tools), key=_vsn, reverse=True): - if page_size and _vsn(vsn) < _vsn(BUILD_TOOLS_WITH_PAGE_SIZE_FROM): - print(f"[SKIP TOO OLD] {vsn}") - continue - for s in BUILD_TOOLS_WITH_BROKEN_ZIPALIGN: - if vsn.startswith(s): - print(f"[SKIP BROKEN] {vsn}") - break - else: - c = os.path.join(tools, vsn, cmd) - if shutil.which(c): - print(f"[FOUND] {c}") - return (c, *args) - return (*_script_cmd(cmd), *args) - - -def _vsn(v: str) -> Tuple[int, ...]: - """ - >>> vs = "31.0.0 32.1.0-rc1 34.0.0-rc3 34.0.0 35.0.0-rc1".split() - >>> for v in sorted(vs, key=_vsn, reverse=True): - ... (_vsn(v), v) - ((35, 0, 0, 0, 1), '35.0.0-rc1') - ((34, 0, 0, 1, 0), '34.0.0') - ((34, 0, 0, 0, 3), '34.0.0-rc3') - ((32, 1, 0, 0, 1), '32.1.0-rc1') - ((31, 0, 0, 1, 0), '31.0.0') - """ - if "-rc" in v: - v = v.replace("-rc", ".0.", 1) - else: - v = v + ".1.0" - return tuple(int(x) if x.isdigit() else -1 for x in v.split(".")) - - -def _script_cmd(command: str) -> Tuple[str, str]: - script_dir = os.path.dirname(__file__) - for cmd in (command, command.replace("-", "_")): - script = os.path.join(script_dir, cmd + ".py") - if os.path.exists(script): - break - else: - raise Error(f"Script for {command} not found") - exe = sys.executable or "python3" - return exe, script - - -def run_command(*args: str, trim: int = 1) -> None: - targs = tuple(os.path.basename(a) for a in args[:trim]) + args[trim:] - print(f"[RUN] {' '.join(targs)}") - try: - subprocess.run(args, check=True) - except subprocess.CalledProcessError as e: - raise Error(f"{args[0]} command failed") from e - except FileNotFoundError as e: - raise Error(f"{args[0]} command not found") from e - - -def main() -> None: - prog = os.path.basename(sys.argv[0]) - usage = (f"{prog} [-h] [--zipalign] [--page-align] [--page-size N] [--internal]\n" - f"{len('usage: ' + prog) * ' '} COMMAND INPUT_FILE [...]") - epilog = f"Commands: {', '.join(COMMANDS)}." - parser = argparse.ArgumentParser(usage=usage, epilog=epilog) - parser.add_argument("--zipalign", action="store_true", - help="run zipalign after COMMAND") - parser.add_argument("--page-align", action="store_true", - help="run zipalign w/ -p option (implies --zipalign)") - parser.add_argument("--page-size", metavar="N", type=int, - help="run zipalign w/ -P N option (implies --page-align)") - parser.add_argument("--internal", action="store_true", - help="use zipalign.py instead of searching $PATH/$ANDROID_HOME/etc.") - parser.add_argument("command", metavar="COMMAND") - parser.add_argument("input_file", metavar="INPUT_FILE") - args, rest = parser.parse_known_args() - try: - inplace_fix(args.command, args.input_file, *rest, - zipalign=bool(args.zipalign or args.page_align or args.page_size), - page_align=bool(args.page_align or args.page_size), - page_size=args.page_size, internal=args.internal) - except Error as e: - print(f"Error: {e}.", file=sys.stderr) - sys.exit(1) - - -if __name__ == "__main__": - main() - -# vim: set tw=80 sw=4 sts=4 et fdm=marker : diff --git a/tools/release/release.sh b/tools/release/release.sh index 83ccc6d258..392074208a 100755 --- a/tools/release/release.sh +++ b/tools/release/release.sh @@ -225,15 +225,6 @@ printf "Unzipping the F-Droid artifact...\n" fdroidTargetPath="${targetPath}/fdroid" unzip "${targetPath}"/elementx-app-fdroid-apks-unsigned.zip -d "${fdroidTargetPath}" -printf "\n================================================================================\n" -printf "Patching the FDroid APKs using inplace-fix.py...\n" - -inplaceFixScript="./tools/release/inplace-fix.py" -python3 "${inplaceFixScript}" --page-size 16 fix-pg-map-id "${fdroidTargetPath}"/app-fdroid-arm64-v8a-release.apk '0000000' -python3 "${inplaceFixScript}" --page-size 16 fix-pg-map-id "${fdroidTargetPath}"/app-fdroid-armeabi-v7a-release.apk '0000000' -python3 "${inplaceFixScript}" --page-size 16 fix-pg-map-id "${fdroidTargetPath}"/app-fdroid-x86-release.apk '0000000' -python3 "${inplaceFixScript}" --page-size 16 fix-pg-map-id "${fdroidTargetPath}"/app-fdroid-x86_64-release.apk '0000000' - printf "\n================================================================================\n" printf "Signing the FDroid APKs...\n" From 39afaa1fe46b5fe65cdb8e7e4b8b88afe04f6cca Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 03:44:53 +0300 Subject: [PATCH 208/338] Do not render in-chat key verification requests in the timeline A m.key.verification.request sent from a client that supports in-chat verification carries a plain text body for clients that do not. Element X has no msgtype of its own for it, so it fell through to OtherMessageType and the timeline printed that body, telling the user their client does not support in-chat key verification right after a verification they had just completed. Drop the event when building the timeline items, as Element X iOS does. --- .../factories/TimelineItemsFactory.kt | 14 +++++- .../factories/TimelineItemsFactoryTest.kt | 46 +++++++++++++++++++ .../api/timeline/item/event/MessageType.kt | 9 +++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index 1679a6030a..9bf7aa3c32 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -21,6 +21,9 @@ import io.element.android.libraries.androidutils.diff.MutableListDiffCache import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.OtherMessageType import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow @@ -110,7 +113,11 @@ class TimelineItemsFactory( ): TimelineItem? { val timelineItem = when (val currentTimelineItem = timelineItems[index]) { - is MatrixTimelineItem.Event -> eventItemFactory.create(currentTimelineItem, index, timelineItems, roomMembers, renderReadReceipts) + is MatrixTimelineItem.Event -> if (currentTimelineItem.event.isKeyVerificationRequest()) { + null + } else { + eventItemFactory.create(currentTimelineItem, index, timelineItems, roomMembers, renderReadReceipts) + } is MatrixTimelineItem.Virtual -> virtualItemFactory.create(currentTimelineItem) MatrixTimelineItem.Other -> null } @@ -118,3 +125,8 @@ class TimelineItemsFactory( return timelineItem } } + +private fun EventTimelineItem.isKeyVerificationRequest(): Boolean { + val messageType = (content as? MessageContent)?.type + return messageType is OtherMessageType && messageType.isKeyVerificationRequest +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt index 53c39ee530..f93c8f196e 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt @@ -12,8 +12,10 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.OtherMessageType import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.timeline.aMessageContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem @@ -82,6 +84,50 @@ class TimelineItemsFactoryTest { ).inOrder() } + @Test + fun `a key verification request is not rendered`() = runTest { + val factory = aTimelineItemsFactory( + config = TimelineItemsFactoryConfig( + computeReadReceipts = false, + computeReactions = false, + ) + ) + val items = listOf( + MatrixTimelineItem.Event( + uniqueId = UniqueId("event-0"), + event = anEventTimelineItem( + sender = A_USER_ID, + content = aMessageContent( + body = "Alice is requesting to verify your key, but your client does not support in-chat key verification.", + messageType = OtherMessageType( + msgType = "m.key.verification.request", + body = "Alice is requesting to verify your key, but your client does not support in-chat key verification.", + ), + ), + ), + ), + MatrixTimelineItem.Event( + uniqueId = UniqueId("event-1"), + event = anEventTimelineItem( + sender = A_USER_ID, + content = aMessageContent(body = "A regular message"), + ), + ), + ) + factory.timelineItems.test { + factory.replaceWith( + timelineItems = items, + roomMembers = emptyList(), + renderReadReceipts = false, + ) + val bodies = awaitItem() + .filterIsInstance() + .map { (it.content as TimelineItemTextContent).body } + assertThat(bodies).containsExactly("A regular message") + cancelAndIgnoreRemainingEvents() + } + } + private suspend fun TestScope.groupPositionsOf(timestamps: List): List { val factory = aTimelineItemsFactory( config = TimelineItemsFactoryConfig( diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt index 2f61b8f58a..f2bd56ac57 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt @@ -128,4 +128,11 @@ sealed interface GalleryItemType { data class OtherMessageType( val msgType: String, val body: String, -) : MessageType +) : MessageType { + /** + * A `m.key.verification.request` carries a body meant for clients that cannot handle in-chat + * verification, so it must not be rendered as a message. + */ + val isKeyVerificationRequest: Boolean + get() = msgType == "m.key.verification.request" +} From c5a878249bf6bb03dd1ae376597fb7e5095d78b4 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 03:48:44 +0300 Subject: [PATCH 209/338] Open the user profile of a user id that contains a slash A slash is valid in the local part of a user id, but in a permalink it also separates the identifier from an event id, so the SDK could not tell the two apart and the link fell back to opening matrix.to in a browser. Only a user id can start with an @, so recover it before handing the URI to the SDK, and only when the identifier really contains a slash so every other permalink keeps taking the same path. --- .../impl/permalink/DefaultPermalinkParser.kt | 20 +++++++++ .../permalink/DefaultPermalinkParserTest.kt | 44 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt index e59530e874..a5f372d7d1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt @@ -8,11 +8,13 @@ package io.element.android.libraries.matrix.impl.permalink +import android.net.Uri import androidx.core.net.toUri import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId @@ -51,6 +53,10 @@ class DefaultPermalinkParser( matrixToConverter.convert(uri) ?: return PermalinkData.FallbackLink(uri) } + matrixToUri.userIdWithSlash()?.let { userId -> + return PermalinkData.UserLink(userId = userId) + } + val result = runCatchingExceptions { parseMatrixEntityFrom(matrixToUri.toString()) }.getOrNull() @@ -84,3 +90,17 @@ class DefaultPermalinkParser( } } } + +/** + * A slash is allowed in the local part of a user id, but it also separates an identifier from an + * event id in a permalink, so [parseMatrixEntityFrom] cannot tell the two apart. Only a user id can + * start with an `@`, so recover it here rather than letting the ambiguity reach the SDK. + */ +private fun Uri.userIdWithSlash(): UserId? { + val identifier = fragment + ?.removePrefix("/") + ?.substringBefore('?') + ?: return null + if (!identifier.startsWith("@") || !identifier.contains('/')) return null + return identifier.takeIf { MatrixPatterns.isUserId(it) }?.let(::UserId) +} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt new file mode 100644 index 0000000000..416213a0b2 --- /dev/null +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.permalink + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.permalink.PermalinkData +import io.element.android.tests.testutils.robolectric.RobolectricTest +import org.junit.Test + +class DefaultPermalinkParserTest : RobolectricTest() { + @Test + fun `a matrix-to link to a user id with a slash is parsed as a user link`() { + val result = createParser().parse("https://matrix.to/#/@it/sme:matrix.org") + assertThat(result).isEqualTo(PermalinkData.UserLink(UserId("@it/sme:matrix.org"))) + } + + @Test + fun `an escaped slash in a user id is parsed as a user link`() { + val result = createParser().parse("https://matrix.to/#/@it%2Fsme:matrix.org") + assertThat(result).isEqualTo(PermalinkData.UserLink(UserId("@it/sme:matrix.org"))) + } + + @Test + fun `an element web link to a user id with a slash is parsed as a user link`() { + val result = createParser().parse("https://app.element.io/#/user/@it/sme:matrix.org") + assertThat(result).isEqualTo(PermalinkData.UserLink(UserId("@it/sme:matrix.org"))) + } + + @Test + fun `a user id with a slash that is not a valid user id is not a user link`() { + val result = createParser().parse("https://example.org/foo/@it/sme") + assertThat(result).isInstanceOf(PermalinkData.FallbackLink::class.java) + } + + private fun createParser() = DefaultPermalinkParser( + matrixToConverter = DefaultMatrixToConverter(), + ) +} From 7dcd55ee0846bcdbf09743643ad31318c05caacc Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 03:55:09 +0300 Subject: [PATCH 210/338] Hide the encryption badge when key storage is set up on the server The settings badge was shown for every unknown backup state, so a session whose key backup lives on the server but has not been reported by the SDK kept a red dot next to Encryption while the Encryption screen itself showed nothing to do. Resolve an unknown backup state the way the Encryption screen already does, by asking whether a backup exists on the server, and only badge the entry when there is none. --- .../indicator/impl/DefaultIndicatorService.kt | 14 ++++++++--- .../impl/DefaultIndicatorServiceTest.kt | 24 +++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/libraries/indicator/impl/src/main/kotlin/io/element/android/libraries/indicator/impl/DefaultIndicatorService.kt b/libraries/indicator/impl/src/main/kotlin/io/element/android/libraries/indicator/impl/DefaultIndicatorService.kt index 0629799c74..1dd2a4c64c 100644 --- a/libraries/indicator/impl/src/main/kotlin/io/element/android/libraries/indicator/impl/DefaultIndicatorService.kt +++ b/libraries/indicator/impl/src/main/kotlin/io/element/android/libraries/indicator/impl/DefaultIndicatorService.kt @@ -9,11 +9,14 @@ package io.element.android.libraries.indicator.impl import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.di.SessionScope import io.element.android.libraries.indicator.api.IndicatorService @@ -43,12 +46,17 @@ class DefaultIndicatorService( override fun showSettingChatBackupIndicator(): State { val backupState by encryptionService.backupStateStateFlow.collectAsState() val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() + var doesBackupExistOnServer by remember { mutableStateOf(null) } + + LaunchedEffect(backupState) { + if (backupState == BackupState.UNKNOWN) { + doesBackupExistOnServer = encryptionService.doesBackupExistOnServer().getOrNull() + } + } return remember { derivedStateOf { - val showForBackup = backupState in listOf( - BackupState.UNKNOWN, - ) + val showForBackup = backupState == BackupState.UNKNOWN && doesBackupExistOnServer != true val showForRecovery = recoveryState in listOf( RecoveryState.DISABLED, RecoveryState.INCOMPLETE, diff --git a/libraries/indicator/impl/src/test/kotlin/io/element/android/libraries/indicator/impl/DefaultIndicatorServiceTest.kt b/libraries/indicator/impl/src/test/kotlin/io/element/android/libraries/indicator/impl/DefaultIndicatorServiceTest.kt index cd861ef530..3782e2c9f1 100644 --- a/libraries/indicator/impl/src/test/kotlin/io/element/android/libraries/indicator/impl/DefaultIndicatorServiceTest.kt +++ b/libraries/indicator/impl/src/test/kotlin/io/element/android/libraries/indicator/impl/DefaultIndicatorServiceTest.kt @@ -42,8 +42,10 @@ class DefaultIndicatorServiceTest { } @Test - fun `test - showSettingChatBackupIndicator is true when BackupState is UNKNOWN`() = runTest { - val encryptionService = FakeEncryptionService() + fun `test - showSettingChatBackupIndicator is true when BackupState is UNKNOWN and no backup exists on the server`() = runTest { + val encryptionService = FakeEncryptionService().apply { + givenDoesBackupExistOnServerResult(Result.success(false)) + } val sessionVerificationService = FakeSessionVerificationService() val sut = DefaultIndicatorService( sessionVerificationService = sessionVerificationService, @@ -61,6 +63,24 @@ class DefaultIndicatorServiceTest { } } + @Test + fun `test - showSettingChatBackupIndicator is false when BackupState is UNKNOWN but a backup exists on the server`() = runTest { + val encryptionService = FakeEncryptionService().apply { + givenDoesBackupExistOnServerResult(Result.success(true)) + } + val sessionVerificationService = FakeSessionVerificationService() + val sut = DefaultIndicatorService( + sessionVerificationService = sessionVerificationService, + encryptionService = encryptionService, + ) + moleculeFlow(RecompositionMode.Immediate) { + sut.showSettingChatBackupIndicator().value + }.test { + assertThat(awaitItem()).isTrue() + assertThat(awaitItem()).isFalse() + } + } + @Test fun `test - showSettingChatBackupIndicator is true when recoveryState is DISABLED`() = runTest { val encryptionService = FakeEncryptionService() From 4c5baf8d6051d1d58117c993911ec4224827a830 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 04:01:05 +0300 Subject: [PATCH 211/338] Send a concrete mime type for media shared with a wildcard one Some apps share an image with the mime type image/* rather than a concrete one. That value was carried straight into the uploaded event, so the message reached the room with an unusable media type and bridges rejected it as unsupported. Resolve a wildcard subtype to a default for its top level type before processing the upload, reusing the mapping the download path already applies so the SDK can pick the right extension. --- .../libraries/core/mimetype/MimeTypes.kt | 13 +++++++ .../libraries/core/mimetype/MimeTypesTest.kt | 35 +++++++++++++++++++ .../matrix/impl/media/RustMediaLoader.kt | 13 ++----- .../impl/AndroidMediaPreProcessor.kt | 16 +++++---- .../impl/AndroidMediaPreProcessorTest.kt | 17 ++++++++- 5 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 libraries/core/src/test/kotlin/io/element/android/libraries/core/mimetype/MimeTypesTest.kt diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt index e7962f8bd6..dee840ea48 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt @@ -63,4 +63,17 @@ object MimeTypes { val subType = components.last() return subType.isNotBlank() && subType != "*" } + + /** + * A mime type with a wildcard subtype is a filter, not a media type, so replace it with a + * concrete default for its top level type. + */ + fun String?.withDefaultSubtype(): String = when { + this == null -> OctetStream + hasSubtype(this) -> this + this == Images -> Jpeg + this == Videos -> Mp4 + this == Audio -> Mp3 + else -> OctetStream + } } diff --git a/libraries/core/src/test/kotlin/io/element/android/libraries/core/mimetype/MimeTypesTest.kt b/libraries/core/src/test/kotlin/io/element/android/libraries/core/mimetype/MimeTypesTest.kt new file mode 100644 index 0000000000..7ba68fa9b1 --- /dev/null +++ b/libraries/core/src/test/kotlin/io/element/android/libraries/core/mimetype/MimeTypesTest.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.core.mimetype + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.mimetype.MimeTypes.withDefaultSubtype +import org.junit.Test + +class MimeTypesTest { + @Test + fun `withDefaultSubtype keeps a mime type that already has a subtype`() { + assertThat(MimeTypes.Png.withDefaultSubtype()).isEqualTo(MimeTypes.Png) + assertThat(MimeTypes.Mp4.withDefaultSubtype()).isEqualTo(MimeTypes.Mp4) + assertThat(MimeTypes.Pdf.withDefaultSubtype()).isEqualTo(MimeTypes.Pdf) + } + + @Test + fun `withDefaultSubtype replaces a wildcard subtype with a concrete one`() { + assertThat(MimeTypes.Images.withDefaultSubtype()).isEqualTo(MimeTypes.Jpeg) + assertThat(MimeTypes.Videos.withDefaultSubtype()).isEqualTo(MimeTypes.Mp4) + assertThat(MimeTypes.Audio.withDefaultSubtype()).isEqualTo(MimeTypes.Mp3) + } + + @Test + fun `withDefaultSubtype falls back to octet stream`() { + assertThat(MimeTypes.Any.withDefaultSubtype()).isEqualTo(MimeTypes.OctetStream) + assertThat("text/*".withDefaultSubtype()).isEqualTo(MimeTypes.OctetStream) + assertThat(null.withDefaultSubtype()).isEqualTo(MimeTypes.OctetStream) + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt index d1401d2624..3231c29403 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt @@ -11,7 +11,7 @@ package io.element.android.libraries.matrix.impl.media import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.extensions.runCatchingExceptions -import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.core.mimetype.MimeTypes.withDefaultSubtype import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaFile import io.element.android.libraries.matrix.api.media.MediaSource @@ -71,15 +71,8 @@ class RustMediaLoader( val mediaFile = innerClient.getMediaFile( mediaSource = mediaSource, filename = filename, - mimeType = when { - mimeType == null -> MimeTypes.OctetStream - MimeTypes.hasSubtype(mimeType) -> mimeType - // Fallback to a default mime type based on the main type, so that the SDK can create a file with the correct extension. - mimeType == MimeTypes.Images -> MimeTypes.Jpeg - mimeType == MimeTypes.Videos -> MimeTypes.Mp4 - mimeType == MimeTypes.Audio -> MimeTypes.Mp3 - else -> MimeTypes.OctetStream - }, + // Fallback to a default mime type based on the main type, so that the SDK can create a file with the correct extension. + mimeType = mimeType.withDefaultSubtype(), useCache = useCache, tempDir = cacheDirectory.path, ) diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt index b374e530f4..4ef8a2410c 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt @@ -29,6 +29,7 @@ import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo +import io.element.android.libraries.core.mimetype.MimeTypes.withDefaultSubtype import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.FileInfo @@ -82,15 +83,16 @@ class AndroidMediaPreProcessor( mediaOptimizationConfig: MediaOptimizationConfig, ): Result = withContext(coroutineDispatchers.computation) { runCatchingExceptions { + val resolvedMimeType = mimeType.withDefaultSubtype() val result = when { - mimeType == MimeTypes.Svg -> processSvgImage(uri, mimeType) - mimeType.isMimeTypeImage() -> { - val shouldBeCompressed = mediaOptimizationConfig.compressImages && mimeType !in notCompressibleImageTypes - processImage(uri, mimeType, shouldBeCompressed) + resolvedMimeType == MimeTypes.Svg -> processSvgImage(uri, resolvedMimeType) + resolvedMimeType.isMimeTypeImage() -> { + val shouldBeCompressed = mediaOptimizationConfig.compressImages && resolvedMimeType !in notCompressibleImageTypes + processImage(uri, resolvedMimeType, shouldBeCompressed) } - mimeType.isMimeTypeVideo() -> processVideo(uri, mimeType, mediaOptimizationConfig.videoCompressionPreset) - mimeType.isMimeTypeAudio() -> processAudio(uri, mimeType) - else -> processFile(uri, mimeType) + resolvedMimeType.isMimeTypeVideo() -> processVideo(uri, resolvedMimeType, mediaOptimizationConfig.videoCompressionPreset) + resolvedMimeType.isMimeTypeAudio() -> processAudio(uri, resolvedMimeType) + else -> processFile(uri, resolvedMimeType) } if (deleteOriginal) { tryOrNull { diff --git a/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt index 3fb639aa9b..b3d5bc805b 100644 --- a/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt +++ b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt @@ -65,9 +65,24 @@ class AndroidMediaPreProcessorTest : RobolectricTest() { assertThat(height).isEqualTo(2268) } + @Test + fun `test processing an image shared with a wildcard mime type`() = runTest { + val mediaUploadInfo = process( + asset = assetImageJpeg, + mimeType = MimeTypes.Images, + mediaOptimizationConfig = MediaOptimizationConfig( + compressImages = true, + videoCompressionPreset = VideoCompressionPreset.STANDARD, + ), + ) + val info = mediaUploadInfo as MediaUploadInfo.Image + assertThat(info.imageInfo.mimetype).isEqualTo(MimeTypes.Jpeg) + } + private suspend fun TestScope.process( asset: Asset, mediaOptimizationConfig: MediaOptimizationConfig, + mimeType: String = asset.mimeType, sdkIntVersion: Int = Build.VERSION_CODES.P, deleteOriginal: Boolean = false, ): MediaUploadInfo { @@ -81,7 +96,7 @@ class AndroidMediaPreProcessorTest : RobolectricTest() { val file = getFileFromAssets(context, asset.filename) val result = sut.process( uri = file.toUri(), - mimeType = asset.mimeType, + mimeType = mimeType, deleteOriginal = deleteOriginal, mediaOptimizationConfig = mediaOptimizationConfig, ) From 1a3b89de12c5fef63a3ca3dfdd9876cd37c9818a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2026 07:33:18 +0000 Subject: [PATCH 212/338] Update dependency io.github.sergio-sastre.ComposablePreviewScanner:android to v0.9.2 (#7431) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 17eb8ff751..6b5a75bc3d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -175,7 +175,7 @@ test_truth = "com.google.truth:truth:1.4.5" test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.22" test_robolectric = "org.robolectric:robolectric:4.16.1" test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" } -test_composable_preview_scanner = "io.github.sergio-sastre.ComposablePreviewScanner:android:0.9.1" +test_composable_preview_scanner = "io.github.sergio-sastre.ComposablePreviewScanner:android:0.9.2" test_detekt_api = { module = "io.gitlab.arturbosch.detekt:detekt-api", version.ref = "detekt" } test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version.ref = "detekt" } From aa6f70530a819e27f53198c50e15664cb16fb676 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 15:18:10 +0200 Subject: [PATCH 213/338] [LinkNewDevice] Move the timeout management when linking a new desktop device to the LinkNewDeviceFlowNode so that it can also be used for the DesktopNotice screen. --- .../linknewdevice/impl/LinkNewDesktopHandler.kt | 11 ++++++++--- .../linknewdevice/impl/LinkNewDeviceFlowNode.kt | 8 ++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt index 7afb9a7623..6f5beafbd9 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt @@ -53,15 +53,20 @@ class LinkNewDesktopHandler( timerJob = null } - fun createNewHandler() { - resetJobs() - handler = matrixClient.createLinkDesktopHandler().getOrNull() + fun startTimer() { + timerJob?.cancel() timerJob = sessionScope.launch { delay(2.minutes) linkDesktopStepFlow.emit(LinkDesktopStep.Error(ErrorType.Expired("Scanning QrCode took too long."))) } } + fun createNewHandler() { + currentJob?.cancel() + currentJob = null + handler = matrixClient.createLinkDesktopHandler().getOrNull() + } + fun reset() { resetJobs() sessionScope.launch { diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index afa570f0a9..a71e32bad5 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -263,6 +263,8 @@ class LinkNewDeviceFlowNode( } LinkDeviceType.Desktop -> { linkNewDesktopHandler.reset() + // Ensure unlock state does not last longer than the timeout for scanning the QR code, so start the timer after unlock + linkNewDesktopHandler.startTimer() backstack.push(NavTarget.DesktopNotice) } } @@ -278,10 +280,14 @@ class LinkNewDeviceFlowNode( NavTarget.DesktopNotice -> { val callback = object : DesktopNoticeNode.Callback { override fun navigateBack() { + // reset the timer when the user leaves the screen (user will have to authenticate again) + linkNewDesktopHandler.reset() backstack.pop() } override fun navigateToQrCodeScanner() { + // reset the timer when the user enters the next screen (they clicked on "Ready to scan") + linkNewDesktopHandler.startTimer() backstack.push(NavTarget.DesktopScanQrCode) } } @@ -290,6 +296,8 @@ class LinkNewDeviceFlowNode( NavTarget.DesktopScanQrCode -> { val callback = object : ScanQrCodeNode.Callback { override fun cancel() { + // reset the timer when the user goes back to the DesktopNoticeNode + linkNewDesktopHandler.startTimer() backstack.pop() } } From d0a1011638e08c7b1138bc99d7e9ecf7c21bf25b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Aug 2026 15:48:35 +0200 Subject: [PATCH 214/338] Add documentation on LinkNewDesktopHandler and LinkNewMobileHandler --- .../impl/LinkNewDesktopHandler.kt | 21 ++++++++++++++++++ .../impl/LinkNewMobileHandler.kt | 22 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt index 6f5beafbd9..bff81a3e9e 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDesktopHandler.kt @@ -39,6 +39,10 @@ class LinkNewDesktopHandler( LinkDesktopStep.Uninitialized ) + /** + * A [StateFlow] that emits the current step of the link desktop process. + * It starts with [LinkDesktopStep.Uninitialized] and will emit other steps as they occur. + */ val stepFlow: StateFlow get() = linkDesktopStepFlow.asStateFlow() @@ -53,6 +57,9 @@ class LinkNewDesktopHandler( timerJob = null } + /** + * Starts (or restarts) a timer that will emit an error in [stepFlow] after 2 minutes. + */ fun startTimer() { timerJob?.cancel() timerJob = sessionScope.launch { @@ -61,12 +68,20 @@ class LinkNewDesktopHandler( } } + /** + * Creates a new [LinkDesktopHandler] and cancels any existing job. + * This should be called when the user wants to retry scanning a QR code. + */ fun createNewHandler() { currentJob?.cancel() currentJob = null handler = matrixClient.createLinkDesktopHandler().getOrNull() } + /** + * Resets the state of the handler and cancels any existing job. + * This should be called when the user wants to start over. + */ fun reset() { resetJobs() sessionScope.launch { @@ -74,6 +89,12 @@ class LinkNewDesktopHandler( } } + /** + * Handles the scanned QR code data. + * This should be called when the user scans a QR code. + * + * @param data The scanned QR code data as a byte array. + */ fun onScannedCode(data: ByteArray) { resetJobs() val currentHandler = handler diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt index 39c9c6fef3..9436c0e901 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt @@ -40,9 +40,19 @@ class LinkNewMobileHandler( LinkMobileStep.Uninitialized ) + /** + * A [StateFlow] that emits the current step of the link mobile process. + * It starts with [LinkMobileStep.Uninitialized] and will emit other steps as they occur. + */ val stepFlow: StateFlow get() = linkMobileStepFlow.asStateFlow() + /** + * Creates a new [LinkMobileHandler] and starts the linking process. + * If [forRotating] is true, it indicates that this is a rotation of the QR code. + * + * @param forRotating Whether this is a rotation of the QR code. Defaults to false. + */ fun createAndStartNewHandler(forRotating: Boolean = false) { Timber.tag(loggerTag.value).d("createAndStartNewHandler()") currentJob?.cancel() @@ -62,6 +72,10 @@ class LinkNewMobileHandler( } } + /** + * Resets the handler and cancels any ongoing job. + * This will also emit [LinkMobileStep.Uninitialized] to the [stepFlow]. + */ fun reset() { currentJob?.cancel() currentJob = null @@ -70,10 +84,18 @@ class LinkNewMobileHandler( } } + /** + * Rotates the QR code by creating a new handler and starting the linking process. + * This is typically called when a timeout occurs for the current QR code. + */ fun rotateQrCode() { createAndStartNewHandler(forRotating = true) } + /** + * Handles the case where there are too many QR code rotations. + * This will reset the handler and emit an error to the [stepFlow]. + */ fun onTooManyRotation() { reset() sessionScope.launch { From 2ac11ebe14681f9326eb1f0f96e48ad2bb2e63e7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 17 Aug 2026 10:30:57 +0200 Subject: [PATCH 215/338] Clarify the comments --- .../features/linknewdevice/impl/LinkNewDeviceFlowNode.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index a71e32bad5..b31aca8cb2 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -280,13 +280,13 @@ class LinkNewDeviceFlowNode( NavTarget.DesktopNotice -> { val callback = object : DesktopNoticeNode.Callback { override fun navigateBack() { - // reset the timer when the user leaves the screen (user will have to authenticate again) + // Stop the timer when the user leaves the screen (user will have to authenticate again) linkNewDesktopHandler.reset() backstack.pop() } override fun navigateToQrCodeScanner() { - // reset the timer when the user enters the next screen (they clicked on "Ready to scan") + // Start again the timer when the user enters the next screen (they clicked on "Ready to scan") linkNewDesktopHandler.startTimer() backstack.push(NavTarget.DesktopScanQrCode) } @@ -296,7 +296,7 @@ class LinkNewDeviceFlowNode( NavTarget.DesktopScanQrCode -> { val callback = object : ScanQrCodeNode.Callback { override fun cancel() { - // reset the timer when the user goes back to the DesktopNoticeNode + // start again the timer when the user goes back to the DesktopNoticeNode linkNewDesktopHandler.startTimer() backstack.pop() } From ddd89f46df37d8524bff054e1c2835a933054ac6 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:33:09 +0300 Subject: [PATCH 216/338] Resolve the System theme from the application configuration Selecting a Light or Dark theme makes AppCompatDelegate rewrite the activity configuration to the forced night mode. isSystemInDarkTheme reads that same configuration, so switching back to System kept showing the previously forced theme. Read the night mode from the application configuration instead, which AppCompat leaves untouched and itself uses to expand MODE_NIGHT_FOLLOW_SYSTEM. The activity configuration is still used as the recomposition key so a system-wide theme change is picked up. --- .../element/android/compound/theme/Theme.kt | 23 +++++++++++-- .../android/compound/theme/ThemeTest.kt | 34 +++++++++++++++---- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/theme/Theme.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/Theme.kt index bf5932da59..e519984e9f 100644 --- a/libraries/compound/src/main/kotlin/io/element/android/compound/theme/Theme.kt +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/Theme.kt @@ -8,8 +8,11 @@ package io.element.android.compound.theme -import androidx.compose.foundation.isSystemInDarkTheme +import android.content.res.Configuration import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -27,12 +30,28 @@ private fun Theme.coerceBlackTheme(allowBlackTheme: Boolean): Theme { @Composable fun Theme.isDark(): Boolean { return when (this) { - Theme.System -> isSystemInDarkTheme() + Theme.System -> isSystemThemeDark() Theme.Dark, Theme.Black -> true Theme.Light -> false } } +/** + * Whether the system-wide theme is dark. + * + * This deliberately does not use [androidx.compose.foundation.isSystemInDarkTheme], which reads the activity configuration: `AppCompatDelegate` rewrites + * that configuration whenever a non-system theme is selected, so it keeps reporting the previously forced value. The application configuration is left + * untouched by AppCompat and is what AppCompat itself reads to expand `MODE_NIGHT_FOLLOW_SYSTEM`. + */ +@Composable +private fun isSystemThemeDark(): Boolean { + val applicationContext = LocalContext.current.applicationContext + val activityConfiguration = LocalConfiguration.current + return remember(activityConfiguration) { + applicationContext.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES + } +} + fun Flow.mapToTheme(allowBlackTheme: Boolean): Flow = map { when (it) { null -> Theme.System diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/theme/ThemeTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/theme/ThemeTest.kt index 3f43cfea67..57ae9dd332 100644 --- a/libraries/compound/src/test/kotlin/io/element/android/compound/theme/ThemeTest.kt +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/theme/ThemeTest.kt @@ -11,19 +11,22 @@ package io.element.android.compound.theme import android.content.res.Configuration import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.tests.testutils.robolectric.RobolectricTest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test +import org.robolectric.RuntimeEnvironment -class ThemeTest { +class ThemeTest : RobolectricTest() { @Test fun `isDark for System dark returns true`() { `isDark for System`( - uiMode = Configuration.UI_MODE_NIGHT_YES, + systemUiMode = Configuration.UI_MODE_NIGHT_YES, expected = true, ) } @@ -31,21 +34,38 @@ class ThemeTest { @Test fun `isDark for System light return false`() { `isDark for System`( - uiMode = Configuration.UI_MODE_NIGHT_NO, + systemUiMode = Configuration.UI_MODE_NIGHT_NO, expected = false, ) } - fun `isDark for System`( - uiMode: Int, + @Test + fun `isDark for System ignores the night mode AppCompat forced on the activity`() { + `isDark for System`( + systemUiMode = Configuration.UI_MODE_NIGHT_YES, + activityUiMode = Configuration.UI_MODE_NIGHT_NO, + expected = true, + ) + `isDark for System`( + systemUiMode = Configuration.UI_MODE_NIGHT_NO, + activityUiMode = Configuration.UI_MODE_NIGHT_YES, + expected = false, + ) + } + + private fun `isDark for System`( + systemUiMode: Int, + activityUiMode: Int = systemUiMode, expected: Boolean, ) = runTest { + val application = RuntimeEnvironment.getApplication() + application.resources.configuration.uiMode = systemUiMode moleculeFlow(RecompositionMode.Immediate) { var result: Boolean? = null CompositionLocalProvider( - // Let set the system to dark + LocalContext provides application, LocalConfiguration provides Configuration().apply { - this.uiMode = uiMode + this.uiMode = activityUiMode }, ) { result = Theme.System.isDark() From 9a4daa19b22cff0fc559a94e0963fc78ff3983ea Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:33:10 +0300 Subject: [PATCH 217/338] Keep the focused login field visible above the keyboard On shorter screens the software keyboard can cover the field the user is typing into on the login and account provider screens, because nothing scrolls the content back into the reduced viewport once the keyboard is shown. Apply the existing bringIntoViewOnImeVisible modifier to those fields, the same way the recovery key screen already does. --- .../login/impl/screens/loginpassword/LoginPasswordView.kt | 3 +++ .../screens/searchaccountprovider/SearchAccountProviderView.kt | 2 ++ 2 files changed, 5 insertions(+) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index 06a1641644..453c716e86 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -56,6 +56,7 @@ import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.form.textFieldState +import io.element.android.libraries.designsystem.modifiers.bringIntoViewOnImeVisible import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusNext import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -191,6 +192,7 @@ private fun LoginForm( enabled = !isLoading, modifier = Modifier .fillMaxWidth() + .bringIntoViewOnImeVisible() .onTabOrEnterKeyFocusNext(focusManager) .testTag(TestTags.loginEmailUsername) .semantics { @@ -243,6 +245,7 @@ private fun LoginForm( enabled = !isLoading, modifier = Modifier .fillMaxWidth() + .bringIntoViewOnImeVisible() .onTabOrEnterKeyFocusNext(focusManager) .testTag(TestTags.loginPassword) .semantics { diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt index 8335dde112..ffe7192991 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt @@ -52,6 +52,7 @@ import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubti import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.form.textFieldState +import io.element.android.libraries.designsystem.modifiers.bringIntoViewOnImeVisible import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusNext import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -110,6 +111,7 @@ fun SearchAccountProviderView( modifier = Modifier .fillMaxWidth() .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) + .bringIntoViewOnImeVisible() .onTabOrEnterKeyFocusNext(focusManager) .testTag(TestTags.changeServerServer), onValueChange = { From 932a4d013c4582d71c1f0c5dfac1b4c59f349cfc Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:33:41 +0300 Subject: [PATCH 218/338] Relax image compression so photos keep more detail Images are downscaled against a 640px reference, which makes inSampleSize halve the source one step further than the documented intent: the KDoc says images should be sent at up to 1280x1280, but a 12000x3916 photo is sent as 3006x979. Text in screenshots and detail in photos is lost at that size. Raise the reference to 1280. inSampleSize is a power of two, so this moves each image one step up rather than to an exact size. --- .../mediaupload/impl/AndroidMediaPreProcessor.kt | 4 ++-- .../mediaupload/impl/AndroidMediaPreProcessorTest.kt | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt index b374e530f4..87c4a22d39 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt @@ -62,10 +62,10 @@ class AndroidMediaPreProcessor( /** * Used for calculating `inSampleSize` for bitmaps. * - * *Note*: Ideally, this should result in images of up to (but not included) 1280x1280 being sent. However, images with very different width and height + * *Note*: Ideally, this should result in images of up to (but not included) 2560x2560 being sent. However, images with very different width and height * values may surpass this limit. (i.e.: an image of `480x3000px` would have `inSampleSize=1` and be sent as is). */ - private const val IMAGE_SCALE_REF_SIZE = 640 + private const val IMAGE_SCALE_REF_SIZE = 1280 private val notCompressibleImageTypes = listOf(MimeTypes.Gif, MimeTypes.WebP) } diff --git a/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt index 3fb639aa9b..d08784c834 100644 --- a/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt +++ b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt @@ -208,10 +208,10 @@ class AndroidMediaPreProcessorTest : RobolectricTest() { assertThat(info.thumbnailFile).isNotNull() assertThat(info.imageInfo).isEqualTo( ImageInfo( - height = 979, - width = 3006, + height = 1958, + width = 6012, mimetype = MimeTypes.Jpeg, - size = 84_845, + size = 682_492, ThumbnailInfo(height = 244, width = 751, mimetype = MimeTypes.Jpeg, size = 7_178), thumbnailSource = null, blurhash = "K07gBzX=j_D4xZjoaSe,s:" @@ -233,10 +233,10 @@ class AndroidMediaPreProcessorTest : RobolectricTest() { assertThat(info.thumbnailFile).isNull() assertThat(info.imageInfo).isEqualTo( ImageInfo( - height = 979, - width = 3_006, + height = 1_958, + width = 6_012, mimetype = MimeTypes.Jpeg, - size = 84_845, + size = 682_492, thumbnailInfo = null, thumbnailSource = null, blurhash = null, From a740423343b96d6c91e7bb9e7eb13ebd38676b02 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 17 Aug 2026 11:34:52 +0200 Subject: [PATCH 219/338] Sync strings. --- .../src/main/res/values-ar/translations.xml | 6 + .../src/main/res/values-fa/translations.xml | 1 + .../src/main/res/values-ar/translations.xml | 7 + .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-ar/translations.xml | 10 + .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-ar/translations.xml | 8 + .../src/main/res/values-fa/translations.xml | 1 + .../src/main/res/values-ar/translations.xml | 36 ++ .../src/main/res/values-es/translations.xml | 20 + .../src/main/res/values-fa/translations.xml | 28 +- .../src/main/res/values-ar/translations.xml | 14 + .../src/main/res/values-es/translations.xml | 5 +- .../src/main/res/values-fa/translations.xml | 7 +- .../src/main/res/values-ar/translations.xml | 15 + .../src/main/res/values-es/translations.xml | 4 +- .../src/main/res/values-ar/translations.xml | 56 ++ .../src/main/res/values-es/translations.xml | 11 +- .../src/main/res/values-fa/translations.xml | 10 +- .../src/main/res/values-ar/translations.xml | 18 + .../src/main/res/values-fa/translations.xml | 3 + .../src/main/res/values-ar/translations.xml | 9 + .../src/main/res/values-es/translations.xml | 6 +- .../src/main/res/values-fa/translations.xml | 4 + .../src/main/res/values-ar/translations.xml | 34 + .../src/main/res/values-es/translations.xml | 6 +- .../src/main/res/values-fa/translations.xml | 15 +- .../src/main/res/values-ar/translations.xml | 40 ++ .../src/main/res/values-fa/translations.xml | 13 + .../src/main/res/values-ar/translations.xml | 10 + .../src/main/res/values-es/translations.xml | 3 + .../src/main/res/values-fa/translations.xml | 1 + .../src/main/res/values-ar/translations.xml | 60 ++ .../src/main/res/values-es/translations.xml | 20 + .../src/main/res/values-fa/translations.xml | 20 + .../src/main/res/values-fi/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 1 + .../src/main/res/values-ar/translations.xml | 6 + .../src/main/res/values-es/translations.xml | 6 + .../src/main/res/values-fa/translations.xml | 6 + .../src/main/res/values-ar/translations.xml | 46 ++ .../src/main/res/values-es/translations.xml | 4 +- .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-ar/translations.xml | 109 ++++ .../src/main/res/values-es/translations.xml | 16 +- .../src/main/res/values-fa/translations.xml | 19 + .../src/main/res/values-ar/translations.xml | 18 + .../src/main/res/values-es/translations.xml | 26 +- .../src/main/res/values-ar/translations.xml | 114 ++++ .../src/main/res/values-es/translations.xml | 35 +- .../src/main/res/values-fa/translations.xml | 35 + .../src/main/res/values-fi/translations.xml | 4 + .../src/main/res/values-fr/translations.xml | 4 + .../src/main/res/values-ar/translations.xml | 13 + .../src/main/res/values-es/translations.xml | 5 + .../src/main/res/values-fa/translations.xml | 4 + .../src/main/res/values-ar/translations.xml | 20 + .../src/main/res/values-fa/translations.xml | 1 + .../src/main/res/values-ar/translations.xml | 115 ++++ .../src/main/res/values-es/translations.xml | 49 +- .../src/main/res/values-fa/translations.xml | 43 ++ .../src/main/res/values-fi/translations.xml | 10 +- .../src/main/res/values-fr/translations.xml | 8 + .../src/main/res/values-ar/translations.xml | 7 + .../src/main/res/values-ar/translations.xml | 22 + .../src/main/res/values-es/translations.xml | 4 + .../src/main/res/values-fa/translations.xml | 5 + .../src/main/res/values-ar/translations.xml | 8 + .../src/main/res/values-fa/translations.xml | 2 + .../src/main/res/values-ar/translations.xml | 98 +++ .../src/main/res/values-es/translations.xml | 33 +- .../src/main/res/values-fa/translations.xml | 19 + .../src/main/res/values-ar/translations.xml | 5 + .../src/main/res/values-fa/translations.xml | 1 + .../src/main/res/values-ar/translations.xml | 188 ++++++ .../src/main/res/values-be/translations.xml | 1 - .../src/main/res/values-bg/translations.xml | 1 - .../src/main/res/values-ca/translations.xml | 2 - .../src/main/res/values-cy/translations.xml | 2 +- .../src/main/res/values-da/translations.xml | 2 - .../src/main/res/values-el/translations.xml | 1 - .../src/main/res/values-es/translations.xml | 50 +- .../src/main/res/values-et/translations.xml | 3 +- .../src/main/res/values-eu/translations.xml | 1 - .../src/main/res/values-fa/translations.xml | 59 +- .../src/main/res/values-fi/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 5 +- .../src/main/res/values-hr/translations.xml | 2 - .../src/main/res/values-it/translations.xml | 1 - .../src/main/res/values-ja/translations.xml | 2 - .../src/main/res/values-ka/translations.xml | 1 - .../src/main/res/values-ko/translations.xml | 1 - .../src/main/res/values-nb/translations.xml | 1 - .../src/main/res/values-nl/translations.xml | 1 - .../src/main/res/values-pl/translations.xml | 1 + .../main/res/values-pt-rBR/translations.xml | 1 - .../src/main/res/values-pt/translations.xml | 3 +- .../src/main/res/values-ro/translations.xml | 1 + .../src/main/res/values-sv/translations.xml | 1 - .../src/main/res/values-tr/translations.xml | 1 - .../src/main/res/values-uk/translations.xml | 3 +- .../src/main/res/values-ur/translations.xml | 2 - .../src/main/res/values-uz/translations.xml | 1 - .../src/main/res/values-vi/translations.xml | 2 - .../src/main/res/values-zh/translations.xml | 3 +- .../impl/src/main/res/values/localazy.xml | 3 +- .../src/main/res/values-ar/translations.xml | 7 + .../src/main/res/values-ar/translations.xml | 5 + .../src/main/res/values-ar/translations.xml | 22 + .../src/main/res/values-es/translations.xml | 2 + .../src/main/res/values-fa/translations.xml | 4 + .../src/main/res/values-ar/translations.xml | 95 +++ .../src/main/res/values-es/translations.xml | 48 +- .../src/main/res/values-fa/translations.xml | 32 +- .../src/main/res/values-ar/translations.xml | 50 ++ .../src/main/res/values-es/translations.xml | 19 +- .../src/main/res/values-fa/translations.xml | 47 +- .../src/main/res/values-ar/translations.xml | 8 + .../src/main/res/values-ar/translations.xml | 36 ++ .../src/main/res/values-es/translations.xml | 23 + .../src/main/res/values-fa/translations.xml | 15 + .../src/main/res/values-ar/translations.xml | 12 + .../src/main/res/values-es/translations.xml | 2 +- .../src/main/res/values-fa/translations.xml | 2 +- .../src/main/res/values-fr/translations.xml | 2 +- .../src/main/res/values-ar/translations.xml | 19 + .../src/main/res/values-ar/translations.xml | 54 ++ .../src/main/res/values-es/translations.xml | 10 +- .../src/main/res/values-fa/translations.xml | 17 + .../src/main/res/values-ar/translations.xml | 4 + .../src/main/res/values-ar/translations.xml | 5 + .../src/main/res/values-ar/translations.xml | 13 + .../src/main/res/values-es/translations.xml | 2 + .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-ar/translations.xml | 73 +++ .../src/main/res/values-fa/translations.xml | 4 + .../src/main/res/values-ar/translations.xml | 9 + .../src/main/res/values-es/translations.xml | 2 + .../src/main/res/values-fa/translations.xml | 2 + .../src/main/res/values-ar/translations.xml | 22 + .../src/main/res/values-es/translations.xml | 7 +- .../src/main/res/values-fa/translations.xml | 4 + .../src/main/res/values-ar/translations.xml | 7 + .../src/main/res/values-ar/translations.xml | 5 + .../src/main/res/values-ar/translations.xml | 140 ++++ .../src/main/res/values-es/translations.xml | 24 + .../src/main/res/values-fa/translations.xml | 29 + .../src/main/res/values-fi/translations.xml | 5 + .../src/main/res/values-fr/translations.xml | 5 + .../impl/src/main/res/values/localazy.xml | 2 + .../src/main/res/values-ar/translations.xml | 11 + .../src/main/res/values-ar/translations.xml | 14 + .../src/main/res/values-fa/translations.xml | 4 + .../src/main/res/values-ar/translations.xml | 33 + .../src/main/res/values-es/translations.xml | 3 + .../src/main/res/values-fa/translations.xml | 1 + .../src/main/res/values-ar/translations.xml | 12 + .../src/main/res/values-ar/translations.xml | 611 ++++++++++++++++++ .../src/main/res/values-es/translations.xml | 246 ++++++- .../src/main/res/values-fa/translations.xml | 199 +++++- .../src/main/res/values-fi/translations.xml | 23 + .../src/main/res/values-fr/translations.xml | 61 +- 162 files changed, 3726 insertions(+), 165 deletions(-) create mode 100644 appnav/src/main/res/values-ar/translations.xml create mode 100644 features/analytics/api/src/main/res/values-ar/translations.xml create mode 100644 features/analytics/impl/src/main/res/values-ar/translations.xml create mode 100644 features/call/impl/src/main/res/values-ar/translations.xml create mode 100644 features/createroom/impl/src/main/res/values-ar/translations.xml create mode 100644 features/deactivation/impl/src/main/res/values-ar/translations.xml create mode 100644 features/ftue/impl/src/main/res/values-ar/translations.xml create mode 100644 features/home/impl/src/main/res/values-ar/translations.xml create mode 100644 features/invite/impl/src/main/res/values-ar/translations.xml create mode 100644 features/invitepeople/impl/src/main/res/values-ar/translations.xml create mode 100644 features/joinroom/impl/src/main/res/values-ar/translations.xml create mode 100644 features/knockrequests/impl/src/main/res/values-ar/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values-ar/translations.xml create mode 100644 features/linknewdevice/impl/src/main/res/values-ar/translations.xml create mode 100644 features/location/impl/src/main/res/values-ar/translations.xml create mode 100644 features/location/impl/src/main/res/values-es/translations.xml create mode 100644 features/location/impl/src/main/res/values-fa/translations.xml create mode 100644 features/lockscreen/impl/src/main/res/values-ar/translations.xml create mode 100644 features/login/impl/src/main/res/values-ar/translations.xml create mode 100644 features/logout/impl/src/main/res/values-ar/translations.xml create mode 100644 features/messages/impl/src/main/res/values-ar/translations.xml create mode 100644 features/poll/api/src/main/res/values-ar/translations.xml create mode 100644 features/poll/impl/src/main/res/values-ar/translations.xml create mode 100644 features/preferences/impl/src/main/res/values-ar/translations.xml create mode 100644 features/rageshake/api/src/main/res/values-ar/translations.xml create mode 100644 features/rageshake/impl/src/main/res/values-ar/translations.xml create mode 100644 features/reportroom/impl/src/main/res/values-ar/translations.xml create mode 100644 features/rolesandpermissions/impl/src/main/res/values-ar/translations.xml create mode 100644 features/roomaliasresolver/impl/src/main/res/values-ar/translations.xml create mode 100644 features/roomdetails/impl/src/main/res/values-ar/translations.xml create mode 100644 features/roomdetailsedit/impl/src/main/res/values-ar/translations.xml create mode 100644 features/roomdirectory/impl/src/main/res/values-ar/translations.xml create mode 100644 features/roommembermoderation/impl/src/main/res/values-ar/translations.xml create mode 100644 features/securebackup/impl/src/main/res/values-ar/translations.xml create mode 100644 features/securityandprivacy/impl/src/main/res/values-ar/translations.xml create mode 100644 features/signedout/impl/src/main/res/values-ar/translations.xml create mode 100644 features/space/impl/src/main/res/values-ar/translations.xml create mode 100644 features/startchat/impl/src/main/res/values-ar/translations.xml create mode 100644 features/userprofile/shared/src/main/res/values-ar/translations.xml create mode 100644 features/verifysession/impl/src/main/res/values-ar/translations.xml create mode 100644 libraries/androidutils/src/main/res/values-ar/translations.xml create mode 100644 libraries/dateformatter/impl/src/main/res/values-ar/translations.xml create mode 100644 libraries/emoji/impl/src/main/res/values-ar/translations.xml create mode 100644 libraries/eventformatter/impl/src/main/res/values-ar/translations.xml create mode 100644 libraries/matrixui/src/main/res/values-ar/translations.xml create mode 100644 libraries/mediaviewer/impl/src/main/res/values-ar/translations.xml create mode 100644 libraries/permissions/api/src/main/res/values-ar/translations.xml create mode 100644 libraries/permissions/impl/src/main/res/values-ar/translations.xml create mode 100644 libraries/push/impl/src/main/res/values-ar/translations.xml create mode 100644 libraries/pushproviders/firebase/src/main/res/values-ar/translations.xml create mode 100644 libraries/pushproviders/unifiedpush/src/main/res/values-ar/translations.xml create mode 100644 libraries/textcomposer/impl/src/main/res/values-ar/translations.xml create mode 100644 libraries/troubleshoot/impl/src/main/res/values-ar/translations.xml create mode 100644 libraries/ui-strings/src/main/res/values-ar/translations.xml diff --git a/appnav/src/main/res/values-ar/translations.xml b/appnav/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..ca3a804097 --- /dev/null +++ b/appnav/src/main/res/values-ar/translations.xml @@ -0,0 +1,6 @@ + + + "تسجيل الخروج والترقية" + "لم يعد %1$s يدعم البروتوكول القديم. يرجى تسجيل الخروج ثم تسجيل الدخول مرة أخرى لمتابعة استخدام التطبيق." + "لم يعد خادمك المنزلي يدعم البروتوكول القديم. يرجى تسجيل الخروج ثم تسجيل الدخول مرة أخرى لمتابعة استخدام التطبيق." + diff --git a/appnav/src/main/res/values-fa/translations.xml b/appnav/src/main/res/values-fa/translations.xml index 16bb7fd3e2..dfff863365 100644 --- a/appnav/src/main/res/values-fa/translations.xml +++ b/appnav/src/main/res/values-fa/translations.xml @@ -2,4 +2,5 @@ "خروج و ارتقا" "‏%1$s دیگر از شیوه‌نامهٔ قدیمی پشتیبانی نمی‌کند. لطفاً برای ادامهٔ استفاده از کاره، خارج شده و دوباره وارد شوید." + "سرور خانگی شما دیگر از پروتکل قدیمی پشتیبانی نمی‌کند. لطفاً برای ادامه استفاده از برنامه، از سیستم خارج شده و دوباره وارد شوید." diff --git a/features/analytics/api/src/main/res/values-ar/translations.xml b/features/analytics/api/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..449a0a01cb --- /dev/null +++ b/features/analytics/api/src/main/res/values-ar/translations.xml @@ -0,0 +1,7 @@ + + + "مشاركة بيانات استخدام مجهولة لمساعدتنا على تتبع المشاكل." + "يمكنك قراءة كافة شروطنا %1$s." + "هنا" + "مشاركة البيانات الإحصائية" + diff --git a/features/analytics/api/src/main/res/values-pl/translations.xml b/features/analytics/api/src/main/res/values-pl/translations.xml index a3347582cc..37848b4c3f 100644 --- a/features/analytics/api/src/main/res/values-pl/translations.xml +++ b/features/analytics/api/src/main/res/values-pl/translations.xml @@ -1,6 +1,6 @@ - "Udostępniaj anonimowe dane użytkowania, aby pomóc nam identyfikować problemy." + "Udostępniaj anonimowe dane użytkowania, aby pomóc nam ulepszać aplikację." "Przeczytaj nasze warunki użytkowania %1$s." "tutaj" "Udostępniaj dane analityczne" diff --git a/features/analytics/impl/src/main/res/values-ar/translations.xml b/features/analytics/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..8d1bf460d6 --- /dev/null +++ b/features/analytics/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,10 @@ + + + "لن نسجّل أو ننشئ ملفات شخصية لأي معلومات شخصية" + "مشاركة بيانات استخدام مجهولة لمساعدتنا على تتبع المشاكل." + "يمكنك قراءة كافة شروطنا %1$s." + "هنا" + "يمكنك إطفاء هذا الخيار في أي وقت" + "لن نشارك بياناتك مع أطراف ثالثة" + "ساعدنا على التحسن %1$s" + diff --git a/features/analytics/impl/src/main/res/values-pl/translations.xml b/features/analytics/impl/src/main/res/values-pl/translations.xml index 61da8408ad..c58d630ce9 100644 --- a/features/analytics/impl/src/main/res/values-pl/translations.xml +++ b/features/analytics/impl/src/main/res/values-pl/translations.xml @@ -1,7 +1,7 @@ "Nie będziemy rejestrować ani profilować żadnych danych osobistych" - "Udostępniaj anonimowe dane użytkowania, aby pomóc nam identyfikować problemy." + "Udostępniaj anonimowe dane użytkowania, aby pomóc nam ulepszać aplikację." "Przeczytaj nasze warunki użytkowania %1$s." "tutaj" "Możesz to wyłączyć w dowolnym momencie" diff --git a/features/call/impl/src/main/res/values-ar/translations.xml b/features/call/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..612dd16a28 --- /dev/null +++ b/features/call/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,8 @@ + + + "مكالمة جارية" + "اضغط للعودة إلى المكالمة" + "☎️ الاتصال يتمّ" + "لا يدعم Element Call استخدام أجهزة الصوت عبر Bluetooth في إصدار Android هذا. يرجى اختيار جهاز صوت آخر." + "مكالمة Element Call واردة" + diff --git a/features/call/impl/src/main/res/values-fa/translations.xml b/features/call/impl/src/main/res/values-fa/translations.xml index c58ebfd5c6..622be3d428 100644 --- a/features/call/impl/src/main/res/values-fa/translations.xml +++ b/features/call/impl/src/main/res/values-fa/translations.xml @@ -3,5 +3,6 @@ "تماس خروجی" "زدن برای بازگشت به تماس" "تماس در جریان ☎️" + "برنامه Element Call در این نسخه اندروید از دستگاه‌های صوتی بلوتوث پشتیبانی نمی‌کند. لطفاً دستگاه صوتی دیگری را انتخاب کنید." "تماس المنتی ورودی" diff --git a/features/createroom/impl/src/main/res/values-ar/translations.xml b/features/createroom/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..7cb356739a --- /dev/null +++ b/features/createroom/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,36 @@ + + + "إنشاء غرفة جديدة" + "دعوة أشخاص" + "حصل خطأ أثناء إنشاء الغرفة" + "تعذّر إنشاء المساحة بسبب خطأ غير معروف. حاول مرة أخرى لاحقًا." + "إضافة اسم…" + "غرفة جديدة" + "مساحة جديدة" + "لا يدخل إلا المدعوّون." + "خاصة" + "بإمكان الجميع العثور على هذه الغرفة. +يمكنك تغيير هذا من إعدادات الغرفة متى شئت." + "يمكن لأي شخص الانضمام." + "عامة" + "يمكن لأي شخص طلب الانضمام، لكن يجب أن يقبل الطلب مسؤول أو مشرف." + "السماح بطلبات الانضمام" + "يمكن لأي شخص في %1$s الانضمام، لكن يجب على الآخرين طلب الوصول." + "طلب الانضمام" + "يمكن للمدعوين فقط الانضمام." + "خاصة" + "يمكن لأي شخص الانضمام." + "عامة" + "يمكن لأي شخص في %1$s الانضمام." + "قياسي" + "من يمكنه الوصول" + "تحتاج إلى عنوان حتى تظهر الغرفة في الدليل العام." + "العنوان" + "ظهور الغرفة" + "(لا توجد مساحة)" + "عدم الإضافة إلى مساحة" + "لم يتم اختيار أي مساحة" + "إضافة إلى مساحة" + "الموضوع (اختياري)" + "إضافة وصف…" + diff --git a/features/createroom/impl/src/main/res/values-es/translations.xml b/features/createroom/impl/src/main/res/values-es/translations.xml index 740e0b70cc..e3ceccbb83 100644 --- a/features/createroom/impl/src/main/res/values-es/translations.xml +++ b/features/createroom/impl/src/main/res/values-es/translations.xml @@ -3,14 +3,34 @@ "Nueva sala" "Invitar personas" "Se ha producido un error al crear la sala" + "No se pudo crear el espacio debido a un error desconocido. Inténtalo de nuevo más tarde." + "Añadir nombre…" + "Nueva sala" + "Nuevo espacio" "Solo las personas invitadas pueden acceder." + "Privado" "Cualquiera puede encontrar esta sala. Puedes cambiar esto en cualquier momento en los ajustes de la sala." + "Cualquiera puede unirse." + "Público" "Cualquiera puede solicitar unirse, pero un administrador o un moderador tendrá que aceptar la solicitud" "Permitir solicitar unirse" + "Cualquiera en %1$s puede unirse, pero el resto debe solicitar acceso." + "Solicitar unirse" + "Solo las personas invitadas pueden unirse." + "Privado" "Cualquiera puede unirse." + "Público" + "Cualquiera de %1$s puede unirse." + "Estándar" + "Quién tiene acceso" "Necesitarás una dirección de sala para que sea visible en el directorio de salas públicas." "Dirección" "Visibilidad de la sala" + "(ningún espacio)" + "No añadir a un espacio" + "No se ha seleccionado ningún espacio" + "Añadir a un espacio" "Tema (opcional)" + "Añadir descripción…" diff --git a/features/createroom/impl/src/main/res/values-fa/translations.xml b/features/createroom/impl/src/main/res/values-fa/translations.xml index 821d55af67..c3ec327d5b 100644 --- a/features/createroom/impl/src/main/res/values-fa/translations.xml +++ b/features/createroom/impl/src/main/res/values-fa/translations.xml @@ -3,12 +3,34 @@ "اتاق جدید" "دعوت افراد" "هنگام ایجاد اتاق خطایی رخ داد" + "به دلیل خطای ناشناخته‌ای، فضا ایجاد نشد. بعداً دوباره امتحان کنید." + "افزودن نام…" + "اتاق جدید" + "فضای جدید" "تنها افراد دعوت شده می‌توانند بپیوندند." + "خصوصی" "هرکسی می‌تواند اتاق را بیابد. می‌توانید بعداً در تظیمات اتاق عوضش کنید." - "درخواست دعوت" - "هرکسی می‌تواند به این اتاق بپیوندد" - "نشانی اتاق" + "هر کسی می‌تواند عضو شود." + "عمومی" + "هر کسی می‌تواند درخواست عضویت بدهد، اما یک مدیر یا ناظر باید درخواست را بپذیرد." + "اجازه درخواست عضویت" + "هر کسی در %1$s می‌تواند عضو شود، اما بقیه باید درخواست دسترسی بدهند." + "درخواست عضویت" + "تنها افراد دعوت شده می‌توانند بپیوندند." + "خصوصی" + "هر کسی می‌تواند عضو شود." + "عمومی" + "هر کسی که در %1$s باشد می‌تواند عضو شود." + "استاندارد" + "چه کسی دسترسی دارد" + "برای اینکه بتوانید آن را در فهرست عمومی مشاهده کنید، به یک آدرس نیاز دارید." + "آدرس" "نمایانی اتاق" + "(بدون فضا)" + "به یک فضا اضافه نکنید" + "فضایی انتخاب نشده است" + "به فضا اضافه کنید" "موضوع (اختیاری)" + "اضافه کردن توضیحات…" diff --git a/features/deactivation/impl/src/main/res/values-ar/translations.xml b/features/deactivation/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..d0fa5afe94 --- /dev/null +++ b/features/deactivation/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,14 @@ + + + "يرجى تأكيد رغبتك في حذف حسابك. لا يمكن التراجع عن هذا الإجراء." + "حذف جميع رسائلي" + "تحذير: قد يرى المستخدمون في المستقبل محادثات غير مكتملة." + "حذف حسابك %1$s، وسيؤدي إلى:" + "إجراء لا رجعة فيه" + "%1$s (لن تتمكن من تسجيل الدخول مرة أخرى، ولا يمكن إعادة استخدام معرّفك)." + "تعطيل حسابك نهائيًا" + "إزالتك من جميع غرف الدردشة." + "حذف معلومات حسابك من خادم الهوية لدينا." + "ستظل رسائلك مرئية للمستخدمين المسجلين، لكنها لن تكون متاحة للمستخدمين الجدد أو غير المسجلين إذا اخترت حذفها." + "حذف الحساب" + diff --git a/features/deactivation/impl/src/main/res/values-es/translations.xml b/features/deactivation/impl/src/main/res/values-es/translations.xml index 17ae73d6c8..dc3e090e06 100644 --- a/features/deactivation/impl/src/main/res/values-es/translations.xml +++ b/features/deactivation/impl/src/main/res/values-es/translations.xml @@ -1,13 +1,14 @@ - "Confirma que quieres desactivar tu cuenta. Esta acción no se puede deshacer." + "Confirma que quieres eliminar tu cuenta. Esta acción no se puede deshacer." "Borrar todos mis mensajes" "Advertencia: Futuros usuarios pueden ver conversaciones incompletas." - "Desactivar tu cuenta es %1$s:" + "Eliminar tu cuenta es %1$s:" "irreversible" "%1$s tu cuenta (no podrás volver a iniciar sesión y tu ID no se podrá volver a utilizar)." "Inhabilitará permanentemente" "Te eliminará de todas las salas de chat." "Eliminará la información de tu cuenta de nuestro servidor de identidad." "Tus mensajes seguirán siendo visibles para los usuarios registrados, pero no estarán disponibles para los usuarios nuevos o no registrados si decides eliminarlos." + "Eliminar cuenta" diff --git a/features/deactivation/impl/src/main/res/values-fa/translations.xml b/features/deactivation/impl/src/main/res/values-fa/translations.xml index 5c4eb00d62..38828d5e60 100644 --- a/features/deactivation/impl/src/main/res/values-fa/translations.xml +++ b/features/deactivation/impl/src/main/res/values-fa/translations.xml @@ -1,9 +1,14 @@ + "لطفاً تأیید کنید که می‌خواهید حساب کاربری خود را حذف کنید. این اقدام قابل بازگشت نیست." "حذف همهٔ پیام‌هایم" + "هشدار: کاربران آینده ممکن است مکالمات ناقص را ببینند." + "حذف حساب کاربری شما %1$s است، این کار باعث می‌شود:" "بازگشت‌ناپذیر" + "حساب کاربری خود را %1$s کنید (شما نمی‌توانید دوباره وارد سیستم شوید و شناسه شما قابل استفاده مجدد نیست)." "از کار انداختن دایمی" "برداشتنتان از همهٔ اتاق‌های گپ." "حذف اطّلاعات حسابتان از کارساز هویت." - "غیرفعّال‌سازی حساب" + "پیام‌های شما همچنان برای کاربران ثبت‌نام‌شده قابل مشاهده خواهند بود، اما اگر تصمیم به حذف آنها بگیرید، برای کاربران جدید یا ثبت‌نام‌نشده در دسترس نخواهند بود." + "حذف حساب کاربری" diff --git a/features/ftue/impl/src/main/res/values-ar/translations.xml b/features/ftue/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..afd974d4bb --- /dev/null +++ b/features/ftue/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,15 @@ + + + "لا يمكنك التأكيد؟" + "إنشاء مفتاح استرداد جديد" + "اختر طريقة التحقق لإعداد المراسلة الآمنة." + "تأكيد هويتك الرقمية" + "استخدام جهاز آخر" + "استخدام مفتاح الاسترداد" + "يمكنك الآن قراءة الرسائل أو إرسالها بأمان، ويمكن لأي شخص تدردش معه أن يثق بهذا الجهاز أيضًا." + "تم التحقق من الجهاز" + "استخدام جهاز آخر" + "في انتظار الجهاز الآخر…" + "يمكنك تغيير إعداداتك لاحقاً." + "السماح بالإشعارات وعدم تفويت أي رسالة" + diff --git a/features/ftue/impl/src/main/res/values-es/translations.xml b/features/ftue/impl/src/main/res/values-es/translations.xml index 8f49a77e0d..9b232f8dab 100644 --- a/features/ftue/impl/src/main/res/values-es/translations.xml +++ b/features/ftue/impl/src/main/res/values-es/translations.xml @@ -2,8 +2,8 @@ "¿No puedes confirmar?" "Crear una nueva clave de recuperación" - "Verifica este dispositivo para configurar la mensajería segura." - "Confirma que eres tú" + "Elige cómo verificar para configurar la mensajería segura." + "Confirma tu identidad digital" "Usar otro dispositivo" "Usar clave de recuperación" "Ahora puedes leer o enviar mensajes de forma segura y cualquier persona con la que chatees también puede confiar en este dispositivo." diff --git a/features/home/impl/src/main/res/values-ar/translations.xml b/features/home/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..59f4d6c821 --- /dev/null +++ b/features/home/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,56 @@ + + + "عطّل تحسين البطارية لهذا التطبيق للتأكد من استلام جميع الإشعارات." + "تعطيل التحسين" + "هل لا تصلك الإشعارات؟" + "تم تحديث صوت إشعاراتك، ليصبح أوضح وأسرع وأقل إزعاجًا." + "حدّثنا الأصوات لديك" + "يتم نسخ دردشاتك احتياطيًا تلقائيًا بتشفير من طرف إلى طرف. لاستعادة هذه النسخة الاحتياطية والاحتفاظ بهويتك الرقمية إذا فقدت الوصول إلى جميع أجهزتك، ستحتاج إلى مفتاح الاسترداد." + "الحصول على مفتاح الاسترداد" + "احتفِظ بنسخة احتياطية من دردشاتك" + "أكّد مفتاح الاسترداد الخاص بك للحفاظ على الوصول إلى تخزين المفاتيح وسجل الرسائل." + "أدخل مفتاح الاسترداد" + "هل نسيت مفتاح الاسترداد؟" + "تخزين المفاتيح لديك غير متزامن" + "للتأكد من عدم تفويت أي مكالمة مهمة، يُرجى تغيير إعداداتك للسماح بإشعارات ملء الشاشة عندما يكون هاتفك مقفلاً." + "حسّن تجربة مكالماتك" + "المحادثات" + "المساحات" + "أمتأكد من رغبتك برفض دعوة للانضمام إلى %1$s؟" + "رفض الدعوة" + "أمتأكد من رغبتك برفض المحادثة الخاصة مع %1$s؟" + "رفض المحادثة" + "لا دعوات" + "دعاك %1$s (%2$s)" + "هذه العملية تُجرى لمرة واحدة، شكراً على انتظارك." + "إعداد حسابك" + "إنشاء محادثة جديد أو غرفة" + "مسح عوامل التصفية" + "ابدأ الآن بمراسلة من تشاء." + "لا محادثات بعد." + "المفضلة" + "يمكنك إضافة محادثة إلى المفضلة من إعدادات المحادثة. +يمكنك الآن إلغاء تحديد عوامل التصفية لرؤية محادثاتك الأخرى" + "ليست لديك أي محادثات مفضلة بعد" + "الدعوات" + "ليس لديك أي دعوات قيد الانتظار." + "منخفضة الأولوية" + "ليست لديك أي دردشات منخفضة الأولوية بعد" + "يمكنك إلغاء تحديد عوامل التصفية لرؤية محادثاتك الأخرى" + "ليست لديك محادثات لهذا الاختيار" + "الأشخاص" + "ليست لديك أي رسائل مباشرة بعد" + "الغرف" + "لست في أي غرفة بعد" + "غير المقروءة" + "تهانينا! +ليست لديك أي رسائل غير مقروءة!" + "تم إرسال طلب الانضمام" + "المحادثات" + "وضع علامة كمقروء" + "وضع علامة كغير مقروء" + "تمت ترقية هذه الغرفة" + "مساحاتك" + "يبدو أنك تستخدم جهازاً جديداً. تحقق باستخدام جهاز آخر ليصبح لك وصول إلى رسائلك المشفرة." + "أثبت هويتك" + diff --git a/features/home/impl/src/main/res/values-es/translations.xml b/features/home/impl/src/main/res/values-es/translations.xml index ed6cfdc990..e841d6ebf8 100644 --- a/features/home/impl/src/main/res/values-es/translations.xml +++ b/features/home/impl/src/main/res/values-es/translations.xml @@ -3,9 +3,11 @@ "Deshabilita la optimización de la batería en esta aplicación para asegurarte de que recibes notificaciones." "Deshabilitar la optimización" "¿No llegan las notificaciones?" - "Recupera tu identidad criptográfica y tu historial de mensajes con una clave de recuperación si has perdido todos tus dispositivos actuales." - "Configurar la recuperación" - "Configura la recuperación para proteger tu cuenta" + "Se ha actualizado tu sonido de notificación: más claro, rápido y menos disruptivo." + "Hemos actualizado tus sonidos" + "Tus chats se respaldan automáticamente con cifrado de extremo a extremo. Para restaurar esta copia de seguridad y conservar tu identidad digital si pierdes el acceso a todos tus dispositivos, necesitarás tu clave de recuperación." + "Obtener clave de recuperación" + "Copia de seguridad de tus chats" "Confirma tu clave de recuperación para mantener el acceso a tu almacén de claves y al historial de mensajes." "Introduce tu clave de recuperación" "¿Olvidaste tu clave de recuperación?" @@ -13,6 +15,7 @@ "Para asegurarte de que nunca te pierdas una llamada importante, modifica tus ajustes para permitir notificaciones a pantalla completa cuando el teléfono esté bloqueado." "Mejora tu experiencia de llamada" "Chats" + "Espacios" "¿Estás seguro de que quieres rechazar la invitación a unirte a %1$s?" "Rechazar la invitación" "¿Estás seguro de que quieres rechazar este chat privado con %1$s?" @@ -32,6 +35,7 @@ Por ahora, puedes deseleccionar los filtros para ver tus otros chats" "Invitaciones" "No tienes ninguna invitación pendiente." "Prioridad baja" + "Aún no tienes ningún chat de prioridad baja" "Puedes deseleccionar filtros para ver tus otros chats." "No tienes chats para esta selección" "Personas" @@ -46,6 +50,7 @@ Por ahora, puedes deseleccionar los filtros para ver tus otros chats" "Marcar como leído" "Marcar como no leído" "Esta sala ha sido renovada" + "Tus espacios" "Parece que estás usando un nuevo dispositivo. Verifica que eres tú para acceder a tus mensajes cifrados." "Verifica que eres tú" diff --git a/features/home/impl/src/main/res/values-fa/translations.xml b/features/home/impl/src/main/res/values-fa/translations.xml index b0c6a30bc4..64ce86ddc7 100644 --- a/features/home/impl/src/main/res/values-fa/translations.xml +++ b/features/home/impl/src/main/res/values-fa/translations.xml @@ -3,12 +3,16 @@ "از کار انداختن بهینه‌سازی باتری برای این کاره برای اطمینان از گرفتن همهٔ آگاهی‌ها." "از کار انداختن بهینه سازی" "آگاهی‌ها نمی‌رسند؟" - "بازگردانی تاریخچهٔ پیام‌ها و هویت رمزنگاشته‌تان با کلید بازیابی در صورت از دست دادن همهٔ افزاره‌های موجودتان." + "پینگ اعلان شما به‌روزرسانی شده است—واضح‌تر، سریع‌تر و کم‌دردسرتر." + "ما صداهای شما را تازه کرده‌ایم" + "چت‌های شما به طور خودکار با رمزگذاری سراسری پشتیبان‌گیری می‌شوند. برای بازیابی این پشتیبان‌گیری و حفظ هویت دیجیتال خود در زمانی که دسترسی به تمام دستگاه‌هایتان را از دست می‌دهید، به کلید بازیابی خود نیاز خواهید داشت." "دریافت کلید بازیابی" - "برپایی بازیابی" + "از چت‌هایتان پشتیبان بگیرید" "کلید بازیابی خود را تأیید کنید تا دسترسی به حافظه کلیدها و تاریخچه پیام‌هایتان حفظ شود ." "ورود کلید بازیابیتان" + "کلید بازیابی خود را فراموش کرده‌اید؟" "ذخیره‌ساز کلیدتان از هم‌گام بودن در آمده" + "برای اطمینان از اینکه هرگز تماس مهمی را از دست نمی‌دهید، لطفاً تنظیمات خود را تغییر دهید تا وقتی تلفنتان قفل است، اعلان‌ها به صورت تمام صفحه نمایش داده شوند." "بهبود تجریهٔ تماستان" "گپ‌ها" "فضاها" @@ -30,6 +34,7 @@ "دعوت‌ها" "هیچ دعوت منتظری ندارید." "اولویت کم" + "شما هنوز هیچ چت با اولویت پایین ندارید" "می توانید پالایه‌ها را برای دیدن دیگر گپ‌هایتان بردارید" "هیچ گپی برای این گزینش ندارید" "افراد" @@ -44,6 +49,7 @@ "علامت‌گذاری به عنوان خوانده شده" "نشان به ناخوانده" "این اتاق ارتقا یافته" + "فضاهای شما" "گویا از افزاره‌ای جدید استفاده می‌کنید. تأیید با افزاره‌ای دیگر برای دسترسی به پیام‌های رمزنگاری شده‌تان." "تأیید کنید که خودتانید" diff --git a/features/invite/impl/src/main/res/values-ar/translations.xml b/features/invite/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..31ed141c3e --- /dev/null +++ b/features/invite/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,18 @@ + + + "لن ترى أي رسائل أو دعوات إلى غرف من هذا المستخدم" + "حظر المستخدم" + "أبلغ موفّر حسابك عن هذه الغرفة." + "صِف سبب الإبلاغ…" + "رفض وحظر" + "أمتأكد من رغبتك برفض دعوة للانضمام إلى %1$s؟" + "رفض الدعوة" + "أمتأكد من رغبتك برفض المحادثة الخاصة مع %1$s؟" + "رفض المحادثة" + "لا دعوات" + "دعاك %1$s (%2$s)" + "نعم، ارفض واحظر" + "هل أنت متأكد من رغبتك في رفض الدعوة للانضمام إلى هذه الغرفة؟ سيمنع هذا أيضًا %1$s من التواصل معك أو دعوتك إلى غرف." + "رفض الدعوة والحظر" + "رفض وحظر" + diff --git a/features/invite/impl/src/main/res/values-fa/translations.xml b/features/invite/impl/src/main/res/values-fa/translations.xml index 9cbad94f91..9e245d7e9f 100644 --- a/features/invite/impl/src/main/res/values-fa/translations.xml +++ b/features/invite/impl/src/main/res/values-fa/translations.xml @@ -1,6 +1,8 @@ + "شما هیچ پیام یا دعوتنامه‌ای از این کاربر نخواهید دید." "انسداد کاربر" + "این اتاق را به ارائه دهنده حساب خود گزارش دهید." "شرح دلیل گزارش…" "رد و انسداد" "مطمئنید که می‌خواهید دعوت پیوستن به %1$s را رد کنید؟" @@ -10,6 +12,7 @@ "بدون دعوت" "%1$s (%2$s) دعوتتان کرد" "بله. رد و انسداد" + "آیا مطمئنید که می‌خواهید دعوت به عضویت در این اتاق را رد کنید؟ این کار همچنین مانع از تماس %1$s با شما یا دعوت شما به اتاق‌ها می‌شود." "رد دعوت و انسداد" "رد و انسداد" diff --git a/features/invitepeople/impl/src/main/res/values-ar/translations.xml b/features/invitepeople/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..fa28cb13b9 --- /dev/null +++ b/features/invitepeople/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,9 @@ + + + "أنت عضو بالفعل" + "مَدعُوّ بالفعل" + "ليست لديك حاليًا أي دردشات مع جهات الاتصال هذه. أكّد دعوتهم إلى هذه الغرفة قبل المتابعة." + "ليست لديك حاليًا أي دردشات مع جهة الاتصال هذه. أكّد دعوتها إلى هذه الغرفة قبل المتابعة." + "هل تريد دعوة جهات اتصال جديدة إلى هذه الغرفة؟" + "هل تريد دعوة جهة اتصال جديدة إلى هذه الغرفة؟" + diff --git a/features/invitepeople/impl/src/main/res/values-es/translations.xml b/features/invitepeople/impl/src/main/res/values-es/translations.xml index e62bb211e5..b9058446b4 100644 --- a/features/invitepeople/impl/src/main/res/values-es/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-es/translations.xml @@ -1,5 +1,9 @@ - "Ya eres miembro" + "Ya es miembro" "Ya estás invitado" + "De momento no tienes ningún chat con estos contactos. Confirma que quieres invitarlos a esta sala antes de continuar." + "De momento no tienes ningún chat con este contacto. Confirma que quieres invitarlo a esta sala antes de continuar." + "¿Invitar contactos nuevos a esta sala?" + "¿Invitar contacto nuevo a esta sala?" diff --git a/features/invitepeople/impl/src/main/res/values-fa/translations.xml b/features/invitepeople/impl/src/main/res/values-fa/translations.xml index 544f01b8c8..100abe539c 100644 --- a/features/invitepeople/impl/src/main/res/values-fa/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-fa/translations.xml @@ -2,4 +2,8 @@ "از پیش عضو است" "از پیش دعوت شده" + "شما در حال حاضر هیچ گفت‌وگویی با این مخاطبین ندارید. قبل از ادامه، دعوت آنها را به این اتاق تأیید کنید." + "شما در حال حاضر هیچ گفت‌وگویی با این مخاطب ندارید. قبل از ادامه، دعوت او را به این اتاق تأیید کنید." + "مخاطبین جدید را به این اتاق دعوت کنید؟" + "مخاطب جدید را به این اتاق دعوت می‌کنید؟" diff --git a/features/joinroom/impl/src/main/res/values-ar/translations.xml b/features/joinroom/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..84a81ad6a5 --- /dev/null +++ b/features/joinroom/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,34 @@ + + + "لقد حظرك %1$s." + "تم حظرك" + "السبب: %1$s." + "إلغاء الطلب" + "نعم، ألغِ" + "هل أنت متأكد من رغبتك في إلغاء طلبك للانضمام إلى هذه الغرفة؟" + "إلغاء طلب الانضمام" + "نعم، ارفض واحظر" + "هل أنت متأكد من رغبتك في رفض الدعوة للانضمام إلى هذه الغرفة؟ سيمنع هذا أيضًا %1$s من التواصل معك أو دعوتك إلى غرف." + "رفض الدعوة والحظر" + "رفض وحظر" + "فشل الانضمام" + "إما أنك تحتاج إلى دعوة للانضمام، أو قد توجد قيود على الوصول." + "نسيان" + "تحتاج إلى دعوة للانضمام" + "بدعوة من" + "إنضمام" + "قد تحتاج إلى دعوة أو إلى أن تكون عضوًا في مساحة لتتمكن من الانضمام." + "إرسال طلب الانضمام" + "الأحرف المسموح بها: %1$d من %2$d" + "رسالة (اختيارية)" + "ستتلقى دعوة للانضمام إلى الغرفة إذا تم قبول طلبك." + "تم إرسال طلب الانضمام" + "تعذّر عرض معاينة الغرفة. قد يكون ذلك بسبب مشكلات في الشبكة أو الخادم." + "تعذّر عرض معاينة هذه الغرفة" + "لا يدعم %1$s المساحات بعد. يمكنك الوصول إلى المساحات عبر الويب." + "المساحات غير مدعومة بعد" + "انقر على الزر أدناه وسيتم إشعار مسؤول الغرفة. ستتمكن من الانضمام إلى المحادثة بعد الموافقة." + "يجب أن تكون عضوًا في هذه الغرفة لعرض سجل الرسائل." + "هل تريد الانضمام إلى هذه الغرفة؟" + "المعاينة غير متاحة" + diff --git a/features/joinroom/impl/src/main/res/values-es/translations.xml b/features/joinroom/impl/src/main/res/values-es/translations.xml index 698a6eb1c9..6773318776 100644 --- a/features/joinroom/impl/src/main/res/values-es/translations.xml +++ b/features/joinroom/impl/src/main/res/values-es/translations.xml @@ -1,7 +1,7 @@ - "Has sido baneado por %1$s." - "Has sido baneado." + "Has sido vetado por %1$s." + "Has sido vetado" "Motivo: %1$s." "Cancelar solicitud" "Sí, cancelar" @@ -15,9 +15,11 @@ "O es necesario estar invitado para unirse o puede haber restricciones de acceso." "Olvidar" "Necesitas una invitación para unirte" + "Te invitó" "Unirse" "Es posible que necesites ser invitado o ser miembro de un espacio para poder unirte." "Enviar solicitud de unión" + "%1$d de %2$d caracteres permitidos" "Mensaje (opcional)" "Recibirás una invitación para unirte a la sala si se acepta tu solicitud." "Solicitud de unión enviada" diff --git a/features/joinroom/impl/src/main/res/values-fa/translations.xml b/features/joinroom/impl/src/main/res/values-fa/translations.xml index 3f9fc99877..d5102b939f 100644 --- a/features/joinroom/impl/src/main/res/values-fa/translations.xml +++ b/features/joinroom/impl/src/main/res/values-fa/translations.xml @@ -1,23 +1,30 @@ - "به دست %1$s از این اتاق محروم شدید." - "از این اتاق محروم شدید" + "شما توسط %1$s مسدود شده‌اید." + "شما محروم شدید" "دلیل: %1$s." "لغو درخواست" "بله. لغو" + "آیا مطمئن هستید که می‌خواهید درخواست عضویت خود در این اتاق را لغو کنید؟" "لغو درخواست پیوستن" "بله. رد و انسداد" + "آیا مطمئنید که می‌خواهید دعوت به عضویت در این اتاق را رد کنید؟ این کار همچنین مانع از تماس %1$s با شما یا دعوت شما به اتاق‌ها می‌شود." "رد دعوت و انسداد" "رد و انسداد" "پیوستن شکست خورد" - "فراموشی این اتاق" - "برای پیوستن به این اتاق نیاز به دعوت دارید" + "یا باید دعوتنامه داشته باشید یا ممکن است محدودیت‌هایی برای دسترسی وجود داشته باشد." + "فراموش کردن" + "برای عضویت نیاز به دعوتنامه دارید" "دعوت شده از سوی" "پیوستن" "برای پیوستن به فضا باید دعوت شده باشید." "در زدن برای پیوستن" + "کاراکترهای مجاز %1$d از %2$d" "پیام (اختیاری)" + "در صورت پذیرش درخواست، دعوتنامه‌ای برای ورود به اتاق دریافت خواهید کرد." "درخواست پیوستن فرستاده شد" + "ما نتوانستیم پیش‌نمایش اتاق را نمایش دهیم. این ممکن است به دلیل مشکلات شبکه یا سرور باشد." + "ما نتوانستیم پیش‌نمایش این اتاق را نمایش دهیم" "%1$s هنوز از فضاها پشتیبانی نمی‌کند. می‌توانید روی وب به فضاها دسترسی داشته باشید." "فضاها هنوز پشتیبانی نمی‌شوند" "زدن روی این دکمه برای آگاه شدن مدیر اتاق. پس از تأیید می‌توانید به گفت‌وگو بپیوندید." diff --git a/features/knockrequests/impl/src/main/res/values-ar/translations.xml b/features/knockrequests/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..4ff4995748 --- /dev/null +++ b/features/knockrequests/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,40 @@ + + + "نعم، قبول الكل" + "هل أنت متأكد من رغبتك في قبول جميع طلبات الانضمام؟" + "قبول جميع الطلبات" + "قبول الكل" + "تعذّر قبول جميع الطلبات. هل ترغب في المحاولة مرة أخرى؟" + "فشل قبول جميع الطلبات" + "جارٍ قبول جميع طلبات الانضمام" + "تعذّر قبول هذا الطلب. هل ترغب في المحاولة مرة أخرى؟" + "فشل قبول الطلب" + "جارٍ قبول طلب الانضمام" + "نعم، ارفض واحظر" + "هل أنت متأكد من رغبتك في رفض %1$s وحظره؟ لن يتمكن هذا المستخدم من طلب الوصول للانضمام إلى هذه الغرفة مرة أخرى." + "رفض الوصول والحظر" + "جارٍ رفض الوصول وحظره" + "نعم، ارفض" + "هل أنت متأكد من رغبتك في رفض طلب %1$s للانضمام إلى هذه الغرفة؟" + "رفض الوصول" + "رفض وحظر" + "تعذّر رفض هذا الطلب. هل ترغب في المحاولة مرة أخرى؟" + "فشل رفض الطلب" + "جارٍ رفض طلب الانضمام" + "عندما يطلب أحدهم الانضمام إلى الغرفة، ستتمكن من رؤية طلبه هنا." + "لا توجد طلبات انضمام معلّقة" + "جارٍ تحميل طلبات الانضمام…" + "طلبات الانضمام" + + "%1$s يريد الانضمام إلى هذه الغرفة" + "%1$s +%2$d آخر يريدان الانضمام إلى هذه الغرفة" + "%1$s +%2$d آخران يريدون الانضمام إلى هذه الغرفة" + "%1$s +%2$d آخرون يريدون الانضمام إلى هذه الغرفة" + "%1$s +%2$d آخرين يريدون الانضمام إلى هذه الغرفة" + "%1$s +%2$d آخرين يريدون الانضمام إلى هذه الغرفة" + + "عرض الكل" + "قبول" + "%1$s يريد الانضمام إلى هذه الغرفة" + "عرض" + diff --git a/features/knockrequests/impl/src/main/res/values-fa/translations.xml b/features/knockrequests/impl/src/main/res/values-fa/translations.xml index 5b931e4f00..af786de6de 100644 --- a/features/knockrequests/impl/src/main/res/values-fa/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-fa/translations.xml @@ -1,21 +1,34 @@ "بله. پذیرش همه" + "آیا مطمئن هستید که می‌خواهید همه درخواست‌های عضویت را بپذیرید؟" "پذیرش همهٔ درخواست‌ها" "پذیرش همه" + "ما نتوانستیم همه درخواست‌ها را بپذیریم. آیا مایلید دوباره امتحان کنید؟" "شکست در پذیرش همهٔ درخواست‌ها" "پذیرفتن همهٔ درخواست‌های پیوستن" + "ما نتوانستیم این درخواست را بپذیریم. آیا مایلید دوباره امتحان کنید؟" "شکست در پذیرش درخواست" "پذیرفتن درخواست پیوستن" "بله. رد و انسداد" + "آیا مطمئن هستید که می‌خواهید %1$s را رد و مسدود کنید؟ این کاربر دیگر نمی‌تواند درخواست دسترسی برای پیوستن به این اتاق را بدهد." "رد و تحریم دسترسی" "رد کردن و تحریم دسترسی" "بله. رد شود" + "آیا مطمئن هستید که می‌خواهید درخواست %1$s برای پیوستن به این اتاق را رد کنید؟" "رد کردن دسترسی" "رد و تحریم" + "ما نمی‌توانستیم این درخواست را رد کنیم. آیا مایلید دوباره امتحان کنید؟" "شکتس در رد درخواست" "در کردن درخواست پیوستن" + "وقتی کسی درخواست عضویت در اتاق را می‌دهد، می‌توانید درخواست او را اینجا ببینید." + "درخواست عضویت در حال بررسی نیست" + "در حال بارگیری درخواست‌های عضویت…" "درخواست‌های پیوستن" + + "%1$s +%2$d افراد دیگری هم می‌خواهند به این اتاق بپیوندند" + "%1$s +%2$d دیگران می‌خواهند به این اتاق بپیوندند" + "دیدن همه" "پذیرش" "%1$s می‌خواهد به این اتاق بپیوندد" diff --git a/features/leaveroom/api/src/main/res/values-ar/translations.xml b/features/leaveroom/api/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..9253cb5365 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-ar/translations.xml @@ -0,0 +1,10 @@ + + + "هل أنت متأكد من رغبتك في مغادرة هذه المحادثة؟ هذه المحادثة ليست عامة ولن تتمكن من الانضمام إليها مرة أخرى دون دعوة." + "أأنت متأكد من رغبتك بخروج الغرفة؟ أنت الشخص الوحيد هنا. وإذا ما غادرت فلن يعود أحد قادراً على الدخول في المستقبل، بما فيهم أنت." + "أأنت متأكد من رغبتك بخروج الغرفة؟ هذه الغرفة ليست عامة ولن تعود قادراً على الدخول مجدداً بلا دعوة" + "اختيار المالكين" + "أنت المالك الوحيد لهذه الغرفة. يجب أن تنقل الملكية إلى شخص آخر قبل مغادرة الغرفة." + "نقل الملكية" + "أأنت متأكد من رغبتك بخروج الغرفة؟" + diff --git a/features/leaveroom/api/src/main/res/values-es/translations.xml b/features/leaveroom/api/src/main/res/values-es/translations.xml index 81b169e5c9..59457c8dad 100644 --- a/features/leaveroom/api/src/main/res/values-es/translations.xml +++ b/features/leaveroom/api/src/main/res/values-es/translations.xml @@ -3,5 +3,8 @@ "¿Estás seguro de que quieres salir de esta conversación? Esta conversación no es pública y no podrás volver a unirte sin una invitación." "¿Estás seguro de que quieres salir de esta sala? Eres la única persona aquí. Si te vas, nadie podrá unirse en el futuro, ni siquiera tú." "¿Seguro que quieres salir de esta sala? Esta sala no es pública y no podrás volver a entrar sin una invitación." + "Elegir propietarios" + "Eres el único propietario de esta sala. Debes transferir la propiedad a otra persona antes de salir de la sala." + "Transferir la propiedad" "¿Seguro que quieres salir de la sala?" diff --git a/features/leaveroom/api/src/main/res/values-fa/translations.xml b/features/leaveroom/api/src/main/res/values-fa/translations.xml index ec2cd89314..6391a70210 100644 --- a/features/leaveroom/api/src/main/res/values-fa/translations.xml +++ b/features/leaveroom/api/src/main/res/values-fa/translations.xml @@ -4,6 +4,7 @@ "مطمئنید که می‌خواهید این اتاق را ترک کنید؟ تنها فرد این‌جا هستید. در صورت ترک، هیچ‌کسی از جمله خودتان در آینده نخواهد توانست به آن بپیوندد." "مطمئنید که می‌خواهید این اتاق را ترک کنید؟ این اتاق عمومی نبوده قادر نخواهید بود بدون دعوت دوباره بپیوندید." "گزینش مالکان" + "شما تنها مالک این اتاق هستید. قبل از ترک اتاق، باید مالکیت را به شخص دیگری منتقل کنید." "انتقال مالکیت" "مطمئنید که می‌خواهید این اتاق را ترک کنید؟" diff --git a/features/linknewdevice/impl/src/main/res/values-ar/translations.xml b/features/linknewdevice/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..895a0e7efe --- /dev/null +++ b/features/linknewdevice/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,60 @@ + + + "امسح رمز QR ضوئيًا" + "افتح %1$s على كمبيوتر محمول أو مكتبي" + "امسح رمز QR ضوئيًا باستخدام هذا الجهاز" + "جاهز للمسح الضوئي" + "افتح %1$s على كمبيوتر مكتبي للحصول على رمز QR" + "الأرقام غير متطابقة" + "أدخل الرمز المكوّن من رقمين" + "سيؤكد هذا أن الاتصال بجهازك الآخر آمن." + "أدخل الرقم المعروض على جهازك الآخر" + "لا يدعم موفّر حسابك %1$s." + "%1$s غير مدعوم" + "لا يدعم موفّر حسابك تسجيل الدخول إلى جهاز جديد باستخدام رمز QR." + "رمز QR غير مدعوم" + "تم إلغاء تسجيل الدخول على الجهاز الآخر." + "تم إلغاء طلب تسجيل الدخول" + "انتهت صلاحية تسجيل الدخول. يرجى المحاولة مرة أخرى." + "لم يكتمل تسجيل الدخول في الوقت المحدد" + "افتح %1$s على الجهاز الآخر" + "اختر %1$s" + "«تسجيل الدخول باستخدام رمز QR»" + "امسح رمز QR المعروض هنا باستخدام الجهاز الآخر" + "افتح %1$s على الجهاز الآخر" + "كمبيوتر مكتبي" + "جارٍ تحميل رمز QR…" + "جهاز محمول" + "ما نوع الجهاز الذي تريد ربطه؟" + "يرجى المحاولة مرة أخرى والتأكد من إدخال الرمز المكوّن من رقمين بشكل صحيح. إذا ظلت الأرقام غير متطابقة، فتواصل مع موفّر حسابك." + "الأرقام غير متطابقة" + "تعذّر إنشاء اتصال آمن بالجهاز الجديد. لا تزال أجهزتك الحالية آمنة ولا داعي للقلق بشأنها." + "ماذا الآن؟" + "حاول تسجيل الدخول مجددًا باستخدام رمز QR إذا كانت هذه مشكلة في الشبكة." + "إذا واجهت المشكلة نفسها، جرّب شبكة Wi-Fi مختلفة أو استخدم بيانات الهاتف المحمول بدلًا من Wi-Fi." + "إذا لم ينجح ذلك، سجّل الدخول يدويًا" + "الاتصال غير آمن" + "سيُطلب منك إدخال الرقمين الظاهرين على هذا الجهاز." + "أدخل الرقم أدناه على جهازك الآخر" + "تم إلغاء تسجيل الدخول على الجهاز الآخر." + "تم إلغاء طلب تسجيل الدخول" + "تم رفض تسجيل الدخول على الجهاز الآخر." + "تم رفض تسجيل الدخول" + "لست بحاجة إلى فعل أي شيء آخر." + "جهازك الآخر مسجّل الدخول بالفعل" + "انتهت صلاحية تسجيل الدخول. يرجى المحاولة مرة أخرى." + "لم يكتمل تسجيل الدخول في الوقت المحدد" + "جهازك الآخر لا يدعم تسجيل الدخول إلى %s باستخدام رمز QR. + +جرّب تسجيل الدخول يدويًا، أو امسح رمز QR ضوئيًا باستخدام جهاز آخر." + "رمز QR غير مدعوم" + "لا يدعم موفّر حسابك %1$s." + "%1$s غير مدعوم" + "استخدم رمز QR المعروض على الجهاز الآخر." + "حاول مرة أخرى" + "رمز QR غير صحيح" + "تحتاج إلى منح %1$s إذنًا لاستخدام كاميرا جهازك للمتابعة." + "اسمح بالوصول إلى الكاميرا لمسح رمز QR ضوئيًا" + "حدث خطأ غير متوقع. يرجى المحاولة مرة أخرى." + "في انتظار جهازك الآخر" + diff --git a/features/linknewdevice/impl/src/main/res/values-es/translations.xml b/features/linknewdevice/impl/src/main/res/values-es/translations.xml index c33dc3ad88..5593f4bfd6 100644 --- a/features/linknewdevice/impl/src/main/res/values-es/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-es/translations.xml @@ -1,16 +1,34 @@ "Escanea el código QR" + "Abre %1$s en un ordenador" "Escanea el código QR con este dispositivo" "Listo para escanear" + "Abre %1$s en un ordenador para obtener el código QR" + "Solo escanea un código QR después de haber seguido los pasos anteriores en tu propio dispositivo. Nunca escanees un código que te haya enviado otra persona." + "Los números no coinciden" + "Introduce el código de 2 dígitos" + "Esto verificará que la conexión con tu otro dispositivo sea segura." + "Introduce el número que aparece en tu otro dispositivo" "Tu proveedor de cuentas no es compatible con %1$s." "%1$s no admitido" + "El proveedor de tu cuenta no admite el inicio de sesión en un nuevo dispositivo con un código QR." "Código QR no admitido" "El inicio de sesión se canceló en el otro dispositivo." "Solicitud de inicio de sesión cancelada" "El inicio de sesión ha caducado. Inténtalo de nuevo." "El inicio de sesión no se completó a tiempo" + "Abre %1$s en el otro dispositivo" "Selecciona %1$s" + "«Iniciar sesión con un código QR»" + "Escanea el código QR que se muestra aquí con el otro dispositivo" + "Abre %1$s en el otro dispositivo" + "Ordenador" + "Cargando código QR…" + "Dispositivo móvil" + "¿Qué tipo de dispositivo quieres vincular?" + "Inténtalo de nuevo y asegúrate de haber introducido correctamente el código de 2 dígitos. Si los números siguen sin coincidir, ponte en contacto con el proveedor de tu cuenta." + "Los números no coinciden" "No se pudo establecer una conexión segura con el nuevo dispositivo. Tus dispositivos actuales siguen siendo seguros y no tienes que preocuparte por ellos." "¿Y ahora qué?" "Intenta iniciar sesión de nuevo con un código QR en caso de que se trate de un problema de red" @@ -23,6 +41,8 @@ "Solicitud de inicio de sesión cancelada" "El inicio de sesión se rechazó en el otro dispositivo." "Inicio de sesión rechazado" + "No tienes que hacer nada más." + "Tu otro dispositivo ya está conectado" "El inicio de sesión ha caducado. Inténtalo de nuevo." "El inicio de sesión no se completó a tiempo" "Tu otro dispositivo no admite el inicio de sesión en %s con un código QR. diff --git a/features/linknewdevice/impl/src/main/res/values-fa/translations.xml b/features/linknewdevice/impl/src/main/res/values-fa/translations.xml index f7b0d7f3db..20c064410c 100644 --- a/features/linknewdevice/impl/src/main/res/values-fa/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-fa/translations.xml @@ -1,16 +1,34 @@ "پویش کد پاس" + "%1$s را روی لپ‌تاپ یا رایانه رومیزی باز کنید." "پویش کد پاس با این افزاره" "آمادهٔ پویش" + "برای دریافت کد QR، %1$s را روی رایانه رومیزی باز کنید." + "فقط پس از انجام مراحل بالا در دستگاه خود، کد QR را اسکن کنید. هرگز کدی را که توسط شخص دیگری ارسال شده است، اسکن نکنید." + "اعداد مطابقت ندارند" + "کد دو رقمی را وارد کنید" + "این کار تأیید می‌کند که اتصال به دستگاه دیگر شما ایمن است." + "عدد نمایش داده شده در دستگاه دیگر خود را وارد کنید" "فراهم کنندهٔ حسابتان از %1$s پشتیبانی نمی‌کند." "%1$s پشتیبانی نمی‌شود" + "ارائه دهنده حساب شما از ورود به دستگاه جدید با کد QR پشتیبانی نمی‌کند." "کد پاس پشتیبانی نمی‌شود" "ورود روی افزارهٔ دیگر لغو شد." "درخواست ورد لغو شد" "ورود منقضی شد. لطفاً دوباره تلاش کنید." "ورود در زمان معیّن کامل نشد" + "%1$s را در دستگاه دیگر باز کنید" "گزینش %1$s" + "«ورود با کد QR»" + "کد QR نشان داده شده در اینجا را با دستگاه دیگر اسکن کنید" + "%1$s را در دستگاه دیگر باز کنید" + "کامپیوتر رومیزی" + "در حال بارگذاری کد QR…" + "دستگاه همراه" + "چه نوع دستگاهی را می‌خواهید لینک کنید؟" + "لطفاً دوباره امتحان کنید و مطمئن شوید که کد دو رقمی را به درستی وارد کرده‌اید. اگر اعداد هنوز مطابقت ندارند، با ارائه دهنده حساب خود تماس بگیرید." + "اعداد مطابقت ندارند" "نتوانست اتّصالی امن به افزارهٔ جدید بسازد. افزاره‌های موجودتان هنوز امنند و نیازی نیست نگرانشان باشید." "اکنون چه؟" "آزمودن ورود دوباره با کد پاس در صورت مشکل شبکه" @@ -23,6 +41,8 @@ "درخواست ورد لغو شد" "ورود به دست افزارهٔ دیگر رد شد." "ورود رد شد" + "لازم نیست کار دیگری انجام دهید." + "دستگاه دیگر شما قبلاً وارد سیستم شده است" "ورود منقضی شد. لطفاً دوباره تلاش کنید." "ورود در زمان معیّن کامل نشد" "افزارهٔ دیگرتان از ورود به %s با کد پاس پشتیبانی نمی‌کند. diff --git a/features/linknewdevice/impl/src/main/res/values-fi/translations.xml b/features/linknewdevice/impl/src/main/res/values-fi/translations.xml index 0ba5a30e58..c18b9bbfd0 100644 --- a/features/linknewdevice/impl/src/main/res/values-fi/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-fi/translations.xml @@ -5,6 +5,7 @@ "Skannaa QR-koodi tällä laitteella" "Valmis skannaamaan" "Avaa %1$s pöytätietokoneella saadaksesi QR-koodin" + "Skannaa QR-koodi vasta, kun olet noudattanut yllä olevia ohjeita omalla laitteellasi. Älä koskaan skannaa toisen henkilön lähettämää koodia." "Numerot eivät täsmää" "Kirjoita 2-numeroinen koodi" "Tämä varmistaa, että yhteys toiseen laitteeseesi on turvallinen." diff --git a/features/linknewdevice/impl/src/main/res/values-fr/translations.xml b/features/linknewdevice/impl/src/main/res/values-fr/translations.xml index 12a770af17..7e642578af 100644 --- a/features/linknewdevice/impl/src/main/res/values-fr/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-fr/translations.xml @@ -5,6 +5,7 @@ "Scanner le code QR avec cet appareil" "Prêt à scanner" "Ouvrir %1$s sur un ordinateur pour obtenir le code QR" + "Ne scannez un code QR qu’après avoir suivi les étapes ci-dessus sur votre propre appareil. Ne scannez jamais un code envoyé par quelqu’un d’autre." "Les nombres ne correspondent pas" "Saisissez le code à 2 chiffres" "Cela permettra de vérifier que la connexion à votre autre appareil est sécurisée." diff --git a/features/location/impl/src/main/res/values-ar/translations.xml b/features/location/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..fb70505fbe --- /dev/null +++ b/features/location/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,6 @@ + + + "سيُخزَّن سجل موقعك المباشر في الغرفة وسيكون مرئيًا للأعضاء بعد انتهاء الجلسة." + "اختر مدة مشاركة موقعك المباشر." + "ليست لديك صلاحيات لمشاركة موقعك المباشر في هذه الغرفة" + diff --git a/features/location/impl/src/main/res/values-es/translations.xml b/features/location/impl/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..9fc15d3468 --- /dev/null +++ b/features/location/impl/src/main/res/values-es/translations.xml @@ -0,0 +1,6 @@ + + + "Tu historial de ubicación en tiempo real se guardará en la sala y será visible para los miembros después de que termine la sesión." + "Elige por cuánto tiempo quieres compartir tu ubicación en tiempo real" + "No tienes permiso para compartir tu ubicación en tiempo real en esta sala." + diff --git a/features/location/impl/src/main/res/values-fa/translations.xml b/features/location/impl/src/main/res/values-fa/translations.xml new file mode 100644 index 0000000000..5183ed2f43 --- /dev/null +++ b/features/location/impl/src/main/res/values-fa/translations.xml @@ -0,0 +1,6 @@ + + + "سابقه موقعیت مکانی زنده شما در اتاق ذخیره می‌شود و پس از پایان جلسه برای اعضا قابل مشاهده خواهد بود." + "مدت زمان اشتراک‌گذاری موقعیت مکانی زنده خود را انتخاب کنید." + "شما مجوز اشتراک‌گذاری موقعیت مکانی زنده خود را در این اتاق ندارید" + diff --git a/features/lockscreen/impl/src/main/res/values-ar/translations.xml b/features/lockscreen/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..b277c420ea --- /dev/null +++ b/features/lockscreen/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,46 @@ + + + "المصادقة بالمقاييس الحيوية" + "إلغاء القفل باستخدام المقاييس الحيوية" + "إلغاء القفل باستخدام المقاييس الحيوية" + "تأكيد المصادقة الحيوية" + "هل نسيت رمز PIN؟" + "تغيير رمز PIN" + "السماح بإلغاء القفل باستخدام المقاييس الحيوية" + "إزالة رمز PIN" + "هل أنت متأكد من رغبتك في إزالة رمز PIN؟" + "إزالة رمز PIN؟" + "السماح بـ %1$s" + "أفضّل استخدام رمز PIN" + "وفّر بعض الوقت واستخدم %1$s لإلغاء قفل التطبيق في كل مرة" + "اختر رمز PIN" + "تأكيد رمز PIN" + "اقفل %1$s لإضافة مزيد من الأمان إلى محادثاتك. + +اختر شيئًا يسهل تذكره. إذا نسيت رمز PIN هذا، فسيتم تسجيل خروجك من التطبيق." + "لا يمكنك اختيار هذا كرمز PIN لأسباب أمنية" + "اختر رمز PIN مختلفًا" + "يُرجى إدخال رمز PIN نفسه مرتين" + "رمزا PIN غير متطابقين" + "ستحتاج إلى تسجيل الدخول مرة أخرى وإنشاء رمز PIN جديد للمتابعة" + "تتم إزالة هذا الجهاز" + + "لديك %1$d محاولات لإلغاء القفل" + "لديك %1$d محاولة لإلغاء القفل" + "لديك %1$d محاولتان لإلغاء القفل" + "لديك %1$d محاولات لإلغاء القفل" + "لديك %1$d محاولة لإلغاء القفل" + "لديك %1$d محاولة لإلغاء القفل" + + + "رمز PIN غير صحيح. لديك %1$d فرص أخرى" + "رمز PIN غير صحيح. لديك %1$d فرصة أخرى" + "رمز PIN غير صحيح. لديك %1$d فرصتان أخريان" + "رمز PIN غير صحيح. لديك %1$d فرص أخرى" + "رمز PIN غير صحيح. لديك %1$d فرصة أخرى" + "رمز PIN غير صحيح. لديك %1$d فرصة أخرى" + + "استخدام المقاييس الحيوية" + "استخدام رمز PIN" + "تجري إزالة الجهاز…" + diff --git a/features/lockscreen/impl/src/main/res/values-es/translations.xml b/features/lockscreen/impl/src/main/res/values-es/translations.xml index c0e34a3763..085c1d1cbf 100644 --- a/features/lockscreen/impl/src/main/res/values-es/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-es/translations.xml @@ -23,7 +23,7 @@ Elige algo que puedas recordar. Si olvidas este PIN, se cerrará la sesión de l "Por favor ingresa el mismo PIN dos veces" "Los PINs no coinciden" "Tendrás que volver a iniciar sesión y crear un nuevo PIN para continuar" - "Se está cerrando tu sesión" + "Se está eliminando este dispositivo" "Tienes %1$d intento de desbloqueo" "Tienes %1$d intentos de desbloqueo" @@ -34,5 +34,5 @@ Elige algo que puedas recordar. Si olvidas este PIN, se cerrará la sesión de l "Usar desbloqueo biométrico" "Usar PIN" - "Cerrando sesión…" + "Eliminando dispositivo…" diff --git a/features/lockscreen/impl/src/main/res/values-pl/translations.xml b/features/lockscreen/impl/src/main/res/values-pl/translations.xml index 29691987f6..e6faf72945 100644 --- a/features/lockscreen/impl/src/main/res/values-pl/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-pl/translations.xml @@ -17,7 +17,7 @@ "Potwierdź PIN" "Zablokuj %1$s, aby zwiększyć bezpieczeństwo swoich czatów. -Wybierz coś łatwego do zapamiętania. Jeśli zapomnisz ten PIN, zostaniesz wylogowany z aplikacji." +Wybierz coś łatwego do zapamiętania. Jeśli zapomnisz tego PINu, zostaniesz wylogowany z aplikacji." "Nie możesz wybrać tego PIN\'u ze względów bezpieczeństwa" "Wybierz inny kod PIN" "Wprowadź ten sam kod PIN dwa razy" diff --git a/features/login/impl/src/main/res/values-ar/translations.xml b/features/login/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..181b38ec24 --- /dev/null +++ b/features/login/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,109 @@ + + + "تغيير مزوّد الحسابات" + "عنوان السيرفر" + "أدخِل عبارة بحث أو عنوان نطاق." + "ابحث عن شركة، أو مجتمع، أو سيرفر خاص." + "ابحث عن مزوّد حسابات" + "هذا المكان الذي تقطن فيه رسائلك — تماماً كاستخدامك مزوّد بريد إلكتروني للاحتفاظ برسائل البريد خاصّتك فيه." + "أنت على وشك تسجيل الدخول إلى %s" + "هذا المكان الذي تقطن فيه رسائلك — تماماً كاستخدامك مزوّد بريد إلكتروني للاحتفاظ برسائل البريد خاصّتك فيه." + "أنت على وشك إنشاء حساب في %s" + "إن Matrix.org سيرفر ضخم ومجاني في شبكة Matrix العامّة وُجِد من أجل مراسلات آمنة ولا مركزية، يُدار من قِبل مؤسسة Matrix.org." + "غير ذلك" + "استخدم مزوّد حسابات آخر كسيرفرك الخاص أو حساب عمل." + "تغيير مزوّد الحسابات" + "Google Play" + "تطبيق Element Pro مطلوب على %1$s. يرجى تنزيله من المتجر." + "Element Pro مطلوب" + "لم نستطع الوصول لهذا السيرفر. رجاءً تحقق إذا ما كنت أدخلت عنوان الـURL بشكل صحيح. إذا كان الـURL صحيحاً فراسل إدارة السيرفر خاصتك لمزيد من المساعدة." + "الخادم غير متاح بسبب مشكلة في ملف .well-known: +%1$s" + "لا يدعم موفّر الحساب المحدد ميزة Sliding Sync. يلزم ترقية الخادم لاستخدام %1$s." + "غير مسموح لـ %1$s بالاتصال بـ %2$s." + "تم إعداد هذا التطبيق للسماح بـ: %1$s." + "موفّر الحساب %1$s غير مسموح به." + "عنوان الـ URL للسيرفر" + "أدخل عنوان نطاق." + "ما عنوان السيرفر خاصتك؟" + "اختر خادمك" + "إنشاء حساب" + "هذا الحساب محذوف." + "اسم المستخدم و/أو كلمة المرور غير صحيحة" + "معرّف المستخدم هذا غير صالح. التنسيق المتوقّع: \'@user:homeserver.org\'" + "تم إعداد هذا الخادم لاستخدام رموز التحديث. وهي غير مدعومة عند استخدام تسجيل الدخول بكلمة المرور." + "لا يتيح المخدِّم المختار تسجيلَ الدخول بكلمة سرّ أو OAuth. راجع المسؤول أو اختر مخدِّماً آخر." + "أدخِل تفاصيلك" + "Matrix عبارة عن شبكة مفتوحة للاتصالات الآمنة واللا مركزية." + "أهلاً بعودتك!" + "تسجيل الدخول إلى %1$s" + "فتح Element Classic" + "افتح Element Classic على جهازك" + "انتقل إلى الإعدادات > الأمان والخصوصية" + "في إدارة مفاتيح التشفير، اختر استرداد الرسائل المشفّرة" + "اتبع التعليمات لتفعيل تخزين مفاتيحك" + "عُد إلى %1$s" + "فعّل تخزين مفاتيحك قبل المتابعة إلى %1$s" + "الإصدار %1$s" + "جارٍ التحقق من الحساب" + "تسجيل الدخول يدوياً" + "تسجيل الدخول إلى %1$s" + "تسجيل الدخول باستخدام رمز QR" + "إنشاء حساب" + "مرحبًا بعودتك" + "مرحباً بك في أسرع %1$s على الإطلاق. مزوَّد من أجل سرعةٍ وبساطة." + "مرحباً بك في %1$s. المعزَّز، من أجل سرعةٍ وبساطة." + "Be in your element" + "جارٍ إنشاء اتصال آمن" + "تعذّر إنشاء اتصال آمن بالجهاز الجديد. لا تزال أجهزتك الحالية آمنة ولا داعي للقلق بشأنها." + "ماذا الآن؟" + "حاول تسجيل الدخول مجددًا باستخدام رمز QR إذا كانت هذه مشكلة في الشبكة." + "إذا واجهت المشكلة نفسها، جرّب شبكة Wi-Fi مختلفة أو استخدم بيانات الهاتف المحمول بدلًا من Wi-Fi." + "إذا لم ينجح ذلك، سجّل الدخول يدويًا" + "الاتصال غير آمن" + "سيُطلب منك إدخال الرقمين الظاهرين على هذا الجهاز." + "أدخل الرقم أدناه على جهازك الآخر" + "سجّل الدخول إلى جهازك الآخر ثم حاول مرة أخرى، أو استخدم جهازًا آخر تم تسجيل الدخول عليه بالفعل." + "لم يتم تسجيل الدخول على الجهاز الآخر" + "تم إلغاء تسجيل الدخول على الجهاز الآخر." + "تم إلغاء طلب تسجيل الدخول" + "تم رفض تسجيل الدخول على الجهاز الآخر." + "تم رفض تسجيل الدخول" + "لست بحاجة إلى فعل أي شيء آخر." + "جهازك الآخر مسجّل الدخول بالفعل" + "انتهت صلاحية تسجيل الدخول. يرجى المحاولة مرة أخرى." + "لم يكتمل تسجيل الدخول في الوقت المحدد" + "جهازك الآخر لا يدعم تسجيل الدخول إلى %s باستخدام رمز QR. + +جرّب تسجيل الدخول يدويًا، أو امسح رمز QR ضوئيًا باستخدام جهاز آخر." + "رمز QR غير مدعوم" + "لا يدعم موفّر حسابك %1$s." + "%1$s غير مدعوم" + "جاهز للمسح الضوئي" + "افتح %1$s على جهاز سطح مكتب" + "انقر على صورتك الرمزية" + "اختر %1$s" + "“ربط جهاز جديد”" + "امسح رمز QR ضوئيًا باستخدام هذا الجهاز" + "متاح فقط إذا كان موفّر حسابك يدعم ذلك." + "افتح %1$s على جهاز آخر للحصول على رمز QR" + "استخدم رمز QR المعروض على الجهاز الآخر." + "حاول مرة أخرى" + "رمز QR غير صحيح" + "الانتقال إلى إعدادات الكاميرا" + "تحتاج إلى منح %1$s إذنًا لاستخدام كاميرا جهازك للمتابعة." + "اسمح بالوصول إلى الكاميرا لمسح رمز QR ضوئيًا" + "امسح رمز QR ضوئيًا" + "البدء من جديد" + "حدث خطأ غير متوقع. يرجى المحاولة مرة أخرى." + "في انتظار جهازك الآخر" + "قد يطلب موفّر حسابك الرمز التالي للتحقق من تسجيل الدخول." + "رمز التحقق الخاص بك" + "تغيير مزوّد الحسابات" + "سيرفر خاص بموظّفي Element." + "Matrix عبارة عن شبكة مفتوحة للاتصالات الآمنة واللا مركزية." + "هذا المكان الذي تقطن فيه رسائلك — تماماً كاستخدامك مزوّد بريد إلكتروني للاحتفاظ برسائل البريد خاصّتك فيه." + "أنت على وشك تسجيل الدخول إلى %1$s" + "اختر موفّر الحساب" + "أنت على وشك إنشاء حساب في %1$s" + diff --git a/features/login/impl/src/main/res/values-es/translations.xml b/features/login/impl/src/main/res/values-es/translations.xml index 3b9ffbe2dd..33b3741234 100644 --- a/features/login/impl/src/main/res/values-es/translations.xml +++ b/features/login/impl/src/main/res/values-es/translations.xml @@ -13,6 +13,9 @@ "Otro" "Usa un proveedor de cuentas diferente, como tu propio servidor privado o una cuenta de trabajo." "Cambiar proveedor de cuentas" + "Google Play" + "%1$s requiere la aplicación Element Pro. Descárgala de la tienda." + "Se requiere Element Pro" "No hemos podido acceder a este servidor base. Comprueba que has introducido correctamente la dirección. Si es correcta, ponte en contacto con el administrador de tu servidor base para obtener más ayuda." "El servidor no está disponible debido a un problema en el archivo .well-known: %1$s" @@ -25,7 +28,7 @@ "¿Cuál es la dirección de tu servidor?" "Selecciona tu servidor" "Crear cuenta" - "Esta cuenta ha sido desactivada." + "Esta cuenta ha sido eliminada." "Usuario y/o contraseña incorrectos" "Este no es un id de usuario válido. Formato esperado: \'@user:homeserver.org\'" "Este servidor está configurado para utilizar tokens de actualización. Estos no son compatibles cuando se utiliza el inicio de sesión basado en contraseña." @@ -34,11 +37,20 @@ "Matrix es una red abierta para una comunicación segura y descentralizada." "¡Hola de nuevo!" "Iniciar sesión en %1$s" + "Abrir Element Classic" + "Abre Element Classic en tu dispositivo" + "Ve a Ajustes > Seguridad y privacidad" + "En «Gestión de claves criptográficas», selecciona «Recuperación de mensajes cifrados»" + "Sigue las instrucciones para activar tu almacén de claves" + "Vuelve a %1$s" + "Activa tu almacén de claves antes de continuar con %1$s" "Versión %1$s" + "Comprobación de la cuenta" "Iniciar sesión manualmente" "Iniciar sesión en %1$s" "Iniciar sesión con un código QR" "Crear cuenta" + "¡Hola de nuevo!" "Bienvenido al %1$s más rápido de todos los tiempos. Diseñado para la velocidad y la simplicidad." "Bienvenido a %1$s. Vitaminado, para mayor rapidez y sencillez." "Siéntete en tu Elemento" @@ -57,6 +69,8 @@ "Solicitud de inicio de sesión cancelada" "El inicio de sesión se rechazó en el otro dispositivo." "Inicio de sesión rechazado" + "No tienes que hacer nada más." + "Tu otro dispositivo ya está conectado" "El inicio de sesión ha caducado. Inténtalo de nuevo." "El inicio de sesión no se completó a tiempo" "Tu otro dispositivo no admite el inicio de sesión en %s con un código QR. diff --git a/features/login/impl/src/main/res/values-fa/translations.xml b/features/login/impl/src/main/res/values-fa/translations.xml index 38c2c11eaa..ce0ab0da20 100644 --- a/features/login/impl/src/main/res/values-fa/translations.xml +++ b/features/login/impl/src/main/res/values-fa/translations.xml @@ -14,8 +14,14 @@ "استفاده از فراهم کنندهٔ حسابی دیگر چون کارساز خصوصی خوتان یا حسابی کاری." "تغییر فراهم کنندهٔ حساب" "پلی گپگل" + "برنامه Element Pro برای %1$s مورد نیاز است. لطفاً آن را از فروشگاه دانلود کنید." + "المنت پرو مورد نیاز است" "ما نتوانستیم به این کارساز خانگی برسیم. لطفاً بررسی کنید که URL کارساز اصلی را به درستی وارد کرده اید. اگر URL صحیح است، برای کمک بیشتر با مدیر کارساز خانگی خود تماس بگیرید." "سرور به دلیل مشکلی در فایل .well-known در دسترس نیست: %1$s" + "ارائه دهنده حساب انتخاب شده از همگام سازی کشویی پشتیبانی نمی کند. برای استفاده از %1$s، ارتقاء سرور لازم است." + "%1$s اجازه اتصال به %2$s را ندارد." + "این برنامه طوری پیکربندی شده است که اجازه موارد زیر را بدهد: %1$s." + "ارائه دهنده حساب %1$s مجاز نیست." "نشانی کارساز خانگی" "ورود نشانی دامنه." "نشانی کارسازتان چیست؟" @@ -30,11 +36,20 @@ "ماتریکس شبکه‌ای بار برای ارتباطات نامتمرکز و امن است." "خوش برگشتید!" "ورود به %1$s" + "باز کردن Element کلاسیک" + "Element Classic را روی دستگاه خود باز کنید" + "به تنظیمات > امنیت و حریم خصوصی بروید" + "در مدیریت کلیدهای رمزنگاری، بازیابی پیام‌های رمزگذاری‌شده را انتخاب کنید." + "برای فعال کردن فضای ذخیره‌سازی کلید، دستورالعمل‌ها را دنبال کنید" + "به %1$s برگردید" + "قبل از ادامه به %1$s، فضای ذخیره‌سازی کلید خود را فعال کنید" "نگارش %1$s" + "درحال بررسی حساب…" "ورود دستی" "ورود به %1$s" "ورود با کد QR" "ایجاد حساب" + "خوش برگشتید" "به سریع‌ترین %1$s خوش آمدید. بازطرّاحی شده برای سرعت و سادگی." "به %1$s خوش آمدید. بازطرّاحی شده برای سرعت و سادگی." "در المنتتان باشید" @@ -53,6 +68,8 @@ "درخواست ورد لغو شد" "ورود به دست افزارهٔ دیگر رد شد." "ورود رد شد" + "لازم نیست کار دیگری انجام دهید." + "دستگاه دیگر شما قبلاً وارد سیستم شده است" "ورود منقضی شد. لطفاً دوباره تلاش کنید." "ورود در زمان معیّن کامل نشد" "افزارهٔ دیگرتان از ورود به %s با کد پاس پشتیبانی نمی‌کند. @@ -67,6 +84,7 @@ "گزینش %1$s" "«پیوند افزارهٔ جدید»" "پویش کد پاس با این افزاره" + "فقط در صورتی موجود است که ارائه دهنده حساب شما از آن پشتیبانی کند." "گشودن %1$s روی افزاره‌ای دیگر برای گرفتن کد پاس" "استفاده از کد پاس نشان داده روی افزارهٔ دیگر." "تلاش دوباره" @@ -85,5 +103,6 @@ "ماتریکس شبکه‌ای بار برای ارتباطات نامتمرکز و امن است." "جایی که گفت‌وگوهایتان خواهند زیست — درست مثل استفاده‌تان از فراهم کنندهٔ رایانامه‌ای برای نگه داشتن رایانامه‌هایتان." "دارید به %1$s وارد می‌شوید" + "ارائه دهنده حساب را انتخاب کنید" "دارید روی %1$s حساب می‌سازید" diff --git a/features/logout/impl/src/main/res/values-ar/translations.xml b/features/logout/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..6825a5c54d --- /dev/null +++ b/features/logout/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,18 @@ + + + "أمتأكد من إزالة هذا الجهاز؟" + "إزالة هذا الجهاز" + "إزالة هذا الجهاز" + "تجري إزالة الجهاز…" + "هذا هو جهازك الوحيد. إذا أزلته، فستحتاج إلى مفتاح استرداد لتأكيد هويتك الرقمية واستعادة محادثاتك المشفّرة في المرة التالية التي تسجّل فيها الدخول." + "أنت على وشك فقدان الوصول إلى محادثاتك المشفّرة" + "كان النسخ الاحتياطي لمفاتيحك لا يزال جاريًا عندما أصبحت غير متصل. أعد الاتصال حتى يمكن نسخ مفاتيحك احتياطيًا قبل إزالة هذا الجهاز." + "لا يزال النسخ الاحتياطي لمفاتيحك جاريًا" + "يُرجى الانتظار حتى تكتمل هذه العملية قبل إزالة هذا الجهاز." + "لا يزال النسخ الاحتياطي لمفاتيحك جاريًا" + "إزالة هذا الجهاز" + "هذا هو جهازك الوحيد. إذا أزلته، فستحتاج إلى مفتاح استرداد لتأكيد هويتك الرقمية واستعادة محادثاتك المشفّرة في المرة التالية التي تسجّل فيها الدخول." + "أنت على وشك فقدان الوصول إلى محادثاتك المشفّرة" + "هذا هو جهازك الوحيد. إذا أزلته، فستحتاج إلى مفتاح استرداد لتأكيد هويتك الرقمية واستعادة محادثاتك المشفّرة في المرة التالية التي تسجّل فيها الدخول." + "تأكد من أن لديك إمكانية الوصول إلى مفتاح الاسترداد الخاص بك قبل إزالة هذا الجهاز" + diff --git a/features/logout/impl/src/main/res/values-es/translations.xml b/features/logout/impl/src/main/res/values-es/translations.xml index 94d0ec56a8..6c451c0834 100644 --- a/features/logout/impl/src/main/res/values-es/translations.xml +++ b/features/logout/impl/src/main/res/values-es/translations.xml @@ -1,18 +1,18 @@ - "¿Estás seguro de que quieres cerrar sesión?" - "Cerrar sesión" - "Cerrar sesión" - "Cerrando sesión…" - "Estás a punto de cerrar tu última sesión. Si cierras sesión ahora, perderás el acceso a tus mensajes cifrados." - "Has desactivado la copia de seguridad" - "Se estaba haciendo una copia de seguridad de tus claves cuando te desconectaste. Vuelve a conectarte para que se haga una copia de seguridad de tus claves antes de desconectarte." + "¿Estás seguro de que quieres eliminar este dispositivo?" + "Eliminar este dispositivo" + "Eliminar este dispositivo" + "Eliminando dispositivo…" + "Este es tu único dispositivo. Si lo eliminas, necesitarás una clave de recuperación para poder confirmar tu identidad digital y restaurar tus chats cifrados la próxima vez que inicies sesión." + "Estás a punto de perder acceso a tus chats cifrados" + "Se estaba haciendo una copia de seguridad de tus claves cuando te desconectaste. Vuelve a conectarte para que se haga una copia de seguridad de tus claves antes de eliminar este dispositivo." "Se sigue guardando una copia de seguridad de tus claves" - "Espera a que se complete antes de cerrar sesión." + "Espera a que se complete antes de eliminar este dispositivo." "Se sigue guardando una copia de seguridad de tus claves" - "Cerrar sesión" - "Estás a punto de cerrar tu última sesión. Si cierras sesión ahora, perderás el acceso a tus mensajes cifrados." - "La recuperación no está configurada" - "Estás a punto de cerrar tu última sesión. Si cierras la sesión ahora, podrías perder el acceso a tus mensajes cifrados." - "¿Has guardado tu clave de recuperación?" + "Eliminar este dispositivo" + "Este es tu único dispositivo. Si lo eliminas, necesitarás una clave de recuperación para poder confirmar tu identidad digital y restaurar tus chats cifrados la próxima vez que inicies sesión." + "Estás a punto de perder el acceso a tus chats cifrados." + "Este es tu único dispositivo. Si lo eliminas, necesitarás una clave de recuperación para poder confirmar tu identidad digital y restaurar tus chats cifrados la próxima vez que inicies sesión." + "Asegúrate de tener acceso a tu clave de recuperación antes de eliminar este dispositivo" diff --git a/features/messages/impl/src/main/res/values-ar/translations.xml b/features/messages/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..80c296fa94 --- /dev/null +++ b/features/messages/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,114 @@ + + + "مرسل الحدث لا يطابق مالك الجهاز الذي أرسله." + "لا يمكن ضمان أصالة هذه الرسالة المشفّرة على هذا الجهاز." + "تم التشفير بواسطة مستخدم تم التحقق منه سابقًا." + "غير مشفّرة." + "تم التشفير بواسطة جهاز غير معروف أو محذوف." + "تم التشفير بواسطة جهاز لم يتحقق منه مالكه." + "تم التشفير بواسطة مستخدم لم يتم التحقق منه." + "قلب الصورة أفقيًا" + "مقلوبة أفقيًا" + "الأصلية" + "قلب الصورة رأسيًا" + "مقلوبة رأسيًا" + "الأصلية" + "تدوير الصورة إلى اليسار" + + "%1$d درجات" + "%1$d درجة" + "%1$d درجتان" + "%1$d درجات" + "%1$d درجة" + "%1$d درجة" + + "تعديل الصورة" + "قد لا تكون التعليقات التوضيحية مرئية للأشخاص الذين يستخدمون تطبيقات أقدم." + "اضغط لتغيير جودة رفع الفيديو" + "تعذّر رفع الملف." + "تعذّرت معالجة الوسائط من أجل رفعها، يرجى المحاولة مجدداً." + "تعذّر رفع الوسائط، يرجى المحاولة مجدداً." + "الحد الأقصى المسموح به لحجم الملف هو %1$s." + "الملف كبير جدًا ولا يمكن رفعه" + "العنصر %1$d من %2$d" + "تحسين جودة الصورة" + "جارٍ المعالجة…" + "إضافة وسائط" + "حظر المستخدم" + "حدِّد على هذا الخيار إذا كنت تريد إخفاء كل الرسائل الحالية والمستقبلية من هذا المستخدم" + "ستُبلَّغ هذه الرسالة إلى إدارة السيرفر خاصتك. لن يكونوا قادرين على قراءة أي رسالة مشفرة." + "سبب إبلاغك عن هذا المحتوى" + "الكاميرا" + "التقاط صورة" + "تسجيل مقطع فيديو" + "مرفق" + "معرض الصور والفيديوهات" + "مشاركة الموقع الجغرافي" + "استفتاء" + "تنسيق النص" + "سجل الرسائل غير متوفر حالياً." + "سجل الرسائل غير متاح في هذه الغرفة. تحقّق من هذا الجهاز لرؤية سجل رسائلك." + "هل تريد دعوته مجدداً؟" + "أنت لوحدك في هذه المحادثة" + "إشعار الغرفة بأكملها" + "الجميع" + "أرسل مجدداً" + "فشل إرسال رسالتك" + "أضف تفاعل" + "هنا بداية %1$s" + "هنا بداية المحادثة" + "مكالمة غير مدعومة. اسأل المتصل عما إذا كان بإمكانه استخدام تطبيق Element X الجديد." + "أظهر أقل" + "نُسِخت الرسالة" + "لا تملك الإذن بالنشر في هذه الغرفة" + + "تفاعل %1$d أعضاء بـ %2$s" + "تفاعل %1$d عضو بـ %2$s" + "تفاعل %1$d عضوان بـ %2$s" + "تفاعل %1$d أعضاء بـ %2$s" + "تفاعل %1$d عضوًا بـ %2$s" + "تفاعل %1$d عضو بـ %2$s" + + + "تفاعلت أنت و%1$d أعضاء بـ %2$s" + "تفاعلت أنت و%1$d عضو بـ %2$s" + "تفاعلت أنت و%1$d عضوان بـ %2$s" + "تفاعلت أنت و%1$d أعضاء بـ %2$s" + "تفاعلت أنت و%1$d عضوًا بـ %2$s" + "تفاعلت أنت و%1$d عضو بـ %2$s" + + "تفاعلت بـ %1$s" + "أظهر أقل" + "أظهر المزيد" + "اعرض ملخص التفاعلات" + "جديدة" + + "%1$d تغيير في الغرفة" + "%1$d تغيير في الغرفة" + "%1$d تغيير في الغرفة" + "%1$d تغييرات في الغرفة" + "%1$d تغيير في الغرفة" + "%1$d تغيير في الغرفة" + + "الانتقال إلى الغرفة الجديدة" + "استُبدلت هذه الغرفة ولم تعد نشطة" + "عرض الرسائل القديمة" + "هذه الغرفة هي استمرار لغرفة أخرى" + + "%1$s و%2$s و%3$d آخرون" + "%1$s و%2$s و%3$d آخر" + "%1$s و%2$s و%3$d آخران" + "%1$s و%2$s و%3$d آخرون" + "%1$s و%2$s و%3$d آخر" + "%1$s و%2$s و%3$d آخر" + + + "%1$s يكتبون الآن" + "%1$s يكتب الآن" + "%1$s يكتبان الآن" + "%1$s يكتبون الآن" + "%1$s يكتبون الآن" + "%1$s يكتبون الآن" + + "%1$s و%2$s" + diff --git a/features/messages/impl/src/main/res/values-es/translations.xml b/features/messages/impl/src/main/res/values-es/translations.xml index 0a0b57cc5e..ed725bc9d2 100644 --- a/features/messages/impl/src/main/res/values-es/translations.xml +++ b/features/messages/impl/src/main/res/values-es/translations.xml @@ -7,9 +7,29 @@ "Cifrado por un dispositivo desconocido o eliminado." "Cifrado por un dispositivo no verificado por su propietario." "Cifrado por un usuario no verificado." + "Voltear la imagen horizontalmente" + "Volteada horizontalmente" + "Original" + "Voltear la imagen verticalmente" + "Volteada verticalmente" + "Original" + "Rotar la imagen hacia la izquierda" + + "%1$d grado" + "%1$d grados" + + "Editar foto" "Es posible que las leyendas no sean visibles para las personas que usan aplicaciones más antiguas." + "Toca para cambiar la calidad de subida del vídeo" + "No se pudo subir el archivo." "Error al procesar el contenido multimedia, por favor inténtalo de nuevo." "Error al subir el contenido multimedia, por favor inténtalo de nuevo." + "El tamaño máximo permitido de archivo es de %1$s." + "El archivo es demasiado grande para subirlo" + "Elemento %1$d de %2$d" + "Optimizar la calidad de la imagen" + "Procesando…" + "Añadir contenido multimedia" "Bloquear usuario" "Marca esta casilla si quieres ocultar todos los mensajes actuales y futuros de este usuario" "Se denunciará este mensaje al administrador de tu servidor base. No será capaz de leer ningún mensaje cifrado." @@ -19,7 +39,7 @@ "Grabar video" "Archivo adjunto" "Biblioteca de fotos y vídeos" - "Ubicación" + "Compartir ubicación" "Encuesta" "Formato de texto" "El historial de mensajes no está disponible en este momento." @@ -37,10 +57,23 @@ "Mostrar menos" "Mensaje copiado" "No tienes permiso para publicar en esta sala" + + "%1$d miembro reaccionó con %2$s" + "%1$d miembros reaccionaron con %2$s" + + + "Tú y %1$d miembro reaccionasteis con %2$s" + "Tú y %1$d miembros reaccionasteis con %2$s" + + "Reaccionaste con %1$s" "Mostrar menos" "Mostrar más" "Mostrar todas las reacciones" "Nuevos" + + "%1$d mensaje eliminado" + "%1$d mensajes eliminados" + "%1$d cambio en la sala" "%1$d cambios en la sala" diff --git a/features/messages/impl/src/main/res/values-fa/translations.xml b/features/messages/impl/src/main/res/values-fa/translations.xml index 4f8f737f9c..f10d9ead33 100644 --- a/features/messages/impl/src/main/res/values-fa/translations.xml +++ b/features/messages/impl/src/main/res/values-fa/translations.xml @@ -1,14 +1,35 @@ + "فرستنده رویداد با صاحب دستگاهی که آن را ارسال کرده است، مطابقت ندارد." "اعتبار این پیام رمز شده نمی‌تواند روی این افزاره تأیید شود." "رمز شده به دست کاربری از پیش تأیید شده." "رمز نشده." "رمز شده به دست افزاره‌ای ناشناخته یا حذف شده." "رمز شده به دست افزاره‌ای که از سوی مالکش تأیید نشده." "رمز شده به دست کاربری تأیید نشده." + "تصویر را به صورت افقی بچرخانید" + "به صورت افقی برگردانده شده" + "اصلی" + "تصویر را به صورت عمودی بچرخانید" + "به صورت عمودی برگردانده شده" + "اصلی" + "تصویر را به سمت چپ بچرخانید" + + "درجه %1$d" + "درجه‌های %1$d" + + "ویرایش عکس" + "ممکن است زیرنویس‌ها برای افرادی که از برنامه‌های قدیمی‌تر استفاده می‌کنند قابل مشاهده نباشند." + "برای تغییر کیفیت آپلود ویدیو، ضربه بزنید" + "فایل قابل آپلود نبود." "پردازش رسانه برای بارگذاری شکست خورد. لطفاً دوباره تلاش کنید." "بارگذاری رسانه شکست خورد. لطفاً دوباره تلاش کنید." + "حداکثر اندازه مجاز فایل %1$s است." + "فایل برای آپلود خیلی بزرگ است" + "مورد %1$d از %2$d" + "بهینه سازی کیفیت تصویر" "پردازش کردن…" + "افزودن رسانه" "انسداد کاربر" "اگر می‌خواهید همه پیام‌های فعلی و آینده را از این کاربر را پنهان کنید، علامت بزنید" "این پیام به مدیر کارساز خانگی شما گزارش خواهد شد. آنها قادر به خواندن پیام های رمزگذاری شده نخواهند بود." @@ -32,13 +53,27 @@ "افزودن شکلک" "آغاز %1$s است." "این، آغاز گفت‌وگوست." + "تماس پشتیبانی نشده. بپرسید که آیا تماس گیرنده می‌تواند از برنامه جدید Element X استفاده کند یا خیر." "نمایش کم‌تر" "پیام رونوشت شد" "اجازهٔ فرستادن به این اتاق را ندارید" + + "عضو %1$d با %2$s واکنش نشان داد" + "اعضای %1$d با %2$s واکنش نشان دادند" + + + "شما و عضو %1$d با %2$s واکنش نشان دادید" + "شما و اعضای %1$d با %2$s واکنش نشان دادید" + + "شما با %1$s واکنش نشان دادید" "نمایش کم‌تر" "نمایش بیش‌تر" "نمایش خلاصهٔ واکنش‌ها" "جدید" + + "پیام حذف شده %1$d" + "پیام‌های حذف شده %1$d" + "%1$dتغییر اتاق" "%1$dتغییر اتاق" diff --git a/features/messages/impl/src/main/res/values-fi/translations.xml b/features/messages/impl/src/main/res/values-fi/translations.xml index 3705047087..308ae57f2b 100644 --- a/features/messages/impl/src/main/res/values-fi/translations.xml +++ b/features/messages/impl/src/main/res/values-fi/translations.xml @@ -70,6 +70,10 @@ "Näytä lisää" "Näytä reaktioiden yhteenveto" "Uusi" + + "%1$d poistettu viesti" + "%1$d poistettua viestiä" + "%1$d muutos huoneeseen" "%1$d muutosta huoneeseen" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 0062421326..6d10d9a493 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -70,6 +70,10 @@ "Afficher plus" "Afficher le résumé des réactions" "Nouveau" + + "%1$d message supprimé" + "%1$d messages supprimés" + "%1$d changement dans le salon" "%1$d changements dans le salon" diff --git a/features/poll/api/src/main/res/values-ar/translations.xml b/features/poll/api/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..e55d1c8710 --- /dev/null +++ b/features/poll/api/src/main/res/values-ar/translations.xml @@ -0,0 +1,13 @@ + + + + "%1$d بالمئة من إجمالي الأصوات" + "%1$d بالمئة من إجمالي الأصوات" + "%1$d بالمئة من إجمالي الأصوات" + "%1$d بالمئة من إجمالي الأصوات" + "%1$d بالمئة من إجمالي الأصوات" + "%1$d بالمئة من إجمالي الأصوات" + + "ٍسيتم حذف الإختيار السابق" + "هذه هي الإجابة الفائزة" + diff --git a/features/poll/api/src/main/res/values-es/translations.xml b/features/poll/api/src/main/res/values-es/translations.xml index 10e1193163..b69a550320 100644 --- a/features/poll/api/src/main/res/values-es/translations.xml +++ b/features/poll/api/src/main/res/values-es/translations.xml @@ -1,4 +1,9 @@ + + "%1$d por ciento de todos los votos" + "%1$d por ciento de todos los votos" + + "Eliminará la selección anterior" "Esta es la respuesta ganadora" diff --git a/features/poll/api/src/main/res/values-fa/translations.xml b/features/poll/api/src/main/res/values-fa/translations.xml index af9620c7e8..1885c72e1b 100644 --- a/features/poll/api/src/main/res/values-fa/translations.xml +++ b/features/poll/api/src/main/res/values-fa/translations.xml @@ -1,5 +1,9 @@ + + "درصد %1$d از کل آرا" + "درصد %1$d از کل آرا" + "گزینش پیشین را برخواهد داشت" "این پاسخ برنده است" diff --git a/features/poll/impl/src/main/res/values-ar/translations.xml b/features/poll/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..02c12f72d8 --- /dev/null +++ b/features/poll/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,20 @@ + + + "إضافة خيار" + "أظهر النتائج فقط حين انتهاء الاستفتاء" + "إخفاء الأصوات" + "الخيار %1$d" + "لم تُحفظ تغييراتك. هل أنت متأكد من أنك تريد الرجوع؟" + "حذف الخيار %1$s" + "السؤال أو الموضوع" + "عن ماذا يدور هذا الاستفتاء؟" + "إنشاء استفتاء" + "هل أنت متأكد من رغبتك في حذف هذا الاستطلاع؟" + "حذف الاستطلاع" + "تعديل الاستفتاء" + "تعذّر العثور على أي استطلاعات جارية." + "تعذّر العثور على أي استطلاعات مغلقة." + "جارية" + "مغلقة" + "الاستطلاعات" + diff --git a/features/poll/impl/src/main/res/values-fa/translations.xml b/features/poll/impl/src/main/res/values-fa/translations.xml index eeedc6c7a0..fd6469a389 100644 --- a/features/poll/impl/src/main/res/values-fa/translations.xml +++ b/features/poll/impl/src/main/res/values-fa/translations.xml @@ -5,6 +5,7 @@ "نهفتن رأی‌ها" "گزینهٔ %1$d" "تغییراتتان ذخیره نشده‌اند. مطمئنید که می‌خواهید برگردید؟" + "حذف گزینه %1$s" "پرسش یا موضوع" "این نظرسنجی دربارهٔ چیست؟" "ایجاد نظرسنجی" diff --git a/features/preferences/impl/src/main/res/values-ar/translations.xml b/features/preferences/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..92f4539cd3 --- /dev/null +++ b/features/preferences/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,115 @@ + + + "للتأكد من عدم تفويت أي مكالمة مهمة، يُرجى تغيير إعداداتك للسماح بإشعارات ملء الشاشة عندما يكون هاتفك مقفلاً." + "حسّن تجربة مكالماتك" + "اختر طريقة استقبال الإشعارات" + "وضع المطوّر" + "فعّله للوصول إلى الميزات والوظائف الخاصة بالمطورين." + "عنوان URL الأساسي المخصص لـ Element Call" + "عيّن عنوان URL أساسيًا مخصصًا لـ Element Call." + "عنوان URL غير صالح، يُرجى التأكد من تضمين البروتوكول (http/https) والعنوان الصحيح." + "إخفاء الصور الرمزية في طلبات الدعوة إلى الغرف" + "إخفاء معاينات الوسائط في المخطط الزمني" + "المختبرات" + "المسافة التي يجب أن تتحركها ليتم إرسال تحديث." + "تأكد من تفعيل \"الموقع الدقيق\" لهذا التطبيق. لتغيير الإذن، انتقل إلى %1$s." + "إعدادات التطبيق" + "تحديثات الموقع المباشر" + + "كل %1$d أمتار" + "كل %1$d متر" + "كل %1$d مترين" + "كل %1$d أمتار" + "كل %1$d مترًا" + "كل %1$d متر" + + "رفع الصور ومقاطع الفيديو بسرعة أكبر وتقليل استخدام البيانات" + "تحسين جودة الوسائط" + "الإشراف والسلامة" + "تحسين الصور تلقائيًا لرفعها بسرعة أكبر وتقليل أحجام الملفات." + "تحسين جودة رفع الصور" + "%1$s. اضغط هنا للتغيير." + "عالية (1080p)" + "منخفضة (480p)" + "قياسية (720p)" + "جودة رفع الفيديو" + "موفّر الإشعارات الفورية" + "عطّل محرر النص المنسق لكتابة Markdown يدويًا." + "إيصالات القراءة" + "إذا كان هذا الخيار متوقفًا، فلن تُرسل إيصالات القراءة الخاصة بك إلى أي شخص. ستظل تتلقى إيصالات القراءة من المستخدمين الآخرين." + "مشاركة حالة التواجد" + "إذا كان هذا الخيار متوقفًا، فلن تتمكن من إرسال أو تلقي إيصالات القراءة أو إشعارات الكتابة." + "إخفاء دائمًا" + "إظهار دائمًا" + "في الغرف الخاصة" + "يمكن دائمًا إظهار الوسائط المخفية بالضغط عليها" + "إظهار الوسائط في المخطط الزمني" + "فعّل خيار عرض مصدر الرسالة في الخط الزمني." + "ليس لديك أي مستخدمين محظورين" + "إلغاء الحظر" + "ستتمكن من رؤية كل رسائلهم مجدّداً." + "إلغاء حظر المستخدم" + "جارٍ إلغاء الحظر…" + "الاسم" + "اسمك" + "حدث خطأ غير معروف وتعذّر تغيير المعلومات." + "غير قادر على تحديث الملف الشخصي" + "تعديل الملف الشخصي" + "يتم تحديث الملف الشخصي…" + "تفعيل الردود في السلاسل" + "سيُعاد تشغيل التطبيق لتطبيق هذا التغيير." + "جرّب أحدث أفكارنا قيد التطوير. هذه الميزات ليست نهائية؛ قد تكون غير مستقرة وقد تتغير." + "هل ترغب في التجربة؟" + "المختبرات" + "إعدادات إضافية" + "نغمة رنين المكالمات" + "المكالمات الصوتية والفيديو" + "عدم تطابق الإعدادات" + "لقد بسّطنا إعدادات الإشعارات لتكون الخيارات أسهل للإيجاد. بعض الإعدادات المخصصة التي كنت قد طبّقتها في السابق ليست ظاهرة هنا، ولكنها ما تزال مفعّلة. + +إذا ما تقدّمت، فبعض إعداداتك قد تتغير." + "المحادثات الخاصة" + "إعدادات خاصة لكل محادثة" + "حدث خطأ أثناء تحديث إعدادات الإشعارات." + "كل الرسائل" + "الإشارات والكلمات الرئيسية فقط" + "في المحادثات الخاصة، أرسل لي إشعارات لـ" + "في المحادثات الجماعية، أرسل لي إشعارات لـ" + "تفعيل الإشعارات على هذا الجهاز" + "تعذّر تصحيح الإعدادات، يُرجى المحاولة مرة أخرى." + "المحادثات الجماعية" + "الدعوات" + "لا يدعم خادمك المنزلي هذا الخيار في الغرف المشفّرة، وقد لا تتلقى إشعارات في بعض الغرف." + "الإشارات" + "اختيار صوت آخر…" + "يُستخدم حاليًا %1$s" + "صوت الرسائل" + "صوت الرسائل" + "الكل" + "الإشارات" + "أرسل لي إشعارات لـ" + "أرسل لي إشعاراً عندما يُكتب @room" + "مخصص" + "صوت مخصص…" + "خطأ في حذف الملف" + "افتراضي Element" + "Element Fade" + "خطأ في استيراد الملف" + "مشكلة في معاينة صوت التنبيه" + "الصوت" + "إغلاق خطأ صوت التنبيه" + "مشكلة في تعيين صوت التنبيه" + "صامت" + "افتراضي النظام" + "لتلقي الإشعارات يرجى تغيير الـ %1$s خاصتك" + "إعدادات النظام" + "إشعارات النظام مُطفأة" + "الإشعارات" + "أسود" + "داكن" + "فاتح" + "النظام" + "سجل الإشعارات الفورية" + "استكشاف الأخطاء وإصلاحها" + "استكشاف أخطاء الإشعارات وإصلاحها" + diff --git a/features/preferences/impl/src/main/res/values-es/translations.xml b/features/preferences/impl/src/main/res/values-es/translations.xml index dd496a1273..60ff403b41 100644 --- a/features/preferences/impl/src/main/res/values-es/translations.xml +++ b/features/preferences/impl/src/main/res/values-es/translations.xml @@ -10,9 +10,25 @@ "URL no válida, asegúrate de incluir el protocolo (http/https) y la dirección correcta." "Ocultar avatares en las invitaciones a salas" "Ocultar vistas previas de multimedia en la cronología" + "Experimental" + "La distancia que debes recorrer para que se active una actualización." + "Asegúrate de que la «Ubicación precisa» está activada para esta aplicación. Para cambiar el permiso, ve a %1$s." + "Ajustes de la aplicación" + "Actualizaciones de ubicación en tiempo real" + + "Cada %1$d metros" + "Cada %1$d metros" + "Sube fotos y vídeos más rápido y reduce el uso de datos" - "Optimizar la calidad de los medios" + "Optimizar la calidad multimedia" "Moderación y seguridad" + "Optimizar automáticamente las imágenes para subidas más rápidas y tamaños de archivo más pequeños." + "Optimizar la calidad de subida de imágenes" + "%1$s. Toca aquí para cambiar." + "Alta (1080p)" + "Baja (480p)" + "Estándar (720p)" + "Calidad de subida de vídeos" "Proveedor de notificaciones push" "Desactiva el editor de texto enriquecido para escribir Markdown manualmente." "Confirmaciones de lectura" @@ -36,7 +52,13 @@ "No se puede actualizar el perfil" "Editar perfil" "Actualizando perfil…" + "Activar respuestas en hilo" + "La aplicación se reiniciará para aplicar este cambio." + "Prueba nuestras últimas ideas en desarrollo. Estas funciones aún no están terminadas; pueden ser inestables y están sujetas a cambios." + "¿Ganas de experimentar?" + "Experimental" "Ajustes adicionales" + "Tono de llamada" "Llamadas de audio y vídeo" "La configuración no coincide" "Hemos simplificado la Configuración de Notificaciones para hacer las opciones más fáciles de encontrar. Algunas configuraciones personalizadas que has elegido en el pasado no se muestran aquí, pero siguen activas. @@ -55,14 +77,39 @@ Si continúas, es posible que algunos de tus ajustes cambien." "Invitaciones" "Tu servidor base no admite esta opción en salas cifradas, puede que no recibas notificaciones de algunas salas." "Menciones" + "Elegir otro sonido…" + "Usando actualmente %1$s" + "Sonido de mensaje" + "Sonido de mensaje" "Todos" "Menciones" "Notificarme para" "Notificarme con @room" + "Personalizado" + "Sonido personalizado…" + "Error al eliminar el archivo" + "Predeterminado de Element" + "Desvanecimiento de Element" + "Error al importar el archivo" + "Problema al previsualizar el sonido de alerta" + "Sonido" + "Ignorar el error del sonido de alerta" + "Problema al establecer el sonido de alerta" + "Silencioso" + "Predeterminado del sistema" "Para recibir notificaciones, cambia tus %1$s." "ajustes del sistema" "Notificaciones del sistema desactivadas" "Notificaciones" + "Ausente" + "Vuelvo enseguida" + "Personalizado…" + "Estado personalizado…" + "Hora de concentrarse" + "En una reunión" + "En la carretera" + "¿Cuál es tu estado?" + "Negro" "Oscuro" "Claro" "Sistema" diff --git a/features/preferences/impl/src/main/res/values-fa/translations.xml b/features/preferences/impl/src/main/res/values-fa/translations.xml index df42ca953c..167bf022ca 100644 --- a/features/preferences/impl/src/main/res/values-fa/translations.xml +++ b/features/preferences/impl/src/main/res/values-fa/translations.xml @@ -1,5 +1,6 @@ + "برای اطمینان از اینکه هرگز تماس مهمی را از دست نمی‌دهید، لطفاً تنظیمات خود را تغییر دهید تا وقتی تلفنتان قفل است، اعلان‌ها به صورت تمام صفحه نمایش داده شوند." "بهبود تجریهٔ تماستان" "گزینش چگونگی دریافت آگاهی" "حالت توسعه‌دهنده" @@ -10,8 +11,20 @@ "نهفتن چهرک‌ها در درخواست‌های دعوت اتاق" "نهفتن رسانه در خط زمانی" "آزمایشگاه‌ها" + "مسافتی که باید طی کنید تا به‌روزرسانی انجام شود." + "مطمئن شوید که «موقعیت مکانی دقیق» برای این برنامه فعال شده است. برای تغییر مجوز به %1$s بروید." + "تنظیمات برنامه" + "به‌روزرسانی‌های مکان زنده" + + "هر کنتور %1$d" + "هر کنتور %1$d" + + "عکس‌ها و ویدیوها را سریع‌تر آپلود کنید و مصرف اینترنت را کاهش دهید" "بهینه سازی کیفیت رسانه" "نظارت و امنیت" + "بهینه‌سازی خودکار تصاویر برای آپلود سریع‌تر و حجم کمتر فایل‌ها." + "بهینه سازی کیفیت آپلود تصاویر" + "%1$s. برای تغییر، اینجا را لمس کنید." "زیاد (۱۰۸۰ت)" "کم (۴۸۰ت)" "استاندارد (۷۲۰ت)" @@ -39,8 +52,13 @@ "ناتوان در به‌روز کردن نمایه" "ویرایش نمایه" "به‌روز کردن نمایه…" + "فعال کردن پاسخ‌های تاپیک" + "برنامه برای اعمال این تغییر مجدداً راه‌اندازی خواهد شد." + "آخرین ایده‌های در حال توسعه ما را امتحان کنید. این ویژگی‌ها هنوز نهایی نشده‌اند؛ ممکن است ناپایدار باشند، ممکن است تغییر کنند." + "احساس تجربی دارید؟" "آزمایشگاه‌ها" "تنظیمات اضافی" + "آهنگ زنگ تماس" "تماس‌های صوتی و تصویری" "نامتطابقت در پیکربندی" "تنظمیات آگاهی را ساده کرده‌ایم تا یافتن انتخاب‌ها را ساده‌تر کنیم. برهی تنظمیات سفارسی که در گذشته گزیده‌اید این‌جا نشان داده نمی‌شوند؛ ولی همچنن فعّالند. @@ -59,14 +77,39 @@ "دعوت‌ها" "کارساز خانگیتان از این گزینه در اتاق‌های رمز شده پشتیبانی نمی‌کند. ممکن است در برخی اتاق‌ها آگاه نشوید." "اشاره‌ها" + "صدای دیگری انتخاب کنید…" + "در حال حاضر از %1$s استفاده می‌شود." + "صدای پیام" + "صدای پیام" "همه" "اشاره‌ها" "آگاه کردنم برای" "آگاه کردنم برای ‪@room" + "سفارشی" + "صدای سفارشی…" + "خطا در حذف فایل" + "Element پیش‌فرض" + "Element فید" + "خطا در وارد کردن فایل" + "مشکل در پیش‌نمایش صدای هشدار" + "صدا" + "خطای صدای هشدار را رد کنید" + "مشکل در تنظیم صدای هشدار" + "بی‌صدا" + "پیش‌فرض سیستم" "برای گرفتن آگاهی‌ها لطفاً%1$sتان را تغییر دهید." "تنظیمات سامانه" "آگاهی‌های سامانه‌ای خاموش شدند" "آگاهی‌ها" + "بعدا" + "همین الان برگرد" + "سفارشی…" + "وضعیت سفارشی…" + "زمان تمرکز" + "در یک جلسه" + "در جاده" + "وضعیتت چطوره؟" + "سیاه" "تیره" "روشن" "سامانه" diff --git a/features/preferences/impl/src/main/res/values-fi/translations.xml b/features/preferences/impl/src/main/res/values-fi/translations.xml index f37673427f..c50989e526 100644 --- a/features/preferences/impl/src/main/res/values-fi/translations.xml +++ b/features/preferences/impl/src/main/res/values-fi/translations.xml @@ -68,7 +68,7 @@ Jos jatkat, jotkin asetukset saattavat muuttua." "Keskustelukohtaiset asetukset" "Ilmoitusasetusten muokkaamisessa tapahtui virhe." "Kaikista viesteistä" - "Vain maininnoista ja avainsanoista" + "Vain maininnoista ja vastauksista" "Yksityiskeskusteluissa, ilmoita minulle" "Ryhmäkeskusteluissa, ilmoita minulle" "Ota ilmoitukset käyttöön tällä laitteella" @@ -101,6 +101,14 @@ Jos jatkat, jotkin asetukset saattavat muuttua." "järjestelmäsi asetuksia" "Järjestelmän ilmoitukset on poissa päältä" "Ilmoitukset" + "Poissa" + "Palaan pian" + "Mukautettu…" + "Mukautettu tila…" + "Keskittymisaika" + "Kokouksessa" + "Tien päällä" + "Mikä on tilasi?" "Musta" "Tumma" "Vaalea" diff --git a/features/preferences/impl/src/main/res/values-fr/translations.xml b/features/preferences/impl/src/main/res/values-fr/translations.xml index fc57491f4a..06fe3be814 100644 --- a/features/preferences/impl/src/main/res/values-fr/translations.xml +++ b/features/preferences/impl/src/main/res/values-fr/translations.xml @@ -101,6 +101,14 @@ Si vous continuez, il est possible que certains de vos paramètres soient modifi "paramètres du système" "Les notifications du système sont désactivées" "Notifications" + "Absent" + "Je reviens tout de suite" + "Personnalisé…" + "Statut personnalisé…" + "Concentré" + "En réunion" + "Sur la route" + "Quel est votre statut ?" "Noir" "Sombre" "Clair" diff --git a/features/rageshake/api/src/main/res/values-ar/translations.xml b/features/rageshake/api/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..13f99159db --- /dev/null +++ b/features/rageshake/api/src/main/res/values-ar/translations.xml @@ -0,0 +1,7 @@ + + + "%1$s أُغلِق قسرياً في آخر مرة استخدمه. أتودّ مشاركة بلاغ إغلاق قسري معنا؟" + "يظهر أنك تهز الهاتز بسرعة. أتودّ فتح شاشة الإبلاغ عن عطل؟" + "رج الهاتف للإبلاغ عن خطأ" + "عتبة الكشف" + diff --git a/features/rageshake/impl/src/main/res/values-ar/translations.xml b/features/rageshake/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..5fd8157467 --- /dev/null +++ b/features/rageshake/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,22 @@ + + + "إرفاق لقطة شاشة" + "لا مانع من التواصل معي إذا كان عندكم أي أسئلة إضافية." + "تواصل معي" + "تعديل لقطة الشاشة" + "صِف مشكلتك رجاءً. ما الذي قمت به؟ ما كنت تتوقع أن يحصل؟ وما الذي حصل فعلياً. زوّدنا بأكبر قدر من المعلومات رجاءً." + "صِف المشكلة…" + "إن كان ممكناً فاكتب الوصف بالإنجليزية." + "الوصف قصير جدًا، يُرجى تقديم مزيد من التفاصيل حول ما حدث. شكرًا!" + "يمكنك إدخال رقم مشكلة مرتبطة على GitHub، إن وجدت." + "مشكلة على GitHub" + "أرسل الـcrash logs" + "السماح بالـLogs" + "سجلاتك كبيرة جدًا، لذلك لا يمكن تضمينها في هذا البلاغ. يرجى إرسالها إلينا بطريقة أخرى." + "إرسال لقطة شاشة" + "سيتمّ تضمين السجلّات (logs) مع رسالتك لنتأكد من أن كل شيء يشتغل كما يجب. لكي ترسل الرسالة بلا سجلّات فأطفئ هذا الخيار." + "%1$s أُغلِق قسرياً في آخر مرة استخدمه. أتودّ مشاركة بلاغ إغلاق قسري معنا؟" + "إذا كنت تواجه مشكلات في الإشعارات، يمكن أن يساعد رفع قواعد الإشعارات الفورية في تحديد السبب الجذري. لاحظ أن هذه القواعد قد تحتوي على معلومات خاصة، مثل اسم العرض الخاص بك أو الكلمات المفتاحية التي تريد تلقي إشعارات عنها." + "إرسال إعدادات الإشعارات" + "عرض السجلات" + diff --git a/features/rageshake/impl/src/main/res/values-es/translations.xml b/features/rageshake/impl/src/main/res/values-es/translations.xml index 5c21e4a9bd..5841d8b29b 100644 --- a/features/rageshake/impl/src/main/res/values-es/translations.xml +++ b/features/rageshake/impl/src/main/res/values-es/translations.xml @@ -8,11 +8,15 @@ "Describe el problema…" "Si es posible, escribe la descripción en inglés." "La descripción es demasiado corta. Proporciona más detalles sobre lo sucedido. ¡Gracias!" + "Puedes introducir el número de una incidencia de GitHub asociada, si la hubiere." + "Incidencia de GitHub" "Enviar registros de fallos" "Permitir registros" "Tus registros de errores son demasiado grandes y no pueden añadirse a este informe, por favor envíanoslos de otra manera." "Enviar captura de pantalla" "Los registros se incluirán con tu mensaje para asegurarse de que todo funciona correctamente. Para enviar tu mensaje sin registros, desactiva esta opción." "%1$s se cerró inesperadamente la última vez que se lo usaste. ¿Quieres compartir un informe de error con nosotros?" + "Si tienes problemas con las notificaciones, enviar las reglas de notificaciones push puede ayudarnos a identificar la causa raíz. Ten en cuenta que estas reglas pueden contener información privada, como tu nombre público o las palabras clave para las que quieres recibir notificaciones." + "Enviar ajustes de notificación" "Ver los registros" diff --git a/features/rageshake/impl/src/main/res/values-fa/translations.xml b/features/rageshake/impl/src/main/res/values-fa/translations.xml index 2e27f97ba6..85d07c41cd 100644 --- a/features/rageshake/impl/src/main/res/values-fa/translations.xml +++ b/features/rageshake/impl/src/main/res/values-fa/translations.xml @@ -8,10 +8,15 @@ "شرح مشکل…" "ترجیحاً توضیحات را به زبان انگلیسی بنویسید." "توضیحات خیلی کوتاه است، لطفاً جزئیات بیشتری در مورد آنچه اتفاق افتاده ارائه دهید. متشکرم!" + "در صورت وجود، می‌توانید شماره مشکل مرتبط با GitHub را وارد کنید." + "مسئله GitHub" "ارسال رخدادنگارهای خطا" "اجازه به گزارش‌ها" + "گزارش‌های شما بیش از حد بزرگ هستند، بنابراین نمی‌توان آنها را در این گزارش گنجاند، لطفاً آنها را از طریق دیگری برای ما ارسال کنید." "ارسال تصویر صفحه" "برای اطمینان از درست کار کردن همه‌چیز گزارش‌ها در پیامتان قرار خواهد گرفت. برای فرستادن پیام بدون گزارش‌ها این تنظیم را خاموش کنید." "%1$sآخرین باری که استفاده شد، از کار افتاد. آیا مایلید گزارش خرابی را با ما به اشتراک بگذارید؟" + "اگر با اعلان‌ها مشکل دارید، آپلود قوانین ارسال اعلان می‌تواند به ما در شناسایی علت اصلی کمک کند. توجه داشته باشید که این قوانین می‌توانند حاوی اطلاعات خصوصی مانند نام نمایشی یا کلمات کلیدی شما باشند که باید برای آنها اعلان دریافت کنید." + "تنظیمات ارسال اعلان" "دیدن گزارش‌ها" diff --git a/features/reportroom/impl/src/main/res/values-ar/translations.xml b/features/reportroom/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..1068833e9b --- /dev/null +++ b/features/reportroom/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,8 @@ + + + "تم إرسال بلاغك بنجاح، لكننا واجهنا مشكلة أثناء محاولة مغادرة الغرفة. يرجى المحاولة مرة أخرى." + "تعذّرت مغادرة الغرفة" + "أبلغ مسؤولك عن هذه الغرفة. إذا كانت الرسائل مشفّرة، فلن يتمكن مسؤولك من قراءتها." + "صِف سبب الإبلاغ…" + "الإبلاغ عن الغرفة" + diff --git a/features/reportroom/impl/src/main/res/values-fa/translations.xml b/features/reportroom/impl/src/main/res/values-fa/translations.xml index 9930e9ceac..a75095ff90 100644 --- a/features/reportroom/impl/src/main/res/values-fa/translations.xml +++ b/features/reportroom/impl/src/main/res/values-fa/translations.xml @@ -1,6 +1,8 @@ + "گزارش شما با موفقیت ارسال شد، اما هنگام تلاش برای خروج از اتاق با مشکلی مواجه شدیم. لطفاً دوباره امتحان کنید." "ناتوان از ترک اتاق" + "این اتاق را به مدیر خود گزارش دهید. اگر پیام‌ها رمزگذاری شده باشند، مدیر شما قادر به خواندن آنها نخواهد بود." "شرح دلیل گزارش…" "گزارش اتاق" diff --git a/features/rolesandpermissions/impl/src/main/res/values-ar/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..75633a1273 --- /dev/null +++ b/features/rolesandpermissions/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,98 @@ + + + "مسؤول" + "حظر الأشخاص" + "تغيير الإعدادات" + "إزالة الرسائل" + "عضو" + "دعوة الأشخاص" + "مشاركة الموقع المباشر" + "إدارة المساحة" + "إدارة الغرف" + "إدارة الأعضاء" + "الرسائل والمحتوى" + "مشرف" + "إزالة الأشخاص" + "تغيير الصورة الرمزية" + "تعديل التفاصيل" + "تغيير الاسم" + "تغيير الموضوع" + "إرسال الرسائل" + "الصلاحيات" + "تعديل المسؤولين" + "لن تتمكن من التراجع عن هذا الإجراء. ستتم ترقية المستخدم ليحصل على مستوى الصلاحيات نفسه الذي لديك." + "إضافة مسؤول؟" + "لن تتمكن من التراجع عن هذا الإجراء. ستنقل الملكية إلى المستخدمين المحددين. بمجرد مغادرتك، سيصبح ذلك دائمًا." + "نقل الملكية؟" + "خفض الرتبة" + "لن تتمكن من التراجع عن هذا التغيير لأنك تخفض رتبتك. إذا كنت آخر مستخدم لديه صلاحيات في الغرفة، فسيكون من المستحيل استعادة الصلاحيات." + "خفض رتبتك؟" + "%1$s (قيد الانتظار)" + "(قيد الانتظار)" + "يتمتع المسؤولون تلقائيًا بصلاحيات المشرفين" + "يتمتع المالكون بامتيازات المسؤولين تلقائيًا." + "تعديل المشرفين" + "اختيار المالكين" + "المسؤولون" + "المشرفون" + "الأعضاء" + "لديك تغييرات غير محفوظة." + "حفظ التغييرات؟" + "لا يوجد مستخدمون محظورون." + + "%1$d محظورين" + "%1$d محظور" + "%1$d محظوران" + "%1$d محظورين" + "%1$d محظورًا" + "%1$d محظور" + + "تحقّق من الإملاء أو جرّب بحثًا جديدًا" + "لا توجد نتائج لـ “%1$s”" + + "%1$d شخص" + "%1$d شخص" + "%1$d شخصين" + "%1$d اشخاص" + "%1$d شخص" + "%1$d شخص" + + "حظر المستخدم" + "إزالة العضو فقط" + "إلغاء الحظر" + "يمكن لهذا المستخدم الانضمام إلى هذه الغرفة مرة أخرى عند دعوته." + "إلغاء حظر المستخدم" + "المحظورون" + "الأعضاء" + + "%1$d مدعوّين" + "%1$d مدعو" + "%1$d مدعوّان" + "%1$d مدعوّين" + "%1$d مدعوًا" + "%1$d مدعو" + + "قيد الانتظار" + "مسؤول" + "مشرف" + "مالك" + "أعضاء الغرفة" + "جارٍ رفع حظر %1$s" + "المسؤولون" + "المسؤولون والمالكون" + "تغيير دوري" + "خفض رتبتي إلى عضو" + "خفض رتبتي إلى مشرف" + "الإشراف على الأعضاء" + "الرسائل والمحتوى" + "المشرفون" + "المالكون" + "الصلاحيات" + "إعادة تعيين الصلاحيات" + "بمجرد إعادة تعيين الصلاحيات، ستفقد الإعدادات الحالية." + "إعادة تعيين الصلاحيات؟" + "الأدوار" + "تفاصيل الغرفة" + "تفاصيل المساحة" + "الأدوار والصلاحيات" + diff --git a/features/rolesandpermissions/impl/src/main/res/values-es/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-es/translations.xml index a41489ea1b..defb25b9be 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-es/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-es/translations.xml @@ -2,9 +2,13 @@ "Administrador" "Vetar personas" + "Cambiar ajustes" "Eliminar mensajes" "Miembro" "Invitar a otras personas" + "Compartir ubicación en tiempo real" + "Gestionar el espacio" + "Gestionar salas" "Administrar miembros" "Mensajes y contenido" "Moderador" @@ -14,25 +18,36 @@ "Cambiar el nombre de la sala" "Cambiar el tema de la sala" "Enviar mensajes" + "Permisos" "Editar administradores" "No podrás deshacer esta acción. Estás promocionando al usuario para que tenga el mismo nivel de poder que tú." - "¿Agregar Admin?" + "¿Agregar administrador?" + "Esta acción es irreversible. Estás transfiriendo la propiedad a los usuarios seleccionados. Una vez que salgas, será permanente." + "¿Transferir la propiedad?" "Degradar" "No podrás deshacer este cambio ya que te estás degradando. Si eres el último usuario privilegiado en la sala será imposible recuperar los privilegios." "¿Degradarte?" "%1$s (Pendiente)" "(Pendiente)" "Los administradores tienen privilegios de moderador de forma automática" + "Los propietarios tienen automáticamente privilegios de administrador." "Editar moderadores" + "Elegir propietarios" "Administradores" "Moderadores" "Miembros" "Tienes cambios sin guardar." "¿Guardar cambios?" - "No hay usuarios baneados." + "No hay usuarios vetados." + + "%1$d vetado" + "%1$d vetados" + + "Revisa la ortografía o intenta una nueva búsqueda." + "Ningún resultado para «%1$s»" - "%1$d Persona" - "%1$d Personas" + "%1$d persona" + "%1$d personas" "Sacar y vetar a un miembro" "Solo eliminar miembro" @@ -41,21 +56,31 @@ "Eliminar veto en la sala" "Vetados" "Miembros" + + "%1$d invitado" + "%1$d invitados" + + "Pendiente" "Administrador" "Moderador" + "Propietario" "Miembros de la sala" "Levantando veto a %1$s" "Administradores" + "Administradores y propietarios" "Cambiar mi rol" "Degradar a miembro" "Degradar a moderador" "Moderación de miembros" "Mensajes y contenido" "Moderadores" + "Propietarios" + "Permisos" "Restablecer permisos" "Una vez que restablezca los permisos, perderá la configuración actual." "¿Restablecer los permisos?" "Roles" "Detalles de la sala" + "Detalles del espacio" "Roles y permisos" diff --git a/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml index e381cec856..345a451678 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml @@ -2,9 +2,13 @@ "ادمین" "تحریم افراد" + "تغییر تنظیمات" "حذف پیام‌ها" "عضو" "دعوت کاربران" + "اشتراک گذاری موقعیت مکانی زنده" + "مدیریت فضا" + "مدیریت اتاق‌ها" "مدیریت اعضا" "پیام‌ها و محتوا" "ناظم" @@ -14,9 +18,11 @@ "تغییر نام اتاق" "دگرگونی موضوع اتاق" "فرستادن پیام‌ها" + "دسترسی‌ها" "ویرایش مدیران" "قادر نخواهید بود این کنش را بازکردانید. داردید کاربر را به سطح قدرت خودتان ارتقا می‌دهید." "افزودن مدیر؟" + "شما قادر به لغو این اقدام نخواهید بود. شما در حال انتقال مالکیت به کاربران انتخاب شده هستید. پس از خروج، این انتقال دائمی خواهد بود." "انتقال مالکیت؟" "تنزل بده" "شما نمی‌توانید این تغییر را بازگردانید زیرا در حال تنزل نقش خود در اتاق هستید، اگر آخرین کاربر ممتاز در اتاق باشید، امکان دستیابی مجدد به دسترسی‌های سطح بالای اتاق غیرممکن است." @@ -33,6 +39,12 @@ "تغییراتی ذخیره نشده دارید." "ذخیرهٔ تغییرات؟" "هیچ کاربری محروم نشده." + + "%1$d محروم شد" + "%1$d محروم شد" + + "املای کلمات را بررسی کنید یا جستجوی جدیدی را امتحان کنید" + "هیچ نتیجه‌ای برای «%1$s» یافت نشد." "%1$d نفر" "%1$d نفر" @@ -44,6 +56,11 @@ "تحریم نکردن از اتاق" "محروم" "اعضا" + + "%1$d دعوت شده" + "%1$d دعوت شده" + + "در حال بررسی" "ادمین" "ناظم" "مالک" @@ -58,10 +75,12 @@ "پیام‌ها و محتوا" "ناظم‌ها" "مالکان" + "دسترسی‌ها" "بازنشانی اجازه‌ها" "پس از بازنشانی اجازه‌ها تنظیمات فعلی را از دست خواهید داد." "بازنشانی اجازه‌ها؟" "نقش‌ها" "جزییات اتاق" + "جزئیات فضا" "نقش‌ها و مجوزها" diff --git a/features/roomaliasresolver/impl/src/main/res/values-ar/translations.xml b/features/roomaliasresolver/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..6500dcebf6 --- /dev/null +++ b/features/roomaliasresolver/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,5 @@ + + + "تعذّر عرض معاينة هذه الغرفة" + "تعذّر حل الاسم المستعار للغرفة." + diff --git a/features/roomaliasresolver/impl/src/main/res/values-fa/translations.xml b/features/roomaliasresolver/impl/src/main/res/values-fa/translations.xml index 4fba81c4f8..f9fdf67b21 100644 --- a/features/roomaliasresolver/impl/src/main/res/values-fa/translations.xml +++ b/features/roomaliasresolver/impl/src/main/res/values-fa/translations.xml @@ -1,4 +1,5 @@ + "ما نتوانستیم پیش‌نمایش این اتاق را نمایش دهیم" "شکست در دریافت نام مستعار اتاق." diff --git a/features/roomdetails/impl/src/main/res/values-ar/translations.xml b/features/roomdetails/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..3d640a615a --- /dev/null +++ b/features/roomdetails/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,188 @@ + + + "لا يرى الأعضاء الجدد السجل" + "يرى الأعضاء الجدد السجل" + "يمكن لأي شخص رؤية السجل" + "تحتاج إلى عنوان حتى تظهر الغرفة في الدليل العام." + "تعديل العنوان" + "حدث خطأ أثناء تحديث إعدادات الإشعارات." + "لا يدعم خادمك المنزلي هذا الخيار في الغرف المشفّرة، وقد لا تتلقى إشعارات في بعض الغرف." + "الاستطلاعات" + "مسؤول" + "حظر الأشخاص" + "إزالة الرسائل" + "عضو" + "دعوة الأشخاص" + "إدارة الأعضاء" + "الرسائل والمحتوى" + "مشرف" + "إزالة الأشخاص" + "تغيير الصورة الرمزية" + "تعديل التفاصيل" + "تغيير الاسم" + "تغيير الموضوع" + "إرسال الرسائل" + "تعديل المسؤولين" + "لن تتمكن من التراجع عن هذا الإجراء. ستتم ترقية المستخدم ليحصل على مستوى الصلاحيات نفسه الذي لديك." + "إضافة مسؤول؟" + "لن تتمكن من التراجع عن هذا الإجراء. ستنقل الملكية إلى المستخدمين المحددين. بمجرد مغادرتك، سيصبح ذلك دائمًا." + "نقل الملكية؟" + "خفض الرتبة" + "لن تتمكن من التراجع عن هذا التغيير لأنك تخفض رتبتك. إذا كنت آخر مستخدم لديه صلاحيات في الغرفة، فسيكون من المستحيل استعادة الصلاحيات." + "خفض رتبتك؟" + "%1$s (قيد الانتظار)" + "(قيد الانتظار)" + "يتمتع المسؤولون تلقائيًا بصلاحيات المشرفين" + "يتمتع المالكون بامتيازات المسؤولين تلقائيًا." + "تعديل المشرفين" + "اختيار المالكين" + "المسؤولون" + "المشرفون" + "الأعضاء" + "لديك تغييرات غير محفوظة." + "حفظ التغييرات؟" + "إضافة موضوع" + "مشفّرة" + "غير مشفّرة" + "غرفة عامة" + "تعديل التفاصيل" + "قد كان هناك خطأ والمعلومات لم تتحدث." + "غير قادر على تعديل الغرفة" + "الرسائل مؤمَّنة بأقفال. أنت والمستقبلون فقط من تملكون المفاتيح الخاصة لفتح أقفالها." + "تشفير الرسائل مفعّل" + "حصل خطأ أثناء تحميل إعدادات الإشعارات." + "تعذّر كتم هذه الغرفة، يرجى المحاولة مجدداً." + "تعذّر إلغاء كتم هذه الغرفة، يرجى المحاولة مجدداً." + "لا تغلق التطبيق حتى الانتهاء." + "جارٍ تحضير الدعوات…" + "دعوة أشخاص" + "دعوة " + "مغادرة المحادثة" + "مغادرة الغرفة" + "الوسائط والملفات" + "مخصص" + "افتراضي" + "الإشعارات" + "الرسائل المثبّتة" + "الملف الشخصي" + "طلبات الانضمام" + "الأدوار والصلاحيات" + "الاسم" + "الأمان والخصوصية" + "الأمان" + "مشاركة الغرفة" + "معلومات الغرفة" + "الموضوع" + "يتم تحديث الغرفة…" + "لا يوجد مستخدمون محظورون." + + "%1$d محظورين" + "%1$d محظور" + "%1$d محظوران" + "%1$d محظورين" + "%1$d محظورًا" + "%1$d محظور" + + "تحقّق من الإملاء أو جرّب بحثًا جديدًا" + "لا توجد نتائج لـ “%1$s”" + + "%1$d شخص" + "%1$d شخص" + "%1$d شخصين" + "%1$d اشخاص" + "%1$d شخص" + "%1$d شخص" + + "حظر المستخدم" + "إزالة العضو فقط" + "إلغاء الحظر" + "يمكن لهذا المستخدم الانضمام إلى هذه الغرفة مرة أخرى عند دعوته." + "إلغاء حظر المستخدم" + "المحظورون" + "الأعضاء" + + "%1$d مدعوّين" + "%1$d مدعو" + "%1$d مدعوّان" + "%1$d مدعوّين" + "%1$d مدعوًا" + "%1$d مدعو" + + "قيد الانتظار" + "مسؤول" + "مشرف" + "مالك" + "أعضاء الغرفة" + "جارٍ رفع حظر %1$s" + "السماح بإعدادات مخصصة" + "سيؤدي تشغيل هذا إلى استبدال إعداداتك الافتراضية" + "أرسل لي إشعارات هذه المحادثة لـ" + "يمكنك تغيير ذلك في الـ %1$s الخاص بك." + "الإعدادات العامة" + "الإعدادات الافتراضية" + "إزالة الإعدادات الخاصة" + "حصل خطأ أثناء تحميل إعدادات الإشعارات." + "تعّذرت استعادة الوضع الافتراضي، يرجى المحاولة مجدداً." + "تعذّر تطبيق الوضع، يرجى المحاولة مجدداً." + "لا يدعم خادمك المنزلي هذا الخيار في الغرف المشفّرة، ولن تتلقى إشعارات في هذه الغرفة." + "كل الرسائل" + "في هذه الغرفة، أرسل لي إشعارات لـ" + "المسؤولون" + "المسؤولون والمالكون" + "تغيير دوري" + "خفض رتبتي إلى عضو" + "خفض رتبتي إلى مشرف" + "الإشراف على الأعضاء" + "الرسائل والمحتوى" + "المشرفون" + "المالكون" + "الصلاحيات" + "إعادة تعيين الصلاحيات" + "بمجرد إعادة تعيين الصلاحيات، ستفقد الإعدادات الحالية." + "إعادة تعيين الصلاحيات؟" + "الأدوار" + "تفاصيل الغرفة" + "الأدوار والصلاحيات" + "وضع علامة كمقروء" + "وضع علامة كغير مقروء" + "إضافة عنوان" + "يمكن لأي شخص في المساحات المصرّح بها الانضمام، لكن يجب على الآخرين طلب الوصول." + "يجب على الجميع طلب الوصول." + "طلب الانضمام" + "يمكن لأي شخص في %1$s الانضمام، لكن يجب على الآخرين طلب الوصول." + "نعم، فعّل التشفير" + "بعد تفعيله، لا يمكن تعطيل التشفير في الغرفة. لن يكون سجل الرسائل مرئيًا لأعضاء الغرفة إلا منذ وقت دعوتهم أو منذ انضمامهم إلى الغرفة. +لن يتمكن أي شخص غير أعضاء الغرفة من قراءة الرسائل. قد يمنع ذلك البوتات والجسور من العمل بشكل صحيح. +لا نوصي بتفعيل التشفير للغرف التي يمكن لأي شخص العثور عليها والانضمام إليها." + "تفعيل التشفير؟" + "بعد تفعيله، لا يمكن تعطيل التشفير." + "التشفير" + "تفعيل التشفير من طرف إلى طرف" + "يمكن لأي شخص الانضمام." + "أي شخص" + "اختر المساحات التي يمكن لأعضائها الانضمام إلى هذه الغرفة دون دعوة. %1$s" + "إدارة المساحات" + "يمكن للأشخاص المدعوين فقط الانضمام." + "بالدعوة فقط" + "الوصول" + "يمكن لأي شخص في المساحات المصرّح بها الانضمام." + "يمكن لأي شخص في %1$s الانضمام." + "أعضاء المساحة" + "المساحات غير مدعومة حاليًا" + "تحتاج إلى عنوان حتى تظهر الغرفة في الدليل العام." + "العنوان" + "السماح بالعثور على هذه الغرفة عند البحث في دليل الغرف العام على %1$s" + "السماح بالعثور عليها عند البحث في الدليل العام." + "مرئية في الدليل العام" + "أي شخص (السجل عام)" + "لن تؤثر التغييرات على الرسائل السابقة، بل على الرسائل الجديدة فقط. %1$s" + "من يمكنه قراءة السجل" + "الأعضاء منذ دعوتهم" + "الأعضاء (السجل الكامل)" + "عناوين الغرف وسيلة للعثور على الغرف والوصول إليها. كما يضمن ذلك إمكانية مشاركة غرفتك بسهولة مع الآخرين. +يمكنك اختيار نشر غرفتك في دليل الغرف العام على خادمك المنزلي." + "نشر الغرفة" + "العناوين وسيلة للعثور على الغرف والمساحات والوصول إليها. كما يضمن ذلك إمكانية مشاركتها بسهولة مع الآخرين." + "الظهور" + "الأمان والخصوصية" + diff --git a/features/roomdetails/impl/src/main/res/values-be/translations.xml b/features/roomdetails/impl/src/main/res/values-be/translations.xml index 2b0b59e359..130912651c 100644 --- a/features/roomdetails/impl/src/main/res/values-be/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-be/translations.xml @@ -88,7 +88,6 @@ "Не ўдалося наладзіць рэжым, паспрабуйце яшчэ раз." "Ваш хатні сервер не падтрымлівае гэту опцыю ў зашыфраваных пакоях, вы не атрымаеце апавяшчэнне ў гэтым пакоі." "Усе паведамленні" - "Толькі згадванні і ключавыя словы" "У гэтым пакоі паведаміце мяне пра" "Адміністратары" "Змяніць маю роль" diff --git a/features/roomdetails/impl/src/main/res/values-bg/translations.xml b/features/roomdetails/impl/src/main/res/values-bg/translations.xml index 7369d78f1d..a7594bc411 100644 --- a/features/roomdetails/impl/src/main/res/values-bg/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-bg/translations.xml @@ -68,7 +68,6 @@ "Неуспешно възстановяване на режима по подразбиране, моля, опитайте отново." "Неуспешно задаване на режима, моля, опитайте отново." "Всички съобщения" - "Само споменавания и ключови думи" "В тази стая, да бъда известяван за" "Администратори" "Промяна на моята роля" diff --git a/features/roomdetails/impl/src/main/res/values-ca/translations.xml b/features/roomdetails/impl/src/main/res/values-ca/translations.xml index 8b9f8d937e..b1a022a17d 100644 --- a/features/roomdetails/impl/src/main/res/values-ca/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ca/translations.xml @@ -91,8 +91,6 @@ "No s\'ha pogut restaurar el mode predeterminat. Torna-ho a provar." "No s\'ha pogut configurar el mode. Torna-ho a provar." "El servidor no admet aquesta opció en sales xifrades, no rebras notificacions en aquesta sala." - "Tots els missatges" - "Mencions y paraules clau (només)" "En aquesta sala, notifica\'m en" "Administradors" "Canvia el meu rol" diff --git a/features/roomdetails/impl/src/main/res/values-cy/translations.xml b/features/roomdetails/impl/src/main/res/values-cy/translations.xml index f5d4eecac5..68c53ec260 100644 --- a/features/roomdetails/impl/src/main/res/values-cy/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-cy/translations.xml @@ -103,7 +103,7 @@ "Wedi methu gosod y modd, ceisiwch eto." "Nid yw eich gweinydd cartref yn cefnogi\'r dewis hwn mewn ystafelloedd sydd wedi\'u hamgryptio, chewch chi ddim eich hysbysu yn yr ystafell hon." "Pob neges" - "Crybwylliadau ac Allweddeiriau\'n unig" + "Dim ond Crybwylliadau ac Allweddeiriau" "Yn yr ystafell hon, rhowch wybod i mi am" "Gweinyddwyr" "Gweinyddwyr a pherchnogion" diff --git a/features/roomdetails/impl/src/main/res/values-da/translations.xml b/features/roomdetails/impl/src/main/res/values-da/translations.xml index 191df9f059..e727221b74 100644 --- a/features/roomdetails/impl/src/main/res/values-da/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-da/translations.xml @@ -113,8 +113,6 @@ "Gendannelse af standardtilstanden mislykkedes. Prøv igen." "Kunne ikke indstille tilstanden. Prøv igen." "Din hjemmeserver understøtter ikke denne indstilling i krypterede rum, du får ikke besked i dette rum." - "Alle beskeder" - "Kun omtaler og nøgleord" "Giv mig besked i dette rum for" "Administratorer" "Administratorer og ejere" diff --git a/features/roomdetails/impl/src/main/res/values-el/translations.xml b/features/roomdetails/impl/src/main/res/values-el/translations.xml index 5f1a3ce4ab..6350d5de24 100644 --- a/features/roomdetails/impl/src/main/res/values-el/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-el/translations.xml @@ -114,7 +114,6 @@ "Αποτυχία ρύθμισης της λειτουργίας, δοκίμασε ξανά." "Ο αρχικός διακομιστής σας δεν υποστηρίζει αυτή την επιλογή σε κρυπτογραφημένες αίθουσες, δεν θα λάβετε ειδοποιήσεις σε αυτή την αίθουσα." "Όλα τα μηνύματα" - "Μόνο αναφορές και λέξεις-κλειδιά" "Σε αυτήν την αίθουσα, ειδοποιήστε με για" "Διαχειριστές" "Διαχειριστές και κάτοχοι" diff --git a/features/roomdetails/impl/src/main/res/values-es/translations.xml b/features/roomdetails/impl/src/main/res/values-es/translations.xml index 9537596d4e..8b9b5365c8 100644 --- a/features/roomdetails/impl/src/main/res/values-es/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-es/translations.xml @@ -1,5 +1,8 @@ + "Los miembros nuevos no ven el historial" + "Los miembros nuevos ven el historial" + "Cualquiera puede ver el historial" "Necesitarás una dirección de sala para que sea visible en el directorio." "Editar dirección" "Se ha producido un error al actualizar la configuración de notificaciones." @@ -21,14 +24,18 @@ "Enviar mensajes" "Editar administradores" "No podrás deshacer esta acción. Estás promocionando al usuario para que tenga el mismo nivel de poder que tú." - "¿Agregar Admin?" + "¿Agregar administrador?" + "Esta acción es irreversible. Estás transfiriendo la propiedad a los usuarios seleccionados. Una vez que salgas, será permanente." + "¿Transferir la propiedad?" "Degradar" "No podrás deshacer este cambio ya que te estás degradando. Si eres el último usuario privilegiado en la sala será imposible recuperar los privilegios." "¿Degradarte?" "%1$s (Pendiente)" "(Pendiente)" "Los administradores tienen privilegios de moderador de forma automática" + "Los propietarios tienen automáticamente privilegios de administrador." "Editar moderadores" + "Elegir propietarios" "Administradores" "Moderadores" "Miembros" @@ -46,11 +53,13 @@ "Se ha producido un error al cargar la configuración de las notificaciones." "No se ha podido silenciar esta sala, inténtalo de nuevo." "Error al dejar de silenciar esta sala, por favor inténtalo de nuevo." + "No cierres la aplicación hasta que se haya finalizado." + "Preparando invitaciones…" "Invitar personas" "Invitar" "Salir de la conversación" "Salir de la sala" - "Medios y archivos" + "Multimedia y archivos" "Personalizado" "Por defecto" "Notificaciones" @@ -58,16 +67,23 @@ "Perfil" "Solicitudes de unión" "Roles y permisos" + "Nombre" "Seguridad y privacidad" "Seguridad" "Compartir sala" "Información de la sala" "Tema" "Actualizando la sala…" - "No hay usuarios baneados." + "No hay usuarios vetados." + + "%1$d vetado" + "%1$d vetados" + + "Revisa la ortografía o intenta una nueva búsqueda." + "Ningún resultado para «%1$s»" - "%1$d Persona" - "%1$d Personas" + "%1$d persona" + "%1$d personas" "Sacar y vetar a un miembro" "Solo eliminar miembro" @@ -76,8 +92,14 @@ "Eliminar veto en la sala" "Vetados" "Miembros" + + "%1$d invitado" + "%1$d invitados" + + "Pendiente" "Administrador" "Moderador" + "Propietario" "Miembros de la sala" "Levantando veto a %1$s" "Permitir configuración personalizada" @@ -92,15 +114,17 @@ "No se pudo cambiar el modo, por favor inténtalo de nuevo." "Tu servidor base no admite esta opción en salas cifradas, no recibirás notificaciones de esta sala." "Todos los mensajes" - "Únicamente Menciones y Palabras clave" "En esta sala, notificarme por" "Administradores" + "Administradores y propietarios" "Cambiar mi rol" "Degradar a miembro" "Degradar a moderador" "Moderación de miembros" "Mensajes y contenido" "Moderadores" + "Propietarios" + "Permisos" "Restablecer permisos" "Una vez que restablezca los permisos, perderá la configuración actual." "¿Restablecer los permisos?" @@ -110,8 +134,10 @@ "Marcar como leído" "Marcar como no leído" "Agregar dirección" + "Cualquiera en espacios autorizados puede unirse, pero el resto debe solicitar acceso." "Todos deben solicitar acceso." "Solicitar unirse" + "Cualquiera en %1$s puede unirse, pero el resto debe solicitar acceso." "Sí, activar cifrado" "Una vez activado, el cifrado de una sala no se puede desactivar. El historial de mensajes solo será visible para los miembros de la sala desde que fueron invitados o desde que se unieron a la sala. Nadie más que los miembros de la sala podrán leer los mensajes. Esto puede impedir que los bots y los puentes funcionen correctamente. @@ -122,21 +148,29 @@ No recomendamos habilitar el cifrado para las salas que cualquiera pueda encontr "Activar el cifrado de extremo a extremo" "Cualquiera puede unirse" "Cualquiera" + "Elige qué miembros de los espacios pueden unirse a esta sala sin una invitación. %1$s" + "Gestionar espacios" "Solo las personas invitadas pueden unirse." "Solo por invitación" "Acceso" + "Cualquiera en espacios autorizados puede unirse." + "Cualquiera en %1$s puede unirse." + "Miembros del espacio" "No se admiten los espacios por el momento." "Necesitarás una dirección de sala para que sea visible en el directorio." "Dirección" "Permite encontrar esta sala buscando en el directorio de salas públicas de %1$s" + "Permitir que sea encontrada al buscar en el directorio público." "Visible en el directorio público" "Cualquiera (el historial es público)" + "Los cambios no afectarán a los mensajes anteriores, solo a los nuevos. %1$s" "Quién puede leer el historial" - "Solo participantes desde que fueron invitados" - "Miembros (historia completa)" + "Miembros desde su invitación" + "Miembros (historial completo)" "Las direcciones de sala son formas de buscar salas y acceder a ellas. Esto también garantiza que puedas compartir fácilmente tu sala con otras personas. Puedes optar por publicar tu sala en el directorio de salas públicas de tu servidor base." "Publicación de la sala" + "Las direcciones son una forma de encontrar y acceder a salas y espacios. También garantizan que puedas compartirlos fácilmente con otras personas." "Visibilidad" "Seguridad y privacidad" diff --git a/features/roomdetails/impl/src/main/res/values-et/translations.xml b/features/roomdetails/impl/src/main/res/values-et/translations.xml index e6962456ac..329ac267bf 100644 --- a/features/roomdetails/impl/src/main/res/values-et/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-et/translations.xml @@ -113,8 +113,7 @@ "Vaikimisi seadistuste taastamine ei õnnestunud. Palun proovi uuesti." "Seadistuste muutmine ei õnnestunud. Palun proovi uuesti." "Sinu koduserver ei toeta seda võimalust krüptitud jututubades, seega sa ei saa selle jututoa kohta teavitusi." - "Kõikide sõnumite korral" - "Mainimiste ja võtmesõnade alusel" + "Kõik sõnumid" "Selles jututoas teavita mind" "Peakasutajad" "Peakasutajad ja omanikud" diff --git a/features/roomdetails/impl/src/main/res/values-eu/translations.xml b/features/roomdetails/impl/src/main/res/values-eu/translations.xml index 64f32e44d9..e00aff7d4c 100644 --- a/features/roomdetails/impl/src/main/res/values-eu/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-eu/translations.xml @@ -83,7 +83,6 @@ "Errorea gertatu da jakinarazpen-ezarpenak kargatzean." "Zure zerbitzaria ez da bateragarria enkriptatutako gelen aukerarekin; litekeena da gela batzuetako jakinarazpenak ez jasotzea." "Mezu guztiak" - "Aipamenak eta hitz gakoak soilik" "Gela honetan, jakinarazi" "Administratzaileak" "Administratzaileak eta jabeak" diff --git a/features/roomdetails/impl/src/main/res/values-fa/translations.xml b/features/roomdetails/impl/src/main/res/values-fa/translations.xml index 7cdfe409dd..1ea8bc2e69 100644 --- a/features/roomdetails/impl/src/main/res/values-fa/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fa/translations.xml @@ -1,6 +1,10 @@ - "نشانی اتاق" + "اعضای جدید تاریخچه را نمی‌بینند" + "اعضای جدید تاریخچه را می‌بینند" + "هر کسی می‌تواند تاریخچه را ببیند" + "برای اینکه بتوانید آن را در فهرست عمومی مشاهده کنید، به یک آدرس نیاز دارید." + "ویرایش آدرس" "هنگام به‌روز کردن تنظیمات آگاهی خطایی رخ داد." "کارساز خانگیتان از این گزینه در اتاق‌های رمز شده پشتیبانی نمی‌کند. ممکن است در برخی اتاق‌ها آگاه نشوید." "نظرسنجی‌ها" @@ -21,6 +25,7 @@ "ویرایش مدیران" "قادر نخواهید بود این کنش را بازکردانید. داردید کاربر را به سطح قدرت خودتان ارتقا می‌دهید." "افزودن مدیر؟" + "شما قادر به لغو این اقدام نخواهید بود. شما در حال انتقال مالکیت به کاربران انتخاب شده هستید. پس از خروج، این انتقال دائمی خواهد بود." "انتقال مالکیت؟" "تنزل بده" "شما نمی‌توانید این تغییر را بازگردانید زیرا در حال تنزل نقش خود در اتاق هستید، اگر آخرین کاربر ممتاز در اتاق باشید، امکان دستیابی مجدد به دسترسی‌های سطح بالای اتاق غیرممکن است." @@ -62,6 +67,7 @@ "نمایه" "درخواست‌های پیوستن" "نقش‌ها و مجوزها" + "نام" "امنیت و محرمانگی" "امنیت" "هم‌رسانی اتاق" @@ -69,6 +75,12 @@ "موضوع" "به‌روز کردن اتاق…" "هیچ کاربری محروم نشده." + + "%1$d محروم شد" + "%1$d محروم شد" + + "املای کلمات را بررسی کنید یا جستجوی جدیدی را امتحان کنید" + "هیچ نتیجه‌ای برای «%1$s» یافت نشد." "%1$d نفر" "%1$d نفر" @@ -80,6 +92,11 @@ "تحریم نکردن از اتاق" "محروم" "اعضا" + + "%1$d دعوت شده" + "%1$d دعوت شده" + + "در حال بررسی" "ادمین" "ناظم" "مالک" @@ -108,6 +125,7 @@ "پیام‌ها و محتوا" "ناظم‌ها" "مالکان" + "دسترسی‌ها" "بازنشانی اجازه‌ها" "پس از بازنشانی اجازه‌ها تنظیمات فعلی را از دست خواهید داد." "بازنشانی اجازه‌ها؟" @@ -116,21 +134,44 @@ "نقش‌ها و مجوزها" "علامت‌گذاری به عنوان خوانده شده" "نشان به ناخوانده" - "افزودن نشانی اتاق" + "اضافه کردن آدرس" + "هر کسی که در فضاهای مجاز باشد می‌تواند عضو شود، اما بقیه باید درخواست دسترسی بدهند." + "همه باید درخواست دسترسی بدهند." "درخواست دعوت" + "هر کسی در %1$s می‌تواند عضو شود، اما بقیه باید درخواست دسترسی بدهند." "بله. به کار انداختن رمزنگاری" + "پس از فعال کردن، رمزگذاری برای یک اتاق قابل غیرفعال کردن نیست، تاریخچه پیام فقط برای اعضای اتاق از زمان دعوت یا از زمان پیوستن به اتاق قابل مشاهده خواهد بود. +هیچ کس غیر از اعضای اتاق قادر به خواندن پیام‌ها نخواهد بود. این ممکن است مانع از عملکرد صحیح ربات‌ها و پل‌ها شود. +ما فعال کردن رمزگذاری را برای اتاق‌هایی که هر کسی می‌تواند آنها را پیدا کند و به آنها بپیوندد، توصیه نمی‌کنیم." "رمزگذاری فعال شود؟" "پس از به کار افتادن، رمزنگاری قابل از کار انداختن نیست." "رمزنگاری" - "هرکسی می‌تواند یافته و بپیوندد" - "هرکسی" - "افراد فقط در صورت دعوت می‌توانند بپیوندند" - "فقط دعوتی" - "دسترسی اتاق" + "فعال کردن رمزنگاری سراسری" + "هر کسی می‌تواند عضو شود." + "هر کس" + "انتخاب کنید که اعضای کدام فضاها می‌توانند بدون دعوتنامه به این اتاق بپیوندند. %1$s" + "مدیریت فضاها" + "فقط افراد دعوت شده می‌توانند عضو شوند." + "فقط دعوت" + "دسترسی" + "هر کسی که در فضاهای مجاز باشد می‌تواند عضو شود." + "هر کسی که در %1$s باشد می‌تواند عضو شود." + "اعضای فضا" "در حال حاضر فضاها پشتیبانی نمی‌شوند" - "نشانی اتاق" + "برای اینکه بتوانید آن را در فهرست عمومی مشاهده کنید، به یک آدرس نیاز دارید." + "آدرس" + "با جستجو در فهرست اتاق‌های عمومی %1$s، امکان یافتن این اتاق را فراهم کنید." + "با جستجو در فهرست عمومی، می‌توان آن را پیدا کرد." + "قابل مشاهده در فهرست عمومی" "هرکسی" + "تغییرات روی پیام‌های قبلی تأثیری نخواهند داشت، فقط روی پیام‌های جدید اعمال می‌شوند. %1$s" + "چه کسی می‌تواند تاریخ بخواند؟" + "اعضا از زمان دعوت" + "اعضا (تاریخچه کامل)" + "آدرس‌های اتاق، راه‌هایی برای پیدا کردن و دسترسی به اتاق‌ها هستند. این همچنین تضمین می‌کند که می‌توانید به راحتی اتاق خود را با دیگران به اشتراک بگذارید. +شما می‌توانید اتاق خود را در دایرکتوری عمومی اتاق سرور خانگی خود منتشر کنید." "انتشار اتاق" - "نمایانی اتاق" + "آدرس‌ها راهی برای پیدا کردن و دسترسی به اتاق‌ها و فضاها هستند. این همچنین تضمین می‌کند که می‌توانید به راحتی آنها را با دیگران به اشتراک بگذارید." + "قابلیت مشاهده" "امنیت و محرمانگی" diff --git a/features/roomdetails/impl/src/main/res/values-fi/translations.xml b/features/roomdetails/impl/src/main/res/values-fi/translations.xml index 4eafca087e..b6586d8884 100644 --- a/features/roomdetails/impl/src/main/res/values-fi/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fi/translations.xml @@ -115,6 +115,7 @@ "Kotipalvelimesi ei tue tätä vaihtoehtoa salatuissa huoneissa, joten et saa ilmoituksia tästä huoneesta." "Kaikista viesteistä" "Vain maininnoista ja avainsanoista" + "Mykistä" "Ilmoita minulle tässä huoneessa" "Ylläpitäjät" "Ylläpitäjät ja omistajat" diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml index 0d6844cb7e..237767c82a 100644 --- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml @@ -114,8 +114,9 @@ "Échec de la configuration du mode, veuillez réessayer." "Votre serveur d’accueil ne supporte pas cette option pour les salons chiffrés, vous ne serez pas notifié(e) dans ce salon." "Tous les messages" - "Mentions et mots clés uniquement" - "Dans ce salon, prévenez-moi pour" + "Les messages avec mentions et mots clés uniquement" + "Aucun message" + "Pour ce salon, afficher une notification pour" "Administrateurs" "Administrateurs et propriétaires" "Changer mon rôle" diff --git a/features/roomdetails/impl/src/main/res/values-hr/translations.xml b/features/roomdetails/impl/src/main/res/values-hr/translations.xml index 5619164dd2..fffdc33e40 100644 --- a/features/roomdetails/impl/src/main/res/values-hr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-hr/translations.xml @@ -116,8 +116,6 @@ "Vraćanje zadanog načina rada nije uspjelo, pokušajte ponovno." "Postavljanje načina rada nije uspjelo, pokušajte ponovno." "Vaš matični poslužitelj ne podržava ovu mogućnost u šifriranim sobama; nećete dobiti obavijest u ovoj sobi." - "Sve poruke" - "Samo spominjanja i ključne riječi" "U ovoj sobi obavijesti me za" "Administratori" "Administratori i vlasnici" diff --git a/features/roomdetails/impl/src/main/res/values-it/translations.xml b/features/roomdetails/impl/src/main/res/values-it/translations.xml index b8a18d4421..c876567303 100644 --- a/features/roomdetails/impl/src/main/res/values-it/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-it/translations.xml @@ -114,7 +114,6 @@ "Impossibile impostare la modalità, riprova." "Il tuo homeserver non supporta questa opzione nelle stanze cifrate, quindi non riceverai notifiche in questa stanza." "Tutti i messaggi" - "Solo menzioni e parole chiave" "In questa stanza, avvisami per" "Amministratori" "Amministratori e proprietari" diff --git a/features/roomdetails/impl/src/main/res/values-ja/translations.xml b/features/roomdetails/impl/src/main/res/values-ja/translations.xml index bf0ef7424c..830ebd5452 100644 --- a/features/roomdetails/impl/src/main/res/values-ja/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ja/translations.xml @@ -110,8 +110,6 @@ "デフォルトの復元に失敗しました。再試行してください。" "設定に失敗しました。再試行してください。" "暗号化されたルームでは、この機能にホームサーバーが対応しないため、このルームからの通知を受信できません。" - "すべてのメッセージ" - "メンションとキーワードのみ" "このルームでは以下の通知を受け取る" "管理者" "管理者と所有者" diff --git a/features/roomdetails/impl/src/main/res/values-ka/translations.xml b/features/roomdetails/impl/src/main/res/values-ka/translations.xml index 8fd0b2c8a6..6bc01b34b9 100644 --- a/features/roomdetails/impl/src/main/res/values-ka/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ka/translations.xml @@ -79,7 +79,6 @@ "რეჟიმის დაყენება ვერ მოხერხდა, გთხოვთ, სცადოთ ხელახლა." "თქვენი სახლის სერვერი არ უჭერს მხარს ამ პარამეტრს დაშიფრულ ოთახებში, თქვენ არ მიიღებთ შეტყობინებას ამ ოთახში." "ყველა შეტყობინება" - "მხოლოდ ხსენებები და საკვანძო სიტყვები" "ამ ოთახში, შემატყობინეთ:" "ადმინისტრატორები" "ჩემი როლის შეცვლა" diff --git a/features/roomdetails/impl/src/main/res/values-ko/translations.xml b/features/roomdetails/impl/src/main/res/values-ko/translations.xml index 4a32cab45d..3d0a106e25 100644 --- a/features/roomdetails/impl/src/main/res/values-ko/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ko/translations.xml @@ -111,7 +111,6 @@ "모드 설정이 실패했습니다. 다시 시도해 주세요." "귀하의 홈 서버는 암호화된 방에서 이 옵션을 지원하지 않으므로, 이 방에서 알림을 받지 못합니다." "모든 메시지" - "언급 및 키워드만" "이 방에서, 알림을 주세요" "관리자" "관리자 및 소유자" diff --git a/features/roomdetails/impl/src/main/res/values-nb/translations.xml b/features/roomdetails/impl/src/main/res/values-nb/translations.xml index 0cb3dfc268..8507587f95 100644 --- a/features/roomdetails/impl/src/main/res/values-nb/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-nb/translations.xml @@ -114,7 +114,6 @@ "Innstilling av modus mislyktes, prøv igjen." "Hjemmeserveren din støtter ikke dette alternativet i krypterte rom, og du vil ikke bli varslet i dette rommet." "Alle meldinger" - "Bare omtaler og nøkkelord" "I dette rommet, varsle meg om" "Administratorer" "Administratorer og eiere" diff --git a/features/roomdetails/impl/src/main/res/values-nl/translations.xml b/features/roomdetails/impl/src/main/res/values-nl/translations.xml index 2ee5caca97..a25a674edc 100644 --- a/features/roomdetails/impl/src/main/res/values-nl/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-nl/translations.xml @@ -85,7 +85,6 @@ "Het instellen van de meldingen is mislukt. Probeer het opnieuw." "Je homeserver ondersteunt deze optie niet in versleutelde kamers; in deze kamer krijg je geen meldingen." "Alle berichten" - "Alleen vermeldingen en trefwoorden" "In deze kamer, stuur me een melding voor" "Beheerders" "Mijn rol wijzigen" diff --git a/features/roomdetails/impl/src/main/res/values-pl/translations.xml b/features/roomdetails/impl/src/main/res/values-pl/translations.xml index b79d3014e5..14225bcdf9 100644 --- a/features/roomdetails/impl/src/main/res/values-pl/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pl/translations.xml @@ -118,6 +118,7 @@ "Twój serwer domowy nie wspiera tej opcji w pokojach szyfrowanych, możesz nie otrzymać powiadomień z tego pokoju." "Wszystkie wiadomości" "Tylko wzmianki i słowa kluczowe" + "Wycisz" "W tym pokoju, powiadamiaj mnie przez" "Administratorzy" "Administratorzy i właściciele" diff --git a/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml b/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml index f2ddfb86cb..e741c43c90 100644 --- a/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml @@ -111,7 +111,6 @@ "Falha ao definir o modo, tente novamente." "Seu servidor-casa não suporta esta opção em salas criptografadas, você não será notificado nesta sala." "Todas as mensagens" - "Somente menções e palavras-chave" "Nesta sala, notifique-me de" "Administradores" "Administradores e proprietários" diff --git a/features/roomdetails/impl/src/main/res/values-pt/translations.xml b/features/roomdetails/impl/src/main/res/values-pt/translations.xml index 04ded84480..53eaebb934 100644 --- a/features/roomdetails/impl/src/main/res/values-pt/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pt/translations.xml @@ -109,8 +109,7 @@ "Falha ao restaurar o modo predefinido, tenta novamente." "Falha ao definir o modo, tenta novamente." "O teu servidor não suporta esta opção em salas cifradas, pelo que não serás notificado nesta sala." - "Qualquer mensagem" - "Menções ou palavras-chave" + "Todas as mensagens" "Nesta sala, notifica-me se" "Administradores" "Administradores e donos" diff --git a/features/roomdetails/impl/src/main/res/values-ro/translations.xml b/features/roomdetails/impl/src/main/res/values-ro/translations.xml index f3feda811a..ced134da3d 100644 --- a/features/roomdetails/impl/src/main/res/values-ro/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ro/translations.xml @@ -118,6 +118,7 @@ "Serverul dumneavoastră nu acceptă această opțiune în camerele criptate, nu veți primi notificări în această cameră." "Toate mesajele" "Numai mențiuni și cuvinte cheie" + "Mut" "În această cameră, anunțați-mă pentru" "Administratori" "Administratori și proprietari" diff --git a/features/roomdetails/impl/src/main/res/values-sv/translations.xml b/features/roomdetails/impl/src/main/res/values-sv/translations.xml index aa9f244498..764b18de76 100644 --- a/features/roomdetails/impl/src/main/res/values-sv/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sv/translations.xml @@ -99,7 +99,6 @@ "Misslyckades att ställa in läget, vänligen pröva igen." "Din hemserver stöder inte det här alternativet i krypterade rum, du blir inte aviserad i det här rummet." "Alla meddelanden" - "Endast omnämnanden och nyckelord" "I det här rummet, meddela mig för" "Administratörer" "Administratörer och ägare" diff --git a/features/roomdetails/impl/src/main/res/values-tr/translations.xml b/features/roomdetails/impl/src/main/res/values-tr/translations.xml index a9bfc6fbe2..9f78649509 100644 --- a/features/roomdetails/impl/src/main/res/values-tr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-tr/translations.xml @@ -97,7 +97,6 @@ "Ayarlanamadı, lütfen tekrar deneyin." "Ana sunucunuz şifreli odalarda bu seçeneği desteklemiyor, bu odada bildirim almayacaksınız." "Tüm mesajlar" - "Yalnızca Bahsetmeler ve Anahtar Kelimeler" "Bu odada, bana bildir" "Yöneticiler" "Rolümü değiştir" diff --git a/features/roomdetails/impl/src/main/res/values-uk/translations.xml b/features/roomdetails/impl/src/main/res/values-uk/translations.xml index 8f566bb235..edc1fab508 100644 --- a/features/roomdetails/impl/src/main/res/values-uk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-uk/translations.xml @@ -117,7 +117,8 @@ "Не вдалося встановити режим, спробуйте ще раз." "Ваш домашній сервер не підтримує цю опцію в зашифрованих кімнатах, ви не отримаєте сповіщення в цій кімнаті." "Всі повідомлення" - "Тільки згадки та ключові слова" + "Лише згадки та ключові слова" + "Вимкнути звук" "У цій кімнаті сповіщати мене про" "Адміністратори" "Адміністратори та власники" diff --git a/features/roomdetails/impl/src/main/res/values-ur/translations.xml b/features/roomdetails/impl/src/main/res/values-ur/translations.xml index 5c32811f36..89387da0de 100644 --- a/features/roomdetails/impl/src/main/res/values-ur/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ur/translations.xml @@ -83,8 +83,6 @@ "متعین وضع کو بحال کرنے میں ناکام، برائے مہربانی دوبارہ کوشش کریں۔" "وضع ترتیب دینے میں ناکام، برائے مہربانی دوبارہ کوشش کریں۔" "آپ کا منزلی خادم مرموز کردہ کمروں میں اس اختیار کی حمایت نہیں کرتا ہے، آپ کو اس کمرے میں مطلع کیا جائے گا۔" - "تمام پیغامات" - "صرف تذکرے اور کلیدی الفاظ" "اس کمرے میں، مجھے مطلع کریں برائے" "منتظمین" "میرا کردار تبدیل کریں" diff --git a/features/roomdetails/impl/src/main/res/values-uz/translations.xml b/features/roomdetails/impl/src/main/res/values-uz/translations.xml index d7778f2df4..10ffe9cf95 100644 --- a/features/roomdetails/impl/src/main/res/values-uz/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-uz/translations.xml @@ -114,7 +114,6 @@ "Rejimni o‘rnatib bo‘lmadi, qayta urinib ko‘ring." "Uy serveringiz shifrlangan xonalarda ushbu imkoniyatni qoʻllab-quvvatlamaydi, shuning uchun bu xonadan bildirishnomalar olmaysiz." "Barcha xabarlar" - "Faqat eslatmalar va kalit so\'zlar" "Bu xonada menga xabar bering" "Adminlar" "Adminlar va egalari" diff --git a/features/roomdetails/impl/src/main/res/values-vi/translations.xml b/features/roomdetails/impl/src/main/res/values-vi/translations.xml index ecc7e4e227..df42b37f77 100644 --- a/features/roomdetails/impl/src/main/res/values-vi/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-vi/translations.xml @@ -90,8 +90,6 @@ "Không thể khôi phục chế độ mặc định, vui lòng thử lại." "Không thể thiết lập chế độ, hãy thử lại nhé." "Máy chủ không hỗ trợ tùy chọn này trong phòng mã hóa, bạn sẽ không nhận thông báo ở đây." - "Tất cả tin nhắn." - "Chỉ đề cập và từ khóa" "Trong phòng này, thông báo cho tôi khi" "Quản trị viên" "Thay đổi vai trò của tôi" diff --git a/features/roomdetails/impl/src/main/res/values-zh/translations.xml b/features/roomdetails/impl/src/main/res/values-zh/translations.xml index 8a5c5734ed..a7592c38c7 100644 --- a/features/roomdetails/impl/src/main/res/values-zh/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-zh/translations.xml @@ -111,7 +111,8 @@ "设置模式失败,请重试。" "主服务器不支持在加密房间中的此选项,因此在此房间你可能无法收到通知。" "所有消息" - "仅限提及和关键词" + "仅提及与关键词" + "静默" "在此房间通知我以下类型" "管理员" "管理员与所有者" diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 31851c2603..8f86015147 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -114,7 +114,8 @@ "Failed setting the mode, please try again." "Your homeserver does not support this option in encrypted rooms, you won\'t get notified in this room." "All messages" - "Mentions and replies only" + "Mentions and Keywords only" + "Mute" "In this room, notify me for" "Admins" "Admins and owners" diff --git a/features/roomdetailsedit/impl/src/main/res/values-ar/translations.xml b/features/roomdetailsedit/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..5d58de994f --- /dev/null +++ b/features/roomdetailsedit/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,7 @@ + + + "تعديل التفاصيل" + "قد كان هناك خطأ والمعلومات لم تتحدث." + "غير قادر على تعديل الغرفة" + "يتم تحديث الغرفة…" + diff --git a/features/roomdirectory/impl/src/main/res/values-ar/translations.xml b/features/roomdirectory/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..ff40f27f27 --- /dev/null +++ b/features/roomdirectory/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,5 @@ + + + "فشل التحميل" + "دليل الغرف" + diff --git a/features/roommembermoderation/impl/src/main/res/values-ar/translations.xml b/features/roommembermoderation/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..3a425d4d7d --- /dev/null +++ b/features/roommembermoderation/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,22 @@ + + + "حظر المستخدم" + "حظر" + "لن يتمكن هذا العضو من الانضمام مرة أخرى حتى إذا تمت دعوته." + "هل أنت متأكد من أنك تريد حظر هذا العضو؟" + "لن يتمكن هذا المستخدم من الانضمام إلى هذه المساحة مرة أخرى إذا تمت دعوته، لكنه سيظل محتفظًا بعضويته في أي غرف أو مساحات فرعية." + "جارٍ حظر %1$s" + "إزالة" + "يمكن لهذا المستخدم الانضمام إلى هذه الغرفة مرة أخرى عند دعوته." + "هل أنت متأكد من رغبتك في إزالة هذا العضو؟" + "سيتمكن هذا المستخدم من الانضمام إلى هذه المساحة مرة أخرى إذا تمت دعوته، وسيظل محتفظًا بعضويته في أي غرف أو مساحات فرعية." + "عرض الملف الشخصي" + "إزالة المستخدم" + "إزالة العضو وحظره من الانضمام مستقبلًا؟" + "جارٍ إزالة %1$s…" + "إلغاء حظر المستخدم" + "إلغاء الحظر" + "سيتمكن هذا المستخدم من الانضمام مرة أخرى إذا تمت دعوته" + "هل أنت متأكد من رغبتك في إلغاء حظر هذا العضو؟" + "جارٍ رفع حظر %1$s" + diff --git a/features/roommembermoderation/impl/src/main/res/values-es/translations.xml b/features/roommembermoderation/impl/src/main/res/values-es/translations.xml index 7f83f7546e..7f4a1e96cf 100644 --- a/features/roommembermoderation/impl/src/main/res/values-es/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-es/translations.xml @@ -4,10 +4,12 @@ "Vetar" "No podrán volver a unirse a esta sala si son invitados." "¿Estás seguro de que quieres vetar a este miembro?" + "No podrá volver a unirse a este espacio aunque le inviten, pero seguirá siendo miembro de cualquier sala o subespacio donde ya lo fuese." "Vetando a %1$s" "Echar" "Podrá volver a unirse a esta sala si se le invita." "¿Seguro que quieres echar a este miembro?" + "Podrá volver a unirse a este espacio si se le invita, y seguirá siendo miembro de cualquier sala o subespacio donde ya lo fuese." "Ver perfil" "Sacar de la sala" "¿Sacar al miembro y prohibirle unirse en el futuro?" diff --git a/features/roommembermoderation/impl/src/main/res/values-fa/translations.xml b/features/roommembermoderation/impl/src/main/res/values-fa/translations.xml index 6ddb482189..d647dd1915 100644 --- a/features/roommembermoderation/impl/src/main/res/values-fa/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-fa/translations.xml @@ -4,15 +4,19 @@ "تحریم" "در صورت دعوت نمی‌تواند دوباره به اتاق بپیوندد." "مطمئنید می‌خواهید این عضو را تحریم کنید؟" + "در صورت دعوت، آنها دیگر نمی‌توانند به این فضا بپیوندند، اما همچنان عضویت خود در هر اتاق یا زیرفضا را حفظ خواهند کرد." "تحریم کردن %1$s" "برداشتن" "در صورت دعوت می‌تواند دوباره به اتاق بپیوندد." "مطمئنید می‌خواهید این عضو را بردارید؟" + "در صورت دعوت، آنها می‌توانند دوباره به این فضا بپیوندند و همچنان عضویت خود را در هر اتاق یا زیرفضا حفظ خواهند کرد." "دیدن نمایه" "حذف کاربر" "برداشتن عضو و تحریم پیوستن در آینده؟" "برداشتن %1$s…" "تحریم نکردن از اتاق" "رفع انسداد" + "در صورت دعوت، آنها می‌توانند دوباره بپیوندند" + "آیا مطمئن هستید که می‌خواهید این عضو را از حالت مسدود خارج کنید؟" "رفع تحریم %1$s" diff --git a/features/securebackup/impl/src/main/res/values-ar/translations.xml b/features/securebackup/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..7b3295f49a --- /dev/null +++ b/features/securebackup/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,95 @@ + + + "حذف تخزين المفاتيح" + "تفعيل النسخ الاحتياطي" + "سيتيح لك هذا الاطّلاع على سجل محادثاتك على أي أجهزة جديدة، وهو مطلوب للنسخ الاحتياطي للمحادثات والهوية الرقمية. %1$s." + "تخزين المفاتيح" + "يجب تشغيل تخزين المفاتيح لنسخ دردشاتك احتياطيًا." + "رفع المفاتيح من هذا الجهاز" + "السماح بتخزين المفاتيح" + "تغيير مفتاح الاسترداد" + "يتم نسخ دردشاتك احتياطيًا تلقائيًا بتشفير من طرف إلى طرف. لاستعادة هذه النسخة الاحتياطية والاحتفاظ بهويتك الرقمية إذا فقدت الوصول إلى جميع أجهزتك، ستحتاج إلى مفتاح الاسترداد." + "أدخل مفتاح الاسترداد" + "تخزين المفاتيح لديك غير متزامن حاليًا." + "الحصول على مفتاح الاسترداد" + "يتم نسخ محادثاتك احتياطيًا تلقائيًا باستخدام التشفير التام بين الطرفين. لاستعادة هذه النسخة الاحتياطية والاحتفاظ بهويتك الرقمية عند فقدان الوصول إلى جميع أجهزتك، ستحتاج إلى مفتاح الاسترداد." + "افتح %1$s على جهاز سطح مكتب" + "سجّل الدخول إلى حسابك مرة أخرى" + "عندما يُطلب منك التحقق من جهازك، اختر %1$s" + "“إعادة تعيين الكل”" + "اتّبع التعليمات لإنشاء مفتاح استرداد جديد" + "احفظ مفتاح الاسترداد الجديد في مدير كلمات مرور أو ملاحظة مشفّرة" + "إعادة تعيين تشفير حسابك باستخدام جهاز آخر" + "مفتاح الاسترداد الذي أدخلته غير مطابق" + "إنهاء الإعداد" + "أدخل مفتاح الاسترداد مرة أخرى." + "أكّد مفتاح الاسترداد" + + "الحد الأدنى %1$d أحرف. لا تستخدم كلمة مرور حسابك" + "الحد الأدنى %1$d حرف. لا تستخدم كلمة مرور حسابك" + "الحد الأدنى %1$d حرفان. لا تستخدم كلمة مرور حسابك" + "الحد الأدنى %1$d أحرف. لا تستخدم كلمة مرور حسابك" + "الحد الأدنى %1$d حرفًا. لا تستخدم كلمة مرور حسابك" + "الحد الأدنى %1$d حرف. لا تستخدم كلمة مرور حسابك" + + "القوة" + "قوة عبارة المرور: %1$s" + "متوسط" + "قوي" + "قوي جدًا" + "ضعيف جدًا" + "ضعيف" + "اختر مفتاح استرداد يمكنك تذكّره." + "أدخل مفتاح استرداد" + "جارٍ تحميل متطلبات مفتاح الاسترداد" + "لتغيير مفتاح الاسترداد، انتقل إلى الإعدادات > التشفير > النسخ الاحتياطي" + "يمكنك استخدام مفتاح الاسترداد لتأكيد الأجهزة الجديدة أو استعادة محادثاتك المشفّرة إذا فقدت الوصول إلى جميع أجهزتك." + "تم إعداد نسختك الاحتياطية بالكامل الآن" + "متابعة إعادة التعيين" + "سيتم الاحتفاظ بتفاصيل حسابك وجهات اتصالك وتفضيلاتك وقائمة الدردشات" + "ستفقد أي سجل رسائل مخزّن على الخادم فقط" + "ستحتاج إلى التحقق من جميع أجهزتك وجهات اتصالك الحالية مرة أخرى" + "لا تُعد تعيين هويتك الرقمية إلا إذا لم يكن لديك وصول إلى جهاز آخر تم التحقق منه ولا تملك مفتاح الاسترداد." + "لا يمكنك التأكيد؟ ستحتاج إلى إعادة تعيين هويتك الرقمية." + "حذف" + "ستفقد سجل محادثاتك المشفّر وستحتاج إلى إعادة ضبط هويتك الرقمية إذا أزلت جميع أجهزتك." + "هل أنت متأكد من رغبتك في حذف تخزين المفاتيح؟" + "سيؤدي حذف تخزين المفاتيح إلى إزالة هويتك الرقمية ومفاتيح الرسائل من الخادم وإيقاف ميزات الأمان التالية:" + "لن يتوفر لك سجل الرسائل المشفّرة على الأجهزة الجديدة" + "ستفقد الوصول إلى رسائلك المشفّرة إذا تم تسجيل خروجك من %1$s في كل مكان" + "هل أنت متأكد من رغبتك في إيقاف تخزين المفاتيح وحذفه؟" + "احصل على مفتاح استرداد جديد إذا فقدت مفتاحك الحالي. بعد تغيير مفتاح الاسترداد، لن يعمل مفتاحك القديم بعد ذلك." + "إنشاء مفتاح استرداد جديد" + "لا تشارك هذا مع أي شخص!" + "تم تغيير مفتاح الاسترداد" + "تغيير مفتاح الاسترداد؟" + "إنشاء مفتاح استرداد جديد" + "تأكد من ألا يتمكن أحد من رؤية هذه الشاشة!" + "يُرجى المحاولة مرة أخرى لتأكيد الوصول إلى تخزين المفاتيح." + "مفتاح الاسترداد غير صحيح" + "إذا كان لديك مفتاح أمان أو عبارة أمان، فسيعمل ذلك أيضًا." + "أدخل…" + "هل فقدت مفتاح الاسترداد؟" + "تم تأكيد مفتاح الاسترداد" + "أدخل مفتاح الاسترداد" + "تم نسخ مفتاح الاسترداد" + "جارٍ الإنشاء…" + "حفظ مفتاح الاسترداد" + "دوّن مفتاح الاسترداد هذا في مكان آمن، مثل مدير كلمات مرور أو ملاحظة مشفّرة أو خزنة مادية." + "اضغط لنسخ مفتاح الاسترداد" + "احفظ مفتاح الاسترداد الخاص بك في مكان آمن" + "لن تتمكن من الوصول إلى مفتاح الاسترداد الجديد بعد هذه الخطوة." + "هل حفظت مفتاح الاسترداد الخاص بك؟" + "تخزين المفاتيح لديك محمي بمفتاح استرداد. إذا احتجت إلى مفتاح استرداد جديد بعد الإعداد، يمكنك إعادة إنشائه بتحديد «تغيير مفتاح الاسترداد»." + "إنشاء مفتاح الاسترداد الخاص بك" + "لا تشارك هذا مع أي شخص!" + "تم إعداد الاسترداد بنجاح" + "الحصول على مفتاح الاسترداد" + "نعم، أعد التعيين الآن" + "لا يمكن التراجع عن هذه العملية." + "هل أنت متأكد من أنك تريد إعادة تعيين هويتك الرقمية؟" + "حدث خطأ غير معروف. يُرجى التحقق من صحة كلمة مرور حسابك ثم المحاولة مرة أخرى." + "أدخل…" + "أكّد أنك تريد إعادة تعيين هويتك الرقمية." + "أدخل كلمة مرور حسابك للمتابعة" + diff --git a/features/securebackup/impl/src/main/res/values-es/translations.xml b/features/securebackup/impl/src/main/res/values-es/translations.xml index 0904559a8f..a99c4bf496 100644 --- a/features/securebackup/impl/src/main/res/values-es/translations.xml +++ b/features/securebackup/impl/src/main/res/values-es/translations.xml @@ -2,16 +2,17 @@ "Borrar almacén de claves" "Activar copia de seguridad" - "Almacena tu identidad criptográfica y las claves de tus mensajes de forma segura en el servidor. Esto te permitirá ver tu historial de mensajes en cualquier dispositivo nuevo. %1$s." + "Esto te permitirá ver tu historial de chat en cualquier dispositivo nuevo y es necesario para la copia de seguridad de tus chats y de tu identidad digital. %1$s." "Almacenamiento de claves" - "El almacenamiento de claves debe estar activado para configurar la recuperación." + "El almacenamiento de claves debe estar activado para hacer copias de seguridad de tus chats." "Subir claves desde este dispositivo" "Permitir almacenamiento de claves" "Cambiar la clave de recuperación" - "Recupera tu identidad criptográfica y tu historial de mensajes con una clave de recuperación si has perdido todos tus dispositivos actuales." + "Tus chats se respaldan automáticamente con cifrado de extremo a extremo. Para restaurar esta copia de seguridad y conservar tu identidad digital si pierdes el acceso a todos tus dispositivos, necesitarás tu clave de recuperación." "Introduce la clave de recuperación" "Tu almacén de claves no está sincronizado actualmente." - "Configurar la recuperación" + "Obtener clave de recuperación" + "Tus chats se respaldan automáticamente con cifrado de extremo a extremo. Para restaurar esta copia de seguridad y conservar tu identidad digital si pierdes el acceso a todos tus dispositivos, necesitarás tu clave de recuperación." "Abre %1$s en un dispositivo de escritorio" "Vuelve a iniciar sesión en tu cuenta" "Cuando se te pida que verifiques tu dispositivo, selecciona %1$s" @@ -19,16 +20,37 @@ "Sigue las instrucciones para crear una nueva clave de recuperación" "Guarda tu nueva clave de recuperación en un administrador de contraseñas o en una nota cifrada" "Restablece el cifrado de tu cuenta usando otro dispositivo" + "La clave de recuperación que introdujiste no coincide" + "Finalizar la configuración" + "Introduce de nuevo tu clave de recuperación." + "Confirma tu clave de recuperación" + + "Mínimo %1$d carácter. No uses la contraseña de tu cuenta" + "Mínimo %1$d caracteres. No uses la contraseña de tu cuenta" + + "Fortaleza" + "Fortaleza de la frase de contraseña: %1$s" + "Moderada" + "Fuerte" + "Muy fuerte" + "Muy débil" + "Débil" + "Elige una clave de recuperación que puedas memorizar." + "Introduce una clave de recuperación" + "Cargando requisitos de la clave de recuperación" + "Para cambiar tu clave de recuperación, ve a Ajustes → Cifrado → Copia de seguridad" + "Puedes usar tu clave de recuperación para confirmar dispositivos nuevos o restaurar tus chats cifrados si pierdes el acceso a todos tus dispositivos." + "Tu copia de seguridad está ahora totalmente configurada" "Continuar con el restablecimiento" "Se conservarán los detalles de tu cuenta, tus contactos, tus preferencias y tu lista de chats" "Perderás cualquier historial de mensajes que solo esté almacenado en el servidor" "Tendrás que verificar de nuevo todos tus dispositivos y contactos existentes" - "Restablece tu identidad solo si no tienes acceso a otro dispositivo en el que hayas iniciado sesión y has perdido tu clave de recuperación." - "¿No puedes confirmar? Tendrás que restablecer tu identidad." - "Desactivar" - "Perderás tus mensajes cifrados si cierras sesión en todos los dispositivos." - "¿Estás seguro de que quieres desactivar la copia de seguridad?" - "Al borrar el almacén de claves, se eliminarán del servidor tu identidad criptográfica y claves de los mensajes, y se desactivarán las siguientes funciones de seguridad:" + "Restablece tu identidad digital solo si ya no tienes acceso a ningún otro dispositivo verificado y no tienes tu clave de recuperación." + "¿No puedes confirmar? Tendrás que restablecer tu identidad digital." + "Eliminar" + "Perderás tu historial de chats cifrados y tendrás que restablecer tu identidad digital si eliminas todos tus dispositivos." + "¿Estás seguro de que quieres eliminar el almacén de claves?" + "Al borrar el almacén de claves, se eliminarán del servidor tu identidad digital y claves de los mensajes, y se desactivarán las siguientes funciones de seguridad:" "No tendrás un historial de mensajes cifrados en nuevos dispositivos" "Perderás el acceso a tus mensajes cifrados si cierras sesión en %1$s en todas partes" "¿Estás seguro de que quieres desactivar el almacenamiento de claves y borrarlo?" @@ -58,12 +80,12 @@ "Generar tu clave de recuperación" "¡No la compartas con nadie!" "Configuración de recuperación terminada" - "Configurar la recuperación" + "Obtener clave de recuperación" "Sí, restablecer ahora" "Este proceso es irreversible." - "¿Estás seguro de que quieres restablecer tu identidad?" + "¿Estás seguro de que quieres restablecer tu identidad digital?" "Se ha producido un error desconocido. Comprueba que la contraseña de tu cuenta sea correcta y vuelve a intentarlo." "Introducir…" - "Confirma que quieres restablecer tu identidad." + "Confirma que quieres restablecer tu identidad digital." "Introduce la contraseña de tu cuenta para continuar" diff --git a/features/securebackup/impl/src/main/res/values-fa/translations.xml b/features/securebackup/impl/src/main/res/values-fa/translations.xml index bd7a43ffba..9457bcb2af 100644 --- a/features/securebackup/impl/src/main/res/values-fa/translations.xml +++ b/features/securebackup/impl/src/main/res/values-fa/translations.xml @@ -4,12 +4,15 @@ "روشن کردن پشتیبان" "ذخیرهٔ کلیدهای پیام‌ها و هویت رمزنگاریتان به صورت امن روی کارساز. این کار می‌گذارد تاریخچهٔ پیام‌هایتان را روی هر افزارهٔ جدیدی ببینید. %1$s." "ذخیره‌ساز کلید" + "برای پشتیبان‌گیری از چت‌هایتان، باید گزینه‌ی ذخیره‌سازی کلید (Key Storage) فعال باشد." "بارگذاری کلیدها از ین افزاره" "اجازهٔ ذخیرهٔ کلید" "تغییر کلید بازیابی" + "چت‌های شما به طور خودکار با رمزگذاری سراسری پشتیبان‌گیری می‌شوند. برای بازیابی این پشتیبان‌گیری و حفظ هویت دیجیتال خود در زمانی که دسترسی به تمام دستگاه‌هایتان را از دست می‌دهید، به کلید بازیابی خود نیاز خواهید داشت." "ورود کلید بازیابی" "ذخیره‌ساز کلیدتان از هم‌گام بودن در آمده." "دریافت کلید بازیابی" + "چت‌های شما به طور خودکار با رمزگذاری سراسری پشتیبان‌گیری می‌شوند. برای بازیابی این پشتیبان‌گیری و حفظ هویت دیجیتال خود در زمانی که دسترسی به تمام دستگاه‌هایتان را از دست می‌دهید، به کلید بازیابی خود نیاز خواهید داشت." "گشودن %1$s در افزارهٔ میزکار" "ورود دوباره به حسابتان" "گزینش %1$s هنگام درخواست تأیید افزاره‌تان" @@ -17,12 +20,33 @@ "پیروی از دستورالعمل‌ها برای ایجاد کلید بازیابی جدید" "ذخیرهٔ کلید بازیابی جدیدتان در مدیر گذرواژه یا یادداشت رمز شده" "بازنشانی رمزنگاری برای حسابتان با استفاده از افزاره‌ای دیگر" + "کلید بازیابی که وارد کرده‌اید مطابقت ندارد" + "پایان تنظیمات" + "دوباره کلید بازیابی خود را وارد کنید." + "کلید بازیابی خود را تأیید کنید" + + "حداقل کاراکتر %1$d. از رمز عبور حساب کاربری خود استفاده نکنید." + "حداقل کاراکترهای %1$d. از رمز عبور حساب کاربری خود استفاده نکنید." + + "قدرت" + "قدرت رمز عبور: %1$s" + "متوسط" + "قوی" + "خیلی قوی" + "خیلی ضعیف" + "ضعیف" + "یک کلید بازیابی انتخاب کنید که بتوانید آن را به خاطر بسپارید." + "یک کلید بازیابی وارد کنید" + "بارگذاری الزامات کلید بازیابی" + "برای تغییر کلید بازیابی خود، به تنظیمات → رمزگذاری → پشتیبان‌گیری بروید." + "اگر دسترسی به همه دستگاه‌هایتان را از دست بدهید، می‌توانید از کلید بازیابی خود برای تأیید دستگاه‌های جدید یا بازیابی چت‌های رمزگذاری‌شده‌تان استفاده کنید." + "پشتیبان‌گیری شما اکنون کاملاً راه‌اندازی شده است" "ادامهٔ بازنشانی" "جزییات حساب، آشنایان، ترجیحات و سیاههٔ گپ‌هایتان حفظ خواهند شد" "تاریخچهٔ گپ‌هایتان را از دست خواهید داد" "لازم است دوباره همهٔ آشنایان و افزاره‌های موجودتان را تأیید کنید" - "فقط اگر به افزاره‌ای وارد شده از پیش دسترسی ندارید و کلید بازیابیتان را گم کرده‌اید بازنشانی کنید." - "نمی‌توانید تأیید کنید؟ لازم است هویتتان را بازنشانی کنید." + "فقط در صورتی که به دستگاه تأیید شده دیگری دسترسی ندارید و کلید بازیابی خود را ندارید، هویت دیجیتال خود را بازنشانی کنید." + "نمی‌توانید تأیید کنید؟ باید هویت دیجیتال خود را مجدداً تنظیم کنید." "حذف" "اگر از تمام دستگاه‌هایتان خارج شوید، تاریخچه چت رمزگذاری‌شده خود را از دست خواهید داد و باید هویت دیجیتال خود را مجدداً تنظیم کنید." "آیا مطمئن هستید که می‌خواهید کلید ذخیره‌سازی را حذف کنید؟" @@ -59,5 +83,9 @@ "دریافت کلید بازیابی" "بله. اکنون بازنشانی شود" "این فرایند بازگشت‌ناپذیر است." + "آیا مطمئن هستید که می‌خواهید هویت دیجیتال خود را بازنشانی کنید؟" + "خطای ناشناخته‌ای رخ داده است. لطفاً بررسی کنید که رمز عبور حساب شما صحیح است و دوباره امتحان کنید." "ورود…" + "تأیید کنید که می‌خواهید هویت دیجیتال خود را مجدداً تنظیم کنید." + "برای ادامه، رمز عبور حساب خود را وارد کنید" diff --git a/features/securityandprivacy/impl/src/main/res/values-ar/translations.xml b/features/securityandprivacy/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..d6006ec8f3 --- /dev/null +++ b/features/securityandprivacy/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,50 @@ + + + "تحتاج إلى عنوان حتى تظهر الغرفة في الدليل العام." + "تعديل العنوان" + "المساحات التي يمكن لأعضائها الانضمام إلى الغرفة دون دعوة." + "إدارة المساحات" + "(مساحة غير معروفة)" + "مساحات أخرى لست عضوًا فيها" + "مساحاتك" + "إضافة عنوان" + "يمكن لأي شخص في المساحات المصرّح بها الانضمام، لكن يجب على الآخرين طلب الوصول." + "يجب على الجميع طلب الوصول." + "طلب الانضمام" + "يمكن لأي شخص في %1$s الانضمام، لكن يجب على الآخرين طلب الوصول." + "نعم، فعّل التشفير" + "بعد تفعيله، لا يمكن تعطيل التشفير في الغرفة. لن يكون سجل الرسائل مرئيًا لأعضاء الغرفة إلا منذ وقت دعوتهم أو منذ انضمامهم إلى الغرفة. +لن يتمكن أي شخص غير أعضاء الغرفة من قراءة الرسائل. قد يمنع ذلك البوتات والجسور من العمل بشكل صحيح. +لا نوصي بتفعيل التشفير للغرف التي يمكن لأي شخص العثور عليها والانضمام إليها." + "تفعيل التشفير؟" + "بعد تفعيله، لا يمكن تعطيل التشفير." + "التشفير" + "تفعيل التشفير من طرف إلى طرف" + "يمكن لأي شخص الانضمام." + "أي شخص" + "اختر المساحات التي يمكن لأعضائها الانضمام إلى هذه الغرفة دون دعوة. %1$s" + "إدارة المساحات" + "يمكن للأشخاص المدعوين فقط الانضمام." + "بالدعوة فقط" + "الوصول" + "يمكن لأي شخص في المساحات المصرّح بها الانضمام." + "يمكن لأي شخص في %1$s الانضمام." + "أعضاء المساحة" + "المساحات غير مدعومة حاليًا" + "تحتاج إلى عنوان حتى تظهر الغرفة في الدليل العام." + "العنوان" + "السماح بالعثور على هذه الغرفة عند البحث في دليل الغرف العام على %1$s" + "السماح بالعثور عليها عند البحث في الدليل العام." + "مرئية في الدليل العام" + "أي شخص (السجل عام)" + "لن تؤثر التغييرات على الرسائل السابقة، بل على الرسائل الجديدة فقط. %1$s" + "من يمكنه قراءة السجل" + "الأعضاء منذ دعوتهم" + "الأعضاء (السجل الكامل)" + "عناوين الغرف وسيلة للعثور على الغرف والوصول إليها. كما يضمن ذلك إمكانية مشاركة غرفتك بسهولة مع الآخرين. +يمكنك اختيار نشر غرفتك في دليل الغرف العام على خادمك المنزلي." + "نشر الغرفة" + "العناوين وسيلة للعثور على الغرف والمساحات والوصول إليها. كما يضمن ذلك إمكانية مشاركتها بسهولة مع الآخرين." + "الظهور" + "الأمان والخصوصية" + diff --git a/features/securityandprivacy/impl/src/main/res/values-es/translations.xml b/features/securityandprivacy/impl/src/main/res/values-es/translations.xml index f7e7812914..49547a939e 100644 --- a/features/securityandprivacy/impl/src/main/res/values-es/translations.xml +++ b/features/securityandprivacy/impl/src/main/res/values-es/translations.xml @@ -2,9 +2,16 @@ "Necesitarás una dirección de sala para que sea visible en el directorio." "Editar dirección" + "Espacios donde los miembros pueden unirse a la sala sin invitación." + "Gestionar espacios" + "(Espacio desconocido)" + "Otros espacios de los que no eres miembro" + "Tus espacios" "Agregar dirección" + "Cualquiera en espacios autorizados puede unirse, pero el resto debe solicitar acceso." "Todos deben solicitar acceso." "Solicitar unirse" + "Cualquiera en %1$s puede unirse, pero el resto debe solicitar acceso." "Sí, activar cifrado" "Una vez activado, el cifrado de una sala no se puede desactivar. El historial de mensajes solo será visible para los miembros de la sala desde que fueron invitados o desde que se unieron a la sala. Nadie más que los miembros de la sala podrán leer los mensajes. Esto puede impedir que los bots y los puentes funcionen correctamente. @@ -15,21 +22,29 @@ No recomendamos habilitar el cifrado para las salas que cualquiera pueda encontr "Activar el cifrado de extremo a extremo" "Cualquiera puede unirse" "Cualquiera" + "Elige qué miembros de los espacios pueden unirse a esta sala sin una invitación. %1$s" + "Gestionar espacios" "Solo las personas invitadas pueden unirse." "Solo por invitación" "Acceso" + "Cualquiera en espacios autorizados puede unirse." + "Cualquiera en %1$s puede unirse." + "Miembros del espacio" "No se admiten los espacios por el momento." "Necesitarás una dirección de sala para que sea visible en el directorio." "Dirección" "Permite encontrar esta sala buscando en el directorio de salas públicas de %1$s" + "Permitir que sea encontrada al buscar en el directorio público." "Visible en el directorio público" "Cualquiera (el historial es público)" + "Los cambios no afectarán a los mensajes anteriores, solo a los nuevos. %1$s" "Quién puede leer el historial" - "Solo participantes desde que fueron invitados" - "Miembros (historia completa)" + "Miembros desde su invitación" + "Miembros (historial completo)" "Las direcciones de sala son formas de buscar salas y acceder a ellas. Esto también garantiza que puedas compartir fácilmente tu sala con otras personas. Puedes optar por publicar tu sala en el directorio de salas públicas de tu servidor base." "Publicación de la sala" + "Las direcciones son una forma de encontrar y acceder a salas y espacios. También garantizan que puedas compartirlos fácilmente con otras personas." "Visibilidad" "Seguridad y privacidad" diff --git a/features/securityandprivacy/impl/src/main/res/values-fa/translations.xml b/features/securityandprivacy/impl/src/main/res/values-fa/translations.xml index 6fc141b487..d4a92973cb 100644 --- a/features/securityandprivacy/impl/src/main/res/values-fa/translations.xml +++ b/features/securityandprivacy/impl/src/main/res/values-fa/translations.xml @@ -1,21 +1,50 @@ - "نشانی اتاق" - "افزودن نشانی اتاق" + "برای اینکه بتوانید آن را در فهرست عمومی مشاهده کنید، به یک آدرس نیاز دارید." + "ویرایش آدرس" + "فضاهایی که اعضا می‌توانند بدون دعوت‌نامه به اتاق بپیوندند." + "مدیریت فضاها" + "(فضای ناشناخته)" + "فضاهای دیگری که عضو آنها نیستید" + "فضاهای شما" + "اضافه کردن آدرس" + "هر کسی که در فضاهای مجاز باشد می‌تواند عضو شود، اما بقیه باید درخواست دسترسی بدهند." + "همه باید درخواست دسترسی بدهند." "درخواست دعوت" + "هر کسی در %1$s می‌تواند عضو شود، اما بقیه باید درخواست دسترسی بدهند." "بله. به کار انداختن رمزنگاری" + "پس از فعال کردن، رمزگذاری برای یک اتاق قابل غیرفعال کردن نیست، تاریخچه پیام فقط برای اعضای اتاق از زمان دعوت یا از زمان پیوستن به اتاق قابل مشاهده خواهد بود. +هیچ کس غیر از اعضای اتاق قادر به خواندن پیام‌ها نخواهد بود. این ممکن است مانع از عملکرد صحیح ربات‌ها و پل‌ها شود. +ما فعال کردن رمزگذاری را برای اتاق‌هایی که هر کسی می‌تواند آنها را پیدا کند و به آنها بپیوندد، توصیه نمی‌کنیم." "رمزگذاری فعال شود؟" "پس از به کار افتادن، رمزنگاری قابل از کار انداختن نیست." "رمزنگاری" - "هرکسی می‌تواند یافته و بپیوندد" - "هرکسی" - "افراد فقط در صورت دعوت می‌توانند بپیوندند" - "فقط دعوتی" - "دسترسی اتاق" + "فعال کردن رمزنگاری سراسری" + "هر کسی می‌تواند عضو شود." + "هر کس" + "انتخاب کنید که اعضای کدام فضاها می‌توانند بدون دعوتنامه به این اتاق بپیوندند. %1$s" + "مدیریت فضاها" + "فقط افراد دعوت شده می‌توانند عضو شوند." + "فقط دعوت" + "دسترسی" + "هر کسی که در فضاهای مجاز باشد می‌تواند عضو شود." + "هر کسی که در %1$s باشد می‌تواند عضو شود." + "اعضای فضا" "در حال حاضر فضاها پشتیبانی نمی‌شوند" - "نشانی اتاق" + "برای اینکه بتوانید آن را در فهرست عمومی مشاهده کنید، به یک آدرس نیاز دارید." + "آدرس" + "با جستجو در فهرست اتاق‌های عمومی %1$s، امکان یافتن این اتاق را فراهم کنید." + "با جستجو در فهرست عمومی، می‌توان آن را پیدا کرد." + "قابل مشاهده در فهرست عمومی" "هرکسی" + "تغییرات روی پیام‌های قبلی تأثیری نخواهند داشت، فقط روی پیام‌های جدید اعمال می‌شوند. %1$s" + "چه کسی می‌تواند تاریخ بخواند؟" + "اعضا از زمان دعوت" + "اعضا (تاریخچه کامل)" + "آدرس‌های اتاق، راه‌هایی برای پیدا کردن و دسترسی به اتاق‌ها هستند. این همچنین تضمین می‌کند که می‌توانید به راحتی اتاق خود را با دیگران به اشتراک بگذارید. +شما می‌توانید اتاق خود را در دایرکتوری عمومی اتاق سرور خانگی خود منتشر کنید." "انتشار اتاق" - "نمایانی اتاق" + "آدرس‌ها راهی برای پیدا کردن و دسترسی به اتاق‌ها و فضاها هستند. این همچنین تضمین می‌کند که می‌توانید به راحتی آنها را با دیگران به اشتراک بگذارید." + "قابلیت مشاهده" "امنیت و محرمانگی" diff --git a/features/signedout/impl/src/main/res/values-ar/translations.xml b/features/signedout/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..fecfbc2b3a --- /dev/null +++ b/features/signedout/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,8 @@ + + + "لقد غيّرت كلمة مرورك في جلسة أخرى" + "لقد حذفت هذه الجلسة من جلسة أخرى" + "أبطل مسؤول الخادم صلاحية وصولك" + "ربما تم تسجيل خروجك لأحد الأسباب المذكورة أدناه. يُرجى تسجيل الدخول مرة أخرى لمتابعة استخدام %s." + "تم تسجيل خروجك" + diff --git a/features/space/impl/src/main/res/values-ar/translations.xml b/features/space/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..346752f14e --- /dev/null +++ b/features/space/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,36 @@ + + + "اختيار المالكين" + "%1$s (مسؤول)" + + "مغادرة %1$d غرف والمساحة" + "مغادرة %1$d غرفة والمساحة" + "مغادرة %1$d غرفتين والمساحة" + "مغادرة %1$d غرف والمساحة" + "مغادرة %1$d غرفة والمساحة" + "مغادرة %1$d غرفة والمساحة" + + "اختر الغرف التي تريد مغادرتها، والتي لست المسؤول الوحيد عنها:" + "يجب أن تعيّن مسؤولًا آخر لهذه المساحة قبل أن تتمكن من المغادرة." + "أنت المالك الوحيد لـ %1$s. يجب أن تنقل الملكية إلى شخص آخر قبل المغادرة." + "لن تتم إزالتك من الغرف التالية لأنك المسؤول الوحيد:" + "مغادرة %1$s؟" + "أنت المسؤول الوحيد عن %1$s" + "نقل الملكية" + "غرفة" + "لن تؤثر إضافة غرفة على إمكانية الوصول إلى الغرفة. لتغيير الوصول، انتقل إلى إعدادات الغرفة > الأمان والخصوصية." + "أضف غرفتك الأولى" + "عرض الأعضاء" + "لن تؤثر إزالة غرفة على إمكانية الوصول إلى الغرفة. لتغيير الوصول، انتقل إلى معلومات الغرفة > الخصوصية والأمان." + + "إزالة %1$d غرف من %2$s" + "إزالة %1$d غرفة من %2$s" + "إزالة %1$d غرفتين من %2$s" + "إزالة %1$d غرف من %2$s" + "إزالة %1$d غرفة من %2$s" + "إزالة %1$d غرفة من %2$s" + + "مغادرة المساحة" + "الأدوار والصلاحيات" + "الأمان والخصوصية" + diff --git a/features/space/impl/src/main/res/values-es/translations.xml b/features/space/impl/src/main/res/values-es/translations.xml index 41887088b6..f65375f616 100644 --- a/features/space/impl/src/main/res/values-es/translations.xml +++ b/features/space/impl/src/main/res/values-es/translations.xml @@ -1,5 +1,28 @@ + "Elegir propietarios" + "%1$s (Admin.)" + + "Salir de %1$d sala y del espacio" + "Salir de %1$d salas y del espacio" + + "Elige las salas de las que quieras salir en las que no eres el único administrador:" + "Debes designar otro administrador para este espacio antes de poder salir." + "Eres el único propietario de %1$s. Debes transferir la propiedad a otra persona antes de salir." + "No se te eliminará de las siguientes salas porque eres el único administrador:" + "¿Salir de %1$s?" + "Eres el único administrador de %1$s" + "Transferencia de propiedad" + "Sala" + "Añadir una sala no afectará a su configuración de acceso. Para cambiar el acceso, ve a Ajustes de la sala > Seguridad y privacidad." + "Añade tu primera sala" + "Ver miembros" + "Eliminar una sala no afectará a su configuración de acceso. Para cambiar el acceso, ve a Información de la sala > Privacidad y seguridad." + + "Eliminar %1$d sala de %2$s" + "Eliminar %1$d salas de %2$s" + + "Salir del espacio" "Roles y permisos" "Seguridad y privacidad" diff --git a/features/space/impl/src/main/res/values-fa/translations.xml b/features/space/impl/src/main/res/values-fa/translations.xml index d8f377c0b2..9aa3818f44 100644 --- a/features/space/impl/src/main/res/values-fa/translations.xml +++ b/features/space/impl/src/main/res/values-fa/translations.xml @@ -2,11 +2,26 @@ "گزینش مالکان" "‏%1$s (مدیر)" + + "اتاق و فضای %1$d را ترک کنید" + "اتاق‌ها و فضای %1$d را ترک کنید" + + "اتاق‌هایی را که می‌خواهید از آنها خارج شوید و تنها مدیر آنها نیستید، انتخاب کنید:" "پیش از ترک باید مدیری دیگر به این فضا تخصیص دهید." + "شما تنها مالک %1$s هستید. قبل از ترک محل، باید مالکیت را به شخص دیگری منتقل کنید." "از اتاق(های) زیر برداشته نخواهید شد؛ چرا که تنها مدیر هستید:" "ترک %1$s؟" "تنها مدیر %1$s هستید" + "انتقال مالکیت" + "اتاق" + "اضافه کردن اتاق تاثیری بر دسترسی به اتاق نخواهد داشت. برای تغییر دسترسی، به تنظیمات اتاق > امنیت و حریم خصوصی بروید." + "اولین اتاق خود را اضافه کنید" "دیدن اعضا" + "حذف یک اتاق تاثیری بر دسترسی به آن نخواهد داشت. برای تغییر دسترسی، به اطلاعات اتاق > حریم خصوصی و امنیت بروید." + + "حذف اتاق %1$d از %2$s" + "حذف اتاق‌های %1$d از %2$s" + "ترک فضا" "نقش‌ها و مجوزها" "امنیت و محرمانگی" diff --git a/features/startchat/impl/src/main/res/values-ar/translations.xml b/features/startchat/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..b4e96844c4 --- /dev/null +++ b/features/startchat/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,12 @@ + + + "إنشاء غرفة جديدة" + "دليل الغرف" + "حدث خطأ أثناء محاولة بدء محادثة" + "الانضمام إلى غرفة باستخدام العنوان" + "العنوان غير صالح" + "أدخل العنوان…" + "تم العثور على غرفة مطابقة" + "لم يتم العثور على الغرفة" + "مثلاً: #room-name:matrix.org" + diff --git a/features/startchat/impl/src/main/res/values-es/translations.xml b/features/startchat/impl/src/main/res/values-es/translations.xml index 64a9f02a17..dd4233ce50 100644 --- a/features/startchat/impl/src/main/res/values-es/translations.xml +++ b/features/startchat/impl/src/main/res/values-es/translations.xml @@ -5,7 +5,7 @@ "Se ha producido un error al intentar iniciar un chat" "Unirse a una sala por su dirección" "Dirección no válida" - "Introducir…" + "Introducir dirección…" "Sala encontrada" "No se encontró la sala" "p. ej., #nombre-de-la-sala:matrix.org" diff --git a/features/startchat/impl/src/main/res/values-fa/translations.xml b/features/startchat/impl/src/main/res/values-fa/translations.xml index 7cb1e1baee..4a4cd239de 100644 --- a/features/startchat/impl/src/main/res/values-fa/translations.xml +++ b/features/startchat/impl/src/main/res/values-fa/translations.xml @@ -5,7 +5,7 @@ "هنگام تلاش برای شروع چت خطایی روی داد" "پیوستن به اتاق با نشانی" "نشانی معتبری نیست" - "ورود…" + "آدرس را وارد کنید…" "اتاق مطابق پیدا شد" "اتاق پیدا نشد" "نمونه: ‪#room-name:matrix.org" diff --git a/features/startchat/impl/src/main/res/values-fr/translations.xml b/features/startchat/impl/src/main/res/values-fr/translations.xml index 9aaebd018f..74a099f93a 100644 --- a/features/startchat/impl/src/main/res/values-fr/translations.xml +++ b/features/startchat/impl/src/main/res/values-fr/translations.xml @@ -5,7 +5,7 @@ "Une erreur s’est produite lors de la tentative de création de la discussion" "Saisir une adresse de salon" "Ce n’est pas une adresse valide" - "Saisir…" + "Adresse du salon…" "Ce salon existe" "Salon non trouvé" "ex: #nom-du-salon:matrix.org" diff --git a/features/userprofile/shared/src/main/res/values-ar/translations.xml b/features/userprofile/shared/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..2292e8cb70 --- /dev/null +++ b/features/userprofile/shared/src/main/res/values-ar/translations.xml @@ -0,0 +1,19 @@ + + + "حظر" + "المستخدمون المحظورون لن يتمكّنوا من إرسال رسائل لك وكل رسائلهم ستُحجب. يمكنك إلغاء حظرهم متى ما أردت." + "حظر المستخدم" + "إلغاء الحظر" + "ستتمكن من رؤية كل رسائلهم مجدّداً." + "إلغاء حظر المستخدم" + "حظر" + "المستخدمون المحظورون لن يتمكّنوا من إرسال رسائل لك وكل رسائلهم ستُحجب. يمكنك إلغاء حظرهم متى ما أردت." + "حظر المستخدم" + "الملف الشخصي" + "إلغاء الحظر" + "ستتمكن من رؤية كل رسائلهم مجدّداً." + "إلغاء حظر المستخدم" + "استخدم تطبيق الويب للتحقق من هذا المستخدم." + "تحقّق من %1$s" + "حدث خطأ أثناء محاولة بدء محادثة" + diff --git a/features/verifysession/impl/src/main/res/values-ar/translations.xml b/features/verifysession/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..104dbdacfa --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,54 @@ + + + "لا يمكنك التأكيد؟" + "إنشاء مفتاح استرداد جديد" + "اختر طريقة التحقق لإعداد المراسلة الآمنة." + "تأكيد هويتك الرقمية" + "استخدام جهاز آخر" + "استخدام مفتاح الاسترداد" + "يمكنك الآن قراءة الرسائل أو إرسالها بأمان، ويمكن لأي شخص تدردش معه أن يثق بهذا الجهاز أيضًا." + "تم التحقق من الجهاز" + "استخدام جهاز آخر" + "في انتظار الجهاز الآخر…" + "شيء ما لا يبدو سليماً. إما مهلة الطلب قد انتهت أو أن الطلب رُفِض." + "تأكد من تطابق الرموز التعبيرية أدناه مع تلك الظاهرة في جلستك الأخرى." + "قارن الرموز التعبيرية" + "أكّد أن الرموز التعبيرية أدناه تطابق الرموز المعروضة على جهاز المستخدم الآخر." + "أكّد أن الأرقام أدناه تطابق الأرقام المعروضة في جلستك الأخرى." + "قارن الأرقام" + "تم التحقق من جلستك الحالية. فيها وصول إلى رسائلك المشفّرة والمستخدمون الآخرون يرونها موثوقة." + "يمكنك الآن الوثوق بالهوية الرقمية لهذا المستخدم عند إرسال الرسائل أو تلقيها." + "تم التحقق من الجهاز" + "أدخل مفتاح الاسترداد" + "إما أن الطلب انتهت مهلته، أو تم رفضه، أو حدث عدم تطابق في التحقق." + "أثبت هويتك ليصبح لديك وصول إلى رسائلك المشفّرة السابقة." + "افتح جلسة موجودة" + "إعادة محاولة عملية التحقق" + "جاهز" + "بانتظار التطابق" + "قارِن مجموعة فريدة من الرموز التعبيرية." + "قارن بين الرموز التعبيرية المميزة، متأكداً من ورودها في نفس الترتيب." + "تم تسجيل الدخول" + "إما أن الطلب انتهت مهلته، أو تم رفضه، أو حدث عدم تطابق في التحقق." + "فشل التحقق" + "لا تتابع إلا إذا كنت أنت من بدأ هذا التحقق." + "تحقّق من الجهاز الآخر للحفاظ على أمان سجل رسائلك." + "تم التحقق من جلستك الحالية. فيها وصول إلى رسائلك المشفّرة والمستخدمون الآخرون يرونها موثوقة." + "تم التحقق من الجهاز" + "تم طلب التحقق" + "لا يتطابقون" + "يتطابقون" + "تأكّد من أن التطبيق مفتوح على الجهاز الآخر قبل بدء التحقق من هنا." + "افتح التطبيق على جهاز آخر تم التحقق منه" + "لمزيد من الأمان، تحقّق من هذا المستخدم بمقارنة مجموعة من الرموز التعبيرية على أجهزتكما. افعل ذلك باستخدام وسيلة موثوقة للتواصل." + "هل تريد التحقق من هذا المستخدم؟" + "لمزيد من الأمان، يريد مستخدم آخر التحقق من هويتك الرقمية. ستُعرض لك مجموعة من الرموز التعبيرية لمقارنتها." + "من المفترض أن تظهر نافذة منبثقة على الجهاز الآخر. ابدأ التحقق من هناك الآن." + "ابدأ التحقق على الجهاز الآخر" + "ابدأ التحقق على الجهاز الآخر" + "في انتظار المستخدم الآخر" + "بمجرد القبول، ستتمكن من متابعة التحقق." + "اِقبل طلب بدء عملية التحقق في جلستك الأخرة للاستمرار." + "بانتظار قبول الطلب" + "تجري إزالة الجهاز…" + 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 52422393bf..de23ddc217 100644 --- a/features/verifysession/impl/src/main/res/values-es/translations.xml +++ b/features/verifysession/impl/src/main/res/values-es/translations.xml @@ -2,8 +2,8 @@ "¿No puedes confirmar?" "Crear una nueva clave de recuperación" - "Verifica este dispositivo para configurar la mensajería segura." - "Confirma que eres tú" + "Elige cómo verificar para configurar la mensajería segura." + "Confirma tu identidad digital" "Usar otro dispositivo" "Usar clave de recuperación" "Ahora puedes leer o enviar mensajes de forma segura y cualquier persona con la que chatees también puede confiar en este dispositivo." @@ -17,7 +17,7 @@ "Confirma que los números que aparecen a continuación coinciden con los mostrados en tu otra sesión." "Comparar números" "Ahora puedes leer o enviar mensajes de forma segura en tu otro dispositivo." - "Ahora puedes confiar en la identidad de este usuario al enviar o recibir mensajes." + "Ahora puedes confiar en la identidad digital de este usuario al enviar o recibir mensajes." "Dispositivo verificado" "Introduce la clave de recuperación" "O bien se agotó el tiempo de solicitud, se rechazó la solicitud o hubo una discrepancia en la verificación." @@ -42,7 +42,7 @@ "Abre la aplicación en otro dispositivo verificado" "Para mayor seguridad, verifica a este usuario comparando un conjunto de emojis en vuestros dispositivos. Hazlo utilizando una forma de comunicación de confianza." "¿Verificar a este usuario?" - "Para mayor seguridad, otro usuario quiere verificar tu identidad. Se te mostrará un conjunto de emojis para compararlos." + "Para mayor seguridad, otro usuario quiere verificar tu identidad digital. Se te mostrará un conjunto de emojis para compararlos." "Deberías ver una ventana emergente en el otro dispositivo. Inicia ahora la verificación desde allí." "Iniciar la verificación en el otro dispositivo" "Iniciar la verificación en el otro dispositivo" @@ -50,5 +50,5 @@ "Una vez aceptada, podrás continuar con la verificación." "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…" + "Eliminando dispositivo…" diff --git a/features/verifysession/impl/src/main/res/values-fa/translations.xml b/features/verifysession/impl/src/main/res/values-fa/translations.xml index d68ec29229..5f2b02584c 100644 --- a/features/verifysession/impl/src/main/res/values-fa/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fa/translations.xml @@ -13,11 +13,14 @@ "يه چيزي درست به نظر نمياد یا زمان درخواست به پایان رسید یا درخواست رد شد." "تأیید تطابق شکلک‌های زیر با شکلک‌های نشان داده شده روی افزارهٔ دیگرتان." "مقایسهٔ شکلک‌ها" + "تأیید کنید که ایموجی‌های زیر با ایموجی‌های نمایش داده شده در دستگاه کاربر دیگر مطابقت دارند." "تأیید کنید که اعداد زیر با اعداد نشان داده شده در جلسه دیگر شما مطابقت دارند." "مقایسهٔ اعداد" "اکنون می‌توانید روی افزارهٔ دیگرتان با امنیت پیام فرستاده و بخوانید." + "اکنون می‌توانید هنگام ارسال یا دریافت پیام، به هویت دیجیتال این کاربر اعتماد کنید." "افزاره تأیید شده" "ورود کلید بازیابی" + "یا مهلت درخواست تمام شده، یا درخواست رد شده، یا عدم تطابق تأیید وجود داشته است." "برای دسترسی به تاریخچه پیام‌های رمزگذاری‌شده‌تان، ثابت کنید که خودتان هستید." "گشودن نشستی موجود" "تلاش برای تأیید دوباره" @@ -26,11 +29,25 @@ "مقایسهٔ مجموعه‌ای یکتا از شکلک‌ها." "شکلک‌ها را مقایسه کنید، از ترتیب نمایش آنان نیز مطمئن شوید." "وارد شده" + "یا مهلت درخواست تمام شده، یا درخواست رد شده، یا عدم تطابق تأیید وجود داشته است." "صحت‌سنجی شکست خورد" + "فقط در صورتی ادامه دهید که خودتان این تأیید را آغاز کرده باشید." + "برای ایمن نگه داشتن تاریخچه پیام‌هایتان، دستگاه دیگر را تأیید کنید." "اکنون می‌توانید روی افزارهٔ دیگرتان با امنیت پیام فرستاده و بخوانید." "افزاره تأیید شده" + "درخواست تأیید" "مطابق نیستند" "مطابقند" + "قبل از شروع تأیید از اینجا، مطمئن شوید که برنامه را در دستگاه دیگر باز کرده‌اید." + "برنامه را روی دستگاه تأیید شده دیگری باز کنید" + "برای امنیت بیشتر، با مقایسه مجموعه‌ای از ایموجی‌ها در دستگاه‌های خود، این کاربر را تأیید کنید. این کار را با استفاده از یک روش ارتباطی مطمئن انجام دهید." + "این کاربر را تایید می‌کنید؟" + "برای امنیت بیشتر، اگر کاربر دیگری بخواهد هویت دیجیتالی شما را تأیید کند، مجموعه‌ای از ایموجی‌ها برای مقایسه به شما نشان داده می‌شود." + "باید یک پنجره‌ی پاپ‌آپ روی دستگاه دیگر ببینید. اکنون تأیید را از آنجا شروع کنید." + "شروع تأیید در دستگاه دیگر" + "شروع تأیید در دستگاه دیگر" + "در انتظار کاربر دیگر" + "پس از پذیرش، می‌توانید به تأیید ادامه دهید." "برای ادامه، درخواست شروع فرآیند تأیید را در جلسه دیگر خود بپذیرید." "منظر پذیرش درخواست" "برداشتن افزاره…" diff --git a/libraries/androidutils/src/main/res/values-ar/translations.xml b/libraries/androidutils/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..6449b128d1 --- /dev/null +++ b/libraries/androidutils/src/main/res/values-ar/translations.xml @@ -0,0 +1,4 @@ + + + "لم يُعثر على تطبيق ليتعامل مع هذا الحدث." + diff --git a/libraries/dateformatter/impl/src/main/res/values-ar/translations.xml b/libraries/dateformatter/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..9e8f6c94d4 --- /dev/null +++ b/libraries/dateformatter/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,5 @@ + + + "%1$s في %2$s" + "هذا الشهر" + diff --git a/libraries/emoji/impl/src/main/res/values-ar/translations.xml b/libraries/emoji/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..5a84323379 --- /dev/null +++ b/libraries/emoji/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,13 @@ + + + "أنشطة" + "أعلام" + "أكل وشرب" + "حيوانات وطبيعة" + "أشياء" + "الابتسامات والأشخاص" + "سفر وأماكن" + "الرموز التعبيرية الأخيرة" + "رموز" + "البحث عن رموز تعبيرية" + diff --git a/libraries/emoji/impl/src/main/res/values-es/translations.xml b/libraries/emoji/impl/src/main/res/values-es/translations.xml index 7a694296b2..d32846b145 100644 --- a/libraries/emoji/impl/src/main/res/values-es/translations.xml +++ b/libraries/emoji/impl/src/main/res/values-es/translations.xml @@ -7,5 +7,7 @@ "Objetos" "Emojis y personas" "Viajes y lugares" + "Emojis recientes" "Símbolos" + "Buscar emojis" diff --git a/libraries/emoji/impl/src/main/res/values-pl/translations.xml b/libraries/emoji/impl/src/main/res/values-pl/translations.xml index c59f2c6b54..9ac9d5dff9 100644 --- a/libraries/emoji/impl/src/main/res/values-pl/translations.xml +++ b/libraries/emoji/impl/src/main/res/values-pl/translations.xml @@ -4,7 +4,7 @@ "Flagi" "Jedzenie i napoje" "Zwierzęta i natura" - "Obiekty" + "Przedmioty" "Buźki i osoby" "Podróż i miejsca" "Ostatnie emoji" diff --git a/libraries/eventformatter/impl/src/main/res/values-ar/translations.xml b/libraries/eventformatter/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..8b64ef4f6c --- /dev/null +++ b/libraries/eventformatter/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,73 @@ + + + "(الصورة غُيِّرت أيضاً)" + "غيّرَ %1$s صورته الشخصية" + "قمتَ بتغيير صورتك الشخصية" + "تم خفض رتبة %1$s إلى عضو" + "تم خفض رتبة %1$s إلى مشرف" + "غيّر %1$s اسمه من %2$s إلى %3$s" + "غيّرتَ اسمك من %1$s إلى %2$s" + "أزال %1$s اسمه (كان %2$s سابقاً)" + "قمتَ بحذف اسمك (كان %1$s سابقاً)" + "عيّن %1$s اسمه ليكون %2$s" + "عيّنتَ اسمك ليكون %1$s" + "تمت ترقية %1$s إلى مسؤول" + "تمت ترقية %1$s إلى مشرف" + "غيّر %1$s صورة الغرفة" + "قمتَ بتغيير صورة الغرفة" + "أزال %1$s صورة الغرفة" + "قمتَ بإزالة صورة الغرفة" + "%1$s حظر %2$s" + "قمتَ بحظر %1$s" + "حظرت %1$s: %2$s" + "قام %1$s بحظر %2$s: %3$s" + "%1$s أنشأ الغرفة" + "قمتَ بإنشاء الغرفة" + "%1$s دعا %2$s" + "قَبِل %1$s الدعوة" + "قمتَ بقبول الدعوة" + "قمتَ بدعوة %1$s" + "قام %1$s بدعوتك" + "انضمَّ %1$s إلى الغرفة" + "اِنضممت إلى الغرفة" + "طلب %1$s الانضمام" + "سمح %1$s لـ %2$s بالانضمام" + "قمتَ بالسماح لـ %1$s بالانضمام" + "قمتَ بطلب الانضمام" + "رفض %1$s طلب %2$s بالانضمام" + "قمتَ برفض طلب %1$s للانضمام" + "رفض %1$s طلب انضمامك" + "لم يعد %1$s مهتماً بالانضمام" + "قمتَ بإلغاء طلب انضمامك" + "غادر %1$s المجموعة" + "قمتَ بمغادرة المجموعة" + "غيّر %1$s اسم الغرفة إلى: %2$s" + "قمتَ بتغيير اسم الغرفة إلى: %1$s" + "حذف %1$s اسم الغرفة" + "قمتَ بحذف اسم الغرفة" + "%1$s لم يُجرِ أي تغييرات" + "لم تُجرِ أي تغييرات" + "غيّر %1$s الرسائل المثبّتة" + "غيّرت الرسائل المثبّتة" + "ثبّت %1$s رسالة" + "قمت بتثبيت رسالة" + "ألغى %1$s تثبيت رسالة" + "قمت بإلغاء تثبيت رسالة" + "رفض %1$s الدعوة" + "قمتَ برفض الدعوة" + "%1$s طرد %2$s" + "قمتَ بطرد %1$s" + "أزلت %1$s: %2$s" + "قام %1$s بإزالة %2$s: %3$s" + "أرسل %1$s دعوة إلى %2$s لينضم إلى الغرفة" + "قمتَ بإرسال دعوة إلى %1$s لينضم إلى الغرفة" + "تراجع %1$s عن دعوة %2$s للانضمام إلى الغرفة" + "قمتَ بالتراجع عن دعوة %1$s للانضمام إلى الغرفة" + "غيّر %1$s الموضوع إلى: %2$s" + "قمتَ بتغيير الموضوع إلى: %1$s" + "أزال %1$s موضوع الغرفة" + "قمتَ بإزالة موضوع الغرفة" + "%1$s فكّ الحظر عن %2$s" + "قمتَ بفك الحظر عن %1$s" + "أحدث %1$s تغييراً غير معلوم في عضويته" + diff --git a/libraries/eventformatter/impl/src/main/res/values-fa/translations.xml b/libraries/eventformatter/impl/src/main/res/values-fa/translations.xml index f7fd635d31..259bed9e5e 100644 --- a/libraries/eventformatter/impl/src/main/res/values-fa/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-fa/translations.xml @@ -19,6 +19,8 @@ "چهرک اتاق را برداشتید" "%2$s به دست %1$s مسدود کرد" "%1$s را مسدود کردید" + "شما %1$s: %2$s را محروم کردید" + "%1$s محروم شد %2$s: %3$s" "%1$s اتاق را ایجاد کرد" "اتاق را ساختید" "%1$s از %2$s دعوت کرد" @@ -55,6 +57,8 @@ "دعوت را رد کردید" "%2$s به دست %1$s برداشته شد" "%1$s را برداشتید" + "شما %1$s: %2$s را حذف کردید" + "%1$s حذف شد %2$s: %3$s" "%1$s دعوتی برای پیوستن %2$s به اتاق فرستاد" "برای %1$s دعوت پیوستن به اتاق فرستادید" "%1$s دعوت پیوستن به اتاق %2$s را باطل کرد" diff --git a/libraries/matrixui/src/main/res/values-ar/translations.xml b/libraries/matrixui/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..5417a5e89f --- /dev/null +++ b/libraries/matrixui/src/main/res/values-ar/translations.xml @@ -0,0 +1,9 @@ + + + "إرسال دعوة" + "هل تريد بدء دردشة مع %1$s؟" + "إرسال دعوة؟" + "ليست لديك حاليًا أي دردشات مع هذا الشخص. أكّد دعوته قبل المتابعة." + "هل تريد بدء دردشة مع جهة الاتصال الجديدة هذه؟" + "دعاك %1$s (%2$s)" + diff --git a/libraries/matrixui/src/main/res/values-es/translations.xml b/libraries/matrixui/src/main/res/values-es/translations.xml index 5d4ac4cf43..26e15d9b2e 100644 --- a/libraries/matrixui/src/main/res/values-es/translations.xml +++ b/libraries/matrixui/src/main/res/values-es/translations.xml @@ -3,5 +3,7 @@ "Enviar invitación" "¿Quieres iniciar un chat con %1$s?" "¿Enviar invitación?" + "De momento no tienes ningún chat con esta persona. Confirma que quieres invitarla antes de continuar." + "¿Iniciar un chat con este nuevo contacto?" "%1$s (%2$s) te invitó" diff --git a/libraries/matrixui/src/main/res/values-fa/translations.xml b/libraries/matrixui/src/main/res/values-fa/translations.xml index 3f5d32f974..9dfece23b2 100644 --- a/libraries/matrixui/src/main/res/values-fa/translations.xml +++ b/libraries/matrixui/src/main/res/values-fa/translations.xml @@ -3,5 +3,7 @@ "فرستادن دعوت" "می‌خواهید گپی را با %1$s بیاغازید؟" "فرستادن دعوت؟" + "شما در حال حاضر هیچ گفت‌وگویی با این شخص ندارید. قبل از ادامه، دعوت از او را تأیید کنید." + "با این مخاطب جدید گفت‌وگو را شروع کنید؟" "%1$s (%2$s) دعوتتان کرد" diff --git a/libraries/mediaviewer/impl/src/main/res/values-ar/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..0e18b01b6b --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,22 @@ + + + "ستتم إزالة هذا الملف من الغرفة ولن يتمكن الأعضاء من الوصول إليه." + "إزالة الملف؟" + "تحقّق من اتصالك بالإنترنت وحاول مرة أخرى." + "ستظهر هنا المستندات والملفات الصوتية والرسائل الصوتية التي يتم رفعها إلى هذه الغرفة." + "لم يتم رفع أي ملفات بعد" + "جارٍ تحميل الملفات…" + "جارٍ تحميل الوسائط…" + "الملفات" + "الوسائط" + "ستظهر هنا الصور ومقاطع الفيديو التي يتم رفعها إلى هذه الغرفة." + "لم يتم رفع أي وسائط بعد" + "الوسائط والملفات" + "التنسيق" + "الاسم" + "لا توجد ملفات أخرى لعرضها" + "لا توجد وسائط أخرى لعرضها" + "معلومات الملف" + "تم الرفع بواسطة" + "تم الرفع في" + diff --git a/libraries/mediaviewer/impl/src/main/res/values-es/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-es/translations.xml index 686dc60658..2a98d41e81 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-es/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-es/translations.xml @@ -6,16 +6,17 @@ "Los documentos, archivos de audio y mensajes de voz subidos a esta sala se mostrarán aquí." "Aún no se ha subido ningún archivo" "Cargando archivos…" - "Cargando medios…" + "Cargando multimedia…" "Archivos" - "Medios" + "Multimedia" "Las imágenes y vídeos subidos a esta sala se mostrarán aquí." "Aún no se ha subido ningún medio" - "Medios y archivos" + "Multimedia y archivos" "Formato de archivo" "Nombre del archivo" "No hay más archivos que mostrar" "No hay más medios que mostrar" + "Información del archivo" "Subido por" "Subido el" diff --git a/libraries/mediaviewer/impl/src/main/res/values-fa/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-fa/translations.xml index 81f8f4db2d..30a9add8f3 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-fa/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-fa/translations.xml @@ -1,5 +1,6 @@ + "این فایل از اتاق حذف خواهد شد و اعضا به آن دسترسی نخواهند داشت." "حذف پرونده؟" "بررسی اتّصال اینترنتیتان و تلاش دوباره." "سندها، پرونده‌ها و پیام‌های صوتی بار گذاشته در این اتاق این‌جا نشان داده خواهند شد." @@ -13,6 +14,9 @@ "رسانه‌ها و پرونده‌ها" "قالب پرونده" "نام پرونده" + "فایل بیشتری برای نمایش وجود ندارد" + "رسانه دیگری برای نمایش وجود ندارد" + "اطلاعات فایل" "بارگذاشته به دست" "بارگذاشته در" diff --git a/libraries/permissions/api/src/main/res/values-ar/translations.xml b/libraries/permissions/api/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..4131b6d27c --- /dev/null +++ b/libraries/permissions/api/src/main/res/values-ar/translations.xml @@ -0,0 +1,7 @@ + + + "من أجل جعل التطبيق يستخدم الكاميرا عليك بمنح الإذن لذلك في إعدادات النظام." + "يرجى منح الإذن في إعدادات النظام." + "من أجل جعل التطبيق يستخدم المايكروفون عليك بمنح الإذن لذلك في إعدادت النظام." + "من أجل جعل التطبيق يظهر الإشعارات عليك أن تمنح الإذن في إعدادات النظام." + diff --git a/libraries/permissions/impl/src/main/res/values-ar/translations.xml b/libraries/permissions/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..5d9fb3e1e6 --- /dev/null +++ b/libraries/permissions/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,5 @@ + + + "يتحقق من قدرة التطبيق على عرض الإشعارات." + "التحقق من الأذونات" + diff --git a/libraries/push/impl/src/main/res/values-ar/translations.xml b/libraries/push/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..7b8e847c72 --- /dev/null +++ b/libraries/push/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,140 @@ + + + "إتصال" + "الاستماع إلى الأحداث" + "إشعارات الصاخبة" + "رنين المكالمات" + "إشعارات صامتة" + + "%1$s: %2$d رسائل" + "%1$s: %2$d رسالة" + "%1$s: %2$d رسالتان" + "%1$s: %2$d رسائل" + "%1$s: %2$d رسالة" + "%1$s: %2$d رسالة" + + + "%d إشعارات" + "%d إشعار" + "%d إشعاران" + "%d إشعارات" + "%d إشعار" + "%d إشعار" + + "تعذّر تسجيل موزّع إشعارات UnifiedPush، لذلك لن تتلقى إشعارات بعد الآن. يرجى التحقق من إعدادات إشعارات التطبيق وحالة موزّع الإشعارات الفورية." + "أتتك رسائل جديدة." + + "لديك %d رسائل جديدة." + "لديك %d رسالة جديدة." + "لديك %d رسالتان جديدتان." + "لديك %d رسائل جديدة." + "لديك %d رسالة جديدة." + "لديك %d رسالة جديدة." + + "📞 مكالمة واردة" + "📹 مكالمة واردة" + "** فشل الإرسال - يرجى فتح الغرفة" + "إنضمام" + "رفض" + + "%d دعوة" + "%d دعوة" + "%d دعوات" + "%d دعوات" + "%d دعوات" + "%d دعوات" + + "دعاك للتحدث" + "دعاك %1$s إلى الدردشة" + "طلبت الإنضمام" + "ذكرك: %1$s" + "رسائل جديدة" + + "%d رسالة جديدة" + "%d رسالة جديدة" + "%d من الرسائل الجديدة" + "%d من الرسائل الجديدة" + "%d من الرسائل الجديدة" + "%d من الرسائل الجديدة" + + "تفاعلت بـ %1$s" + "وضع علامة كمقروء" + "رد سريع " + "دعاك للانضمام للغرفة" + "دعاك %1$s للانضمام إلى الغرفة" + "أنا" + "إشارة أو رد من %1$s" + "دعاك للانضمام إلى المساحة" + "دعاك %1$s للانضمام إلى المساحة" + "إنك ترى الإشعارات! اضغط عليّ!" + "سلسلة في %1$s" + "%1$s:%2$s" + "%1$s:%2$s:%3$s" + + "%d رسالة مُعلَمة لم تُقرأ" + "%d رسالة مُعلَمة لم تُقرأ" + "%d من الرسائل المُعلَمة لم تُقرأ" + "%d من الرسائل المُعلَمة لم تُقرأ" + "%d من الرسائل المُعلَمة لم تُقرأ" + "%d من الرسائل المُعلَمة لم تُقرأ" + + "%1$s و %2$s" + "%1$s في %2$s" + "%1$s في %2$s و %3$s" + + "%d غرفة" + "%d غرفة" + "%d من الغرف" + "%d من الغرف" + "%d من الغرف" + "%d من الغرف" + + "المزامنة في الخلفية" + "خدمات Google" + "لم يتم العثور على خدمات Google صالحة. الإشعارات قد لا تعمل كما يجب." + "التحقق من المستخدمين المحظورين" + "عرض المستخدمين المحظورين" + "لا يوجد مستخدمون محظورون." + + "لقد حظرت %1$d مستخدمين. لن تتلقى إشعارات لهؤلاء المستخدمين." + "لقد حظرت %1$d مستخدم. لن تتلقى إشعارات لهذا المستخدم." + "لقد حظرت %1$d مستخدمين. لن تتلقى إشعارات لهذين المستخدمين." + "لقد حظرت %1$d مستخدمين. لن تتلقى إشعارات لهؤلاء المستخدمين." + "لقد حظرت %1$d مستخدمًا. لن تتلقى إشعارات لهؤلاء المستخدمين." + "لقد حظرت %1$d مستخدم. لن تتلقى إشعارات لهؤلاء المستخدمين." + + "المستخدمون المحظورون" + "يحصل على اسم الموفّر الحالي." + "لم يتم تحديد أي موفّر إشعارات فورية." + "موفّر الإشعارات الفورية الحالي: %1$s والموزّع الحالي: %2$s. لكن لم يتم العثور على الموزّع %3$s. ربما تم إلغاء تثبيت التطبيق؟" + "موفّر الإشعارات الفورية الحالي: %1$s، لكن لم يتم إعداد أي موزّعين." + "موفّر الإشعارات الفورية الحالي: %1$s." + "موفّر الإشعارات الفورية الحالي: %1$s (%2$s)" + "موفّر الإشعارات الفورية الحالي" + "يتحقق من أن التطبيق يدعم موفّر إشعارات فورية واحدًا على الأقل." + "لم يتم العثور على دعم لأي موفّر إشعارات فورية." + + "تم العثور على %1$d من موفّري الإشعارات الفورية: %2$s" + "تم العثور على %1$d موفّر إشعارات فورية: %2$s" + "تم العثور على %1$d من موفّري الإشعارات الفورية: %2$s" + "تم العثور على %1$d من موفّري الإشعارات الفورية: %2$s" + "تم العثور على %1$d من موفّري الإشعارات الفورية: %2$s" + "تم العثور على %1$d من موفّري الإشعارات الفورية: %2$s" + + "تم إنشاء التطبيق بدعم لـ: %1$s" + "دعم موفّر الإشعارات الفورية" + "يتحقق من قدرة التطبيق على عرض إشعار." + "لم يتم النقر على الإشعار." + "تعذّر عرض الإشعار." + "تم النقر على الإشعار!" + "عرض إشعار" + "يرجى النقر على الإشعار لمتابعة الاختبار." + "يتحقق من أن التطبيق يستقبل الإشعارات الفورية." + "خطأ: رفض موفّر الإشعارات الفورية الطلب." + "خطأ: %1$s." + "خطأ، تعذّر اختبار الإشعارات الفورية." + "خطأ، انتهت مهلة انتظار الإشعار الفوري." + "خطأ: بوابة الإشعارات الفورية تفرض قيودًا على معدّل استخدام هذا الجهاز، لذا لن تكون الإشعارات موثوقة. يُرجى إعداد بوابة إشعارات فورية أخرى." + "استغرق اختبار إرسال واستقبال الإشعارات الفورية %1$d مللي ثانية." + "اختبار إرسال واستقبال الإشعارات الفورية" + diff --git a/libraries/push/impl/src/main/res/values-es/translations.xml b/libraries/push/impl/src/main/res/values-es/translations.xml index 412d32bfb6..b66e05be05 100644 --- a/libraries/push/impl/src/main/res/values-es/translations.xml +++ b/libraries/push/impl/src/main/res/values-es/translations.xml @@ -13,7 +13,17 @@ "%d notificación" "%d notificaciones" + "No se pudo registrar el distribuidor de notificaciones de UnifiedPush, así que no recibirás más notificaciones. Comprueba los ajustes de notificaciones de la aplicación y el estado del distribuidor de push." "Tienes nuevos mensajes." + + "Tienes %d mensaje nuevo." + "Tienes %d mensajes nuevos." + + + "Se ha compartido %1$d archivo adjunto" + "Se han compartido %1$d archivos adjuntos" + + "📞 Llamada entrante" "📹 Llamada entrante" "** No se ha podido enviar - por favor, abre la sala" "Unirse" @@ -24,6 +34,7 @@ "Te invitó a chatear" "%1$s te invitó a chatear" + "Comenzó a compartir su ubicación en tiempo real" "Te mencionó: %1$s" "Mensajes nuevos" @@ -37,7 +48,10 @@ "%1$s te invitó a unirte a la sala" "Yo" "%1$s mencionó o respondió" + "Te invitó a unirte al espacio" + "%1$s te invitó a unirte al espacio" "¡Estás viendo la notificación! ¡Haz clic en mí!" + "Hilo en %1$s" "%1$s: %2$s" "%1$s: %2$s %3$s" @@ -54,10 +68,20 @@ "Sincronización en segundo plano" "Servicios de Google" "No se han encontrado Servicios de Google Play válidos. Es posible que las notificaciones no funcionen correctamente." + "Comprobando usuarios bloqueados" + "Ver usuarios bloqueados" + "No hay usuarios bloqueados." + + "Has bloqueado %1$d usuario. No recibirás notificaciones de este usuario." + "Has bloqueado %1$d usuarios. No recibirás notificaciones de estos usuarios." + "Usuarios bloqueados" "Obtener el nombre del proveedor actual." "No se ha seleccionado ningún proveedor de push." + "Proveedor de push actual: %1$s y distribuidor actual: %2$s. Pero no se encuentra el distribuidor %3$s. ¿Quizás se ha desinstalado la aplicación?" + "Proveedor de push actual: %1$s, pero no se ha configurado ningún distribuidor." "Proveedor de push actual: %1$s." + "Proveedor de push actual: %1$s (%2$s)" "Proveedor de push actual" "Asegurarse de que la aplicación tiene al menos un proveedor de push." "No se encontró ningún proveedor de push." diff --git a/libraries/push/impl/src/main/res/values-fa/translations.xml b/libraries/push/impl/src/main/res/values-fa/translations.xml index 4afd68e17b..f1c70c7f08 100644 --- a/libraries/push/impl/src/main/res/values-fa/translations.xml +++ b/libraries/push/impl/src/main/res/values-fa/translations.xml @@ -13,7 +13,17 @@ "%dاعلان" "%dاعلان‌" + "توزیع‌کننده اعلان UnifiedPush قابل ثبت نیست، بنابراین دیگر اعلانی دریافت نخواهید کرد. لطفاً تنظیمات اعلان‌های برنامه و وضعیت توزیع‌کننده اعلان را بررسی کنید." "پیام‌های جدیدی دارید." + + "شما یک پیام جدید %d دارید." + "شما پیام‌های جدید %d دارید." + + + "پیوست %1$d مشترک" + "پیوست‌های %1$d مشترک" + + "📞 تماس ورودی" "📹 تماس ورودی" "**‌شکست در فرستادن - لطفاً اتاق را بگشایید" "پیوستن" @@ -24,6 +34,7 @@ "به گپ دعوتتان کرد" "%1$s به گپ دعوتتان کرد" + "شروع به اشتراک گذاری موقعیت مکانی زنده خود کردند" "به شما اشاره کرد: %1$s" "پیام جدید" @@ -37,7 +48,10 @@ "%1$s دعوت کرد به اتاق بپیوندید" "خودم" "%1$s اشاره کرد یا پاسخ داد" + "از شما دعوت شد تا به این فضا بپیوندید" + "%1$s از شما دعوت کرد تا به این فضا بپیوندید" "دارید آگاهی را مشاهده می‌کنید! کلیک کنید!" + "رزوه در %1$s" "%1$s: %2$s" "%1$s: %2$s %3$s" @@ -54,13 +68,28 @@ "همگام سازی پس‌زمینه" "خدمات گوگل" "خدمت پلی گوگل معتبری پیدا نشد. ممکن است آگاهی‌ها به درستی کار نکنند." + "بررسی کاربران مسدود شده" + "مشاهده کاربران مسدود شده" + "هیچ کاربری مسدود نشده است." + + "شما کاربر %1$d را مسدود کرده‌اید. شما اعلان‌هایی برای این کاربر دریافت نخواهید کرد." + "شما کاربران %1$d را مسدود کرده‌اید. برای این کاربران اعلان دریافت نخواهید کرد." + "کاربران مسدود" "گرفتن نام فراهم کنندهٔ کنونی." "هیچ فراهم کنندهٔ ارسالی گزیده نشده." + "ارائه دهنده فعلی پوش: %1$s و توزیع کننده فعلی: %2$s. اما توزیع کننده %3$s پیدا نشد. شاید برنامه حذف نصب شده باشد؟" + "ارائه دهنده فشار فعلی: %1$s، اما هیچ توزیع کننده ای پیکربندی نشده است." "فراهم کنندهٔ ارسال کنونی: %1$s." + "ارائه دهنده خدمات فشار فعلی: %1$s (%2$s)" "فراهم کنندهٔ ارسال کنونی" "ازمینان از پشتیبانی برنامه از لااقل یک فراهم کنندهٔ ارسال." "پشتیبانی از هیچ فراهم کنندهٔ ارسالی پیدا نشد." + + "ارائه دهنده فشار %1$d یافت شد: %2$s" + "ارائه دهندگان فشار %1$d یافت شد: %2$s" + + "این برنامه با پشتیبانی از موارد زیر ساخته شده است: %1$s" "پشتیبانی از فراهم‌کنندهٔ ارسال" "بررسی توانایی برنامه در نمایش آگاهی." "روی آگاهی زده نشد." diff --git a/libraries/push/impl/src/main/res/values-fi/translations.xml b/libraries/push/impl/src/main/res/values-fi/translations.xml index 24c10e8e69..0d8f486663 100644 --- a/libraries/push/impl/src/main/res/values-fi/translations.xml +++ b/libraries/push/impl/src/main/res/values-fi/translations.xml @@ -19,6 +19,10 @@ "Sinulla on %d uusi viesti." "Sinulla on %d uutta viestiä." + + "Jakoi %1$d liitteen" + "Jakoi %1$d liitettä" + "📞 Saapuva puhelu" "📹 Saapuva puhelu" "** Lähetys epäonnistui - avaa huone" @@ -30,6 +34,7 @@ "Kutsui sinut keskustelemaan" "%1$s kutsui sinut keskustelemaan" + "Aloitti reaaliaikaisen sijainnin jakamisen" "Mainitsi sinut: %1$s" "Uusia viestejä" 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 1b0c44d90e..6c129cd3ed 100644 --- a/libraries/push/impl/src/main/res/values-fr/translations.xml +++ b/libraries/push/impl/src/main/res/values-fr/translations.xml @@ -19,6 +19,10 @@ "Vous avez %d nouveau message." "Vous avez %d nouveaux messages." + + "%1$d attachement partagé" + "%1$d attachements partagés" + "📞 Appel entrant" "📹 Appel entrant" "** Échec de l’envoi - veuillez ouvrir le salon" @@ -30,6 +34,7 @@ "Vous a invité(e) à discuter" "%1$s vous a invité à discuter" + "A commencé à partager sa position en direct" "Mentionné(e) : %1$s" "Nouveaux messages" diff --git a/libraries/push/impl/src/main/res/values/localazy.xml b/libraries/push/impl/src/main/res/values/localazy.xml index 4977a9cf3d..bbe6f958b9 100644 --- a/libraries/push/impl/src/main/res/values/localazy.xml +++ b/libraries/push/impl/src/main/res/values/localazy.xml @@ -34,6 +34,7 @@ "Invited you to chat" "%1$s invited you to chat" + "Requested to join" "Started sharing their live location" "Mentioned you: %1$s" "New Messages" @@ -102,6 +103,7 @@ "Error: %1$s." "Error, cannot test push." "Error, timeout waiting for push." + "Error: the push gateway is rate limiting this device, so notifications won\'t be reliable. Please set up a different push gateway." "Push loop back took %1$d ms." "Test Push loop back" diff --git a/libraries/pushproviders/firebase/src/main/res/values-ar/translations.xml b/libraries/pushproviders/firebase/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..e5229be2f3 --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/res/values-ar/translations.xml @@ -0,0 +1,11 @@ + + + "يتحقق من توفر Firebase." + "Firebase غير متاح." + "Firebase متاح." + "التحقق من Firebase" + "يتحقق من توفر رمز Firebase المميز." + "رمز Firebase المميز غير معروف." + "رمز Firebase المميز: %1$s." + "التحقق من رمز Firebase المميز" + diff --git a/libraries/pushproviders/unifiedpush/src/main/res/values-ar/translations.xml b/libraries/pushproviders/unifiedpush/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..355ceef22d --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/main/res/values-ar/translations.xml @@ -0,0 +1,14 @@ + + + "يتحقق من توفر موزّعي UnifiedPush." + "لم يتم العثور على أي موزّع UnifiedPush." + + "تم العثور على %1$d من موزّعي UnifiedPush: %2$s." + "تم العثور على %1$d موزّع UnifiedPush: %2$s." + "تم العثور على %1$d من موزّعي UnifiedPush: %2$s." + "تم العثور على %1$d من موزّعي UnifiedPush: %2$s." + "تم العثور على %1$d من موزّعي UnifiedPush: %2$s." + "تم العثور على %1$d من موزّعي UnifiedPush: %2$s." + + "التحقق من UnifiedPush" + diff --git a/libraries/pushproviders/unifiedpush/src/main/res/values-fa/translations.xml b/libraries/pushproviders/unifiedpush/src/main/res/values-fa/translations.xml index c80e952967..af2f5f6bad 100644 --- a/libraries/pushproviders/unifiedpush/src/main/res/values-fa/translations.xml +++ b/libraries/pushproviders/unifiedpush/src/main/res/values-fa/translations.xml @@ -2,5 +2,9 @@ "اطمینان از در دسترس بودن توزیعگرهای ارسال یکپارچه." "هیچ توزیعگر ارسال یکپارچه‌ای پیدا نشد." + + "توزیع‌کننده‌ی %1$d پیدا شد: %2$s." + "توزیع‌کنندگان %1$d موارد زیر را یافتند: %2$s." + "بررسی ارسال یکپارچه" diff --git a/libraries/textcomposer/impl/src/main/res/values-ar/translations.xml b/libraries/textcomposer/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..594a06ab15 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,33 @@ + + + "إضافة مرفق" + "تبديل قائمة النقاط" + "إلغاء وإغلاق خيارات تنسيق النص" + "تبديل كتلة التعليمات البرمجية" + "أضف تعليقًا توضيحيًا" + "رسالة مشفّرة…" + "راسل…" + "رسالة غير مشفّرة…" + "إنشاء رابط" + "تعديل الرابط" + "%1$s، الحالة: %2$s" + "تطبيق التنسيق الغامق" + "تطبيق التنسيق المائل" + "معطّل" + "غير مفعّل" + "مفعّل" + "تطبيق التنسيق الذي يتوسّطه خط" + "تطبيق التنسيق الذي تحته خط" + "تبديل وضع ملء الشاشة" + "ترك مسافة بادئة" + "تطبيق تنسيق الكود المضمن" + "تعيين الرابط" + "تبديل القائمة المرقمة" + "فتح خيارات كتابة الرسالة" + "تبديل الاقتباس" + "حذف الرابط" + "إزالة المسافة البادئة" + "الرابط" + "قد لا تكون التعليقات التوضيحية مرئية للأشخاص الذين يستخدمون تطبيقات أقدم." + "اضغط مطولًا للتسجيل" + diff --git a/libraries/textcomposer/impl/src/main/res/values-es/translations.xml b/libraries/textcomposer/impl/src/main/res/values-es/translations.xml index 68b8b7b02c..16016658e5 100644 --- a/libraries/textcomposer/impl/src/main/res/values-es/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-es/translations.xml @@ -10,9 +10,12 @@ "Mensaje no cifrado…" "Crear un enlace" "Editar enlace" + "%1$s, estado: %2$s" "Aplicar formato negrita" "Aplicar formato cursiva" "deshabilitado" + "desactivado" + "activado" "Aplicar formato tachado" "Aplicar formato de subrayado" "Pantalla completa" diff --git a/libraries/textcomposer/impl/src/main/res/values-fa/translations.xml b/libraries/textcomposer/impl/src/main/res/values-fa/translations.xml index fd55fdf9d9..c3fcffc642 100644 --- a/libraries/textcomposer/impl/src/main/res/values-fa/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-fa/translations.xml @@ -28,5 +28,6 @@ "برداشتن پیوند" "تونرفتگی" "پیوند" + "ممکن است زیرنویس‌ها برای افرادی که از برنامه‌های قدیمی‌تر استفاده می‌کنند قابل مشاهده نباشند." "نگه داشتن برای ضبط" diff --git a/libraries/troubleshoot/impl/src/main/res/values-ar/translations.xml b/libraries/troubleshoot/impl/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..85331aa86a --- /dev/null +++ b/libraries/troubleshoot/impl/src/main/res/values-ar/translations.xml @@ -0,0 +1,12 @@ + + + "سجل الإشعارات الفورية" + "تشغيل الاختبارات" + "تشغيل الاختبارات مرة أخرى" + "فشلت بعض الاختبارات. يرجى التحقق من التفاصيل." + "شغّل الاختبارات لاكتشاف أي مشكلة في إعداداتك قد تجعل الإشعارات لا تعمل كما هو متوقع." + "محاولة الإصلاح" + "اجتازت جميع الاختبارات بنجاح." + "استكشاف أخطاء الإشعارات وإصلاحها" + "تتطلب بعض الاختبارات انتباهك. يرجى التحقق من التفاصيل." + diff --git a/libraries/ui-strings/src/main/res/values-ar/translations.xml b/libraries/ui-strings/src/main/res/values-ar/translations.xml new file mode 100644 index 0000000000..d8862e6dc7 --- /dev/null +++ b/libraries/ui-strings/src/main/res/values-ar/translations.xml @@ -0,0 +1,611 @@ + + + "أضف صورة رمزية" + "أضف تفاعل: %1$s" + "العنوان" + "الصورة الرمزية" + "مسح حقل البحث" + "تصغير حقل نص الرسالة" + "تقليل عدد الأصوات المسموح بها لكل شخص" + "زيادة عدد الأصوات المسموح بها لكل شخص" + "احذف" + + "تم إدخال %1$d أرقام" + "تم إدخال %1$d رقم" + "تم إدخال %1$d رقمين" + "تم إدخال %1$d أرقام" + "تم إدخال %1$d رقمًا" + "تم إدخال %1$d رقم" + + "المدة: %1$s" + "تعديل الصورة الرمزية" + "العنوان الكامل سيكون %1$s" + "تفاصيل التشفير" + "تكبير حقل نص الرسالة" + "أخفِ كلمة السر" + "معلومات" + "انضم للمكالمة" + "الانتقال للأسفل" + "الانتقال إلى الرسائل غير المقروءة" + "انقل الخريطة إلى موقعي" + "الإشارات فقط" + "مكتوم" + "إشارات جديدة" + "رسائل جديدة" + "مكالمة جارية" + "صورة رمزية لمستخدم آخر" + "الصفحة %1$d" + "إيقاف مؤقت" + "الرسالة الصوتية، المدة: %1$s ، الموضع الحالي: %2$s" + "معاينة الصورة" + "حقل الرمز السري (PIN) " + "موقع محدد" + "تشغيل" + "سرعة التشغيل" + "استفتاء" + "استفتاء منتهٍ" + "الموضع: %1$s" + "رمز QR" + "تفاعل مع %1$s" + "تفاعل مع الرموز التعبيرية الأخرى" + "قرأ %1$s و%2$s الرسالة السابقة" + + "قرأ %1$s و%2$d آخرون الرسالة السابقة" + "قرأ %1$s و%2$d آخر الرسالة السابقة" + "قرأ %1$s و%2$d آخران الرسالة السابقة" + "قرأ %1$s و%2$d آخرون الرسالة السابقة" + "قرأ %1$s و%2$d آخر الرسالة السابقة" + "قرأ %1$s و%2$d آخر الرسالة السابقة" + + "قرأ %1$s الرسالة السابقة" + "إظهار كل إيصالات القراءة" + "أزِل التفاعل: %1$s" + "أزِل التفاعل بـ %1$s" + "الصورة الرمزية للغرفة" + "إرسال ملفات" + "موقع المرسل" + "أرسلها %1$s في %2$s" + "إجراء مقيد زمنياً، لديك دقيقة واحدة للتحقق " + "الإعدادات، إجراء مطلوب" + "إظهار كلمة السر" + "بدء مكالمة" + "بدء مكالمة فيديو" + "بدء مكالمة صوتية" + "سلسلة في %1$s" + "سلاسل في %1$s" + "غرفة مستبدلة" + "الصورة الرمزية للمستخدم" + "قائمة المستخدم" + "معاينة الفيديو" + "عرض الصورة الرمزية" + "عرض التفاصيل" + "رسالة صوتية، المدة: %1$s" + "تسجيل رسالة صوتية." + "إيقاف التسجيل" + "الصورة الرمزية الخاصة بك" + "قبول" + "أضف وصف" + "إضافة غرف موجودة" + "أضف إلى الجدول الزمني" + "رجوع" + "مكالمة" + "الغاء" + "ليس الآن" + "اختيار ملف" + "اختيار صورة" + "مسح" + "إغلاق" + "أتمم التحقق" + "تأكيد" + "تأكيد كلمة المرور" + "المتابعة" + "نسخ" + "نسخ التعليق التوضيحي" + "نسخ الرابط" + "نسخ رابط الرسالة" + "انسخ النص" + "إنشاء" + "إنشاء غرفة" + "إنشاء مساحة" + "تعطيل" + "تعطيل الحساب" + "رفض" + "رفض وحظر" + "حذف" + "حذف الحساب" + "حذف الملف" + "حذف الاستطلاع" + "إلغاء تحديد الكل" + "تعطيل" + "تجاهل" + "إغلاق" + "تم" + "تنزيل" + "تعديل" + "تعديل التعليق التوضيحي" + "تعديل الاستفتاء" + "تمكين" + "إنهاء الاستفتاء" + "إدخال رمز PIN" + "استكشاف المساحات العامة" + "إنهاء" + "أنسيت كلمة السر؟" + "إعادة توجيه" + "ارجع" + "الانتقال إلى الأدوار والصلاحيات" + "اذهب إلى الإعدادات" + "تجاهل" + "دعوة " + "دعوة أشخاص" + "قم بدعوة الأصدقاء إلى %1$s" + "دعوة أشخاص إلى %1$s" + "الدعوات" + "إنضمام" + "تعلم المزيد" + "مغادرة" + "مغادرة المحادثة" + "مغادرة الغرفة" + "مغادرة المساحة" + "تحميل المزيد" + "إدارة الحساب" + "إدارة الحساب والأجهزة" + "إدارة الأجهزة" + "إدارة الغرف" + "إرسال رسالة" + "تصغير" + "التالي" + "لا" + "ليس الآن" + "حسناً" + "فتح قائمة السياق" + "الإعدادات" + "فتح باستخدام" + "تثبيت" + "رد سريع " + "عبارة" + "تفاعَل" + "رفض" + "ازالة" + "إزالة التعليق التوضيحي" + "إزالة الرسالة" + "رد" + "ردّ في ثريد" + "إبلاغ" + "الإبلاغ عن مشكلة" + "الابلاغ عن المحتوى" + "الإبلاغ عن المحادثة" + "الإبلاغ عن الغرفة" + "إعادة تعيين" + "إعادة تعيين الهوية" + "اعادة المحاولة" + "أعد محاولة فك التشفير" + "حفظ" + "بحث" + "تحديد الكل" + "ارسال" + "إرسال الرسالة المعدّلة" + "أرسل رسالة" + "إرسال رسالة صوتية" + "مشاركة" + "مشاركة رابط" + "مشاركة الموقع المباشر" + "إظهار" + "تسجيل الدخول مرة أخرى" + "إزالة هذا الجهاز" + "إزالة هذا الجهاز على أي حال" + "تخطي" + "ابدأ" + "ابدأ المحادثة" + "البدء من جديد" + "ابدأ التحقق" + "انقر لتحميل الخريطة" + "إيقاف" + "التقاط صورة" + "اضغط لعرض الخيارات" + "ترجمة" + "حاول مرة أخرى" + "إلغاء التثبيت" + "مكالمة فيديو" + "عرض" + "عرض في المخطط الزمني" + "عرض المصدر" + "مكالمة صوتية" + "نعم" + "نعم، حاول مرة أخرى" + "يدعم خادمك الآن بروتوكولًا جديدًا أسرع. سجّل الخروج ثم سجّل الدخول مرة أخرى للترقية الآن. سيساعدك القيام بذلك الآن على تجنب تسجيل خروج قسري عند إزالة البروتوكول القديم لاحقًا." + "ترقية متاحة" + "حول التطبيق" + "سياسة الاستخدام المقبول" + "إضافة حساب" + "إضافة حساب آخر" + "جارٍ إضافة تعليق توضيحي" + "إعدادات متقدمة" + "صورة" + "التحليلات" + "قمتَ بمغادرة المجموعة" + "تم تسجيل خروجك من الجلسة" + "المظهر" + "الصوت" + "تجريبي" + "المستخدمون المحظورون" + "فقاعات" + "تم رفض المكالمة" + "مكالمة جارية" + "بدأت المكالمة" + "رفضت مكالمة" + "النسخ الاحتياطي للدردشة" + "تم النسخ إلى الحافظة" + "حقوق الملكية" + "يتم إنشاء الغرفة…" + "جارٍ إنشاء المساحة…" + "تم إلغاء الطلب" + "غادرت الغرفة" + "غادرت المساحة" + "تم رفض الدعوة" + "خطأ في فك التشفير" + "الوصف" + "خيارات المطوِّرين" + "معرّف الجهاز" + "الدردشة المباشرة" + "عدم إظهار هذا مرة أخرى" + "فشل التنزيل" + "جارٍ التنزيل" + "(مُعدّلة)" + "تعديل" + "جارٍ تعديل التعليق التوضيحي" + "* %1$s %2$s" + "ملف فارغ" + "التشفير" + "التشفير مفعّل" + "ينتهي عند %1$s" + "أدخل رمز PIN الخاص بك" + "خطأ" + "حدث خطأ، قد لا تتلقى إشعارات للرسائل الجديدة. يرجى استكشاف أخطاء الإشعارات وإصلاحها من الإعدادات. + +السبب: %1$s." + "الجميع" + "فشل" + "مفضلة" + "مضافة إلى المفضلة" + "جارٍ مزامنة الإشعارات…" + "ملف" + "تمت إزالة الملف" + "تم حفظ الملف" + "تم حفظ الملف في التنزيلات" + "إعادة توجيه الرسالة" + "المستخدمة بكثرة" + "المعرض" + "GIF" + "مكالمة جماعية جارية" + "صورة" + "ردٌّ على %1$s" + "تثبيت ملف الـAPK" + "لم يتم العثور على الـMatrix ID هذا، ولذا فالدعوة قد لا تكون وصلت." + "تتم مغادرة المجموعة" + "جارٍ مغادرة المساحة" + "تم نسخ السطر إلى الحافظة" + "تم نسخ الرابط إلى الحافظة" + "ربط جهاز جديد" + "الموقع المباشر" + "انتهت مشاركة الموقع المباشر" + "جارٍ التحميل…" + "جارٍ تحميل المزيد…" + + "%d آخرون" + "%d آخر" + "%d آخران" + "%d آخرون" + "%d آخر" + "%d آخر" + + + "%1$d أعضاء" + "%1$d عضو" + "%1$d عضو" + "%1$d أعضاء" + "%1$d عضو" + "%1$d عضو" + + "رسالة" + "إجراءات الرسالة" + "فشل إرسال الرسالة" + "مظهر الرسائل" + "حُذِفت الرسالة" + "حديث" + "كتم" + "الاسم" + "%1$s (%2$s)" + "لا نتائج" + "لا يوجد اسم للغرفة" + "لا يوجد اسم للمساحة" + "غير مشفّر" + "غير متصل" + "تراخيص المصادر المفتوحة" + "أو" + "خيارات أخرى" + "كلمة السر" + "الأشخاص" + "الرابط الثابت" + "الإذن" + "مثبّتة" + "يرجى التحقق من اتصالك بالإنترنت" + "يرجى الانتظار…" + "أمتأكد من رغبتك بإنهاء الاستفتاء؟" + "استفتاء: %1$s" + "استطلاع" + "مجموع الأصوات: %1$s" + "ستظهر النتائج في حين انتهاء الاستفتاء" + + "%d صوت" + "%d صوت" + "%d صوت" + "%d أصوات" + "%d صوت" + "%d صوت" + + "جارٍ التحضير…" + "سياسة الخصوصية" + "خاصة" + "غرفة خاصة" + "مساحة خاصة" + "عامة" + "غرفة عامة" + "مساحة عامة" + "التفاعل" + "ردود الفعل" + "السبب" + "مفتاح الاسترداد" + "يتم التحديث…" + "جارٍ الإزالة…" + + "%1$d ردود" + "%1$d رد" + "%1$d ردّان" + "%1$d ردود" + "%1$d ردًا" + "%1$d رد" + + "الرد على %1$s" + "الإبلاغ عن مشكلة" + "أُرسِل الإبلاغ" + "محرر النص المنسق" + "الدور" + "غرفة" + "اسم الغرفة" + "مثال: اسم مشروعك" + + "%1$d غرف" + "%1$d غرفة" + "%1$d غرفتان" + "%1$d غرف" + "%1$d غرفة" + "%1$d غرفة" + + "تم حفظ التغييرات" + "جارٍ الحفظ" + "قفل الشاشة" + "اِبحث عن شخص ما" + "نتائج البحث" + "الأمان" + "شوهد بواسطة" + "اختر حسابًا" + + "تم تحديد %1$d" + "تم تحديد %1$d" + "تم تحديد %1$d" + "تم تحديد %1$d" + "تم تحديد %1$d" + "تم تحديد %1$d" + + "إرسال إلى" + "جارٍ الإرسال…" + "فشل الإرسال" + "تم الإرسال" + ". " + "السيرفر غير مدعوم" + "الخادم غير متاح" + "عنوان URL للسيرفر" + "الإعدادات" + "مشاركة المساحة" + "يمكن للأعضاء الجدد رؤية السجل" + "تمت مشاركة الموقع المباشر" + "الموقع المشارَك" + "مساحة مشتركة" + "جارٍ إزالة الجهاز" + "حدث خطأ ما" + "واجهنا مشكلة. يرجى المحاولة مرة أخرى." + "مساحة" + "أعضاء المساحة" + "ما موضوع هذه المساحة؟" + + "%1$d مساحات" + "%1$d مساحة" + "%1$d مساحتان" + "%1$d مساحات" + "%1$d مساحة" + "%1$d مساحة" + + "يتم بدء المحادثة…" + "مُلصَق" + "تمت العملية بنجاح" + "مقترح" + "اقتراحات" + "تتم المزامنة" + "نص" + "إشعارات الجهات الخارجية" + "ثريد" + "السلاسل" + "الموضوع" + "ما موضوع هذه الغرفة؟" + "غير قادر على فك التشفير" + "أُرسلت من جهاز غير آمن" + "ليس لديك صلاحية الوصول إلى هذه الرسالة" + "تمت إعادة تعيين الهوية الرقمية الموثّقة للمرسل" + "لم يتم التمكن من إرسال الدعوة إلى عضو أو أكثر." + "غير قادر على إرسال الدعوة (الدعوات)" + "إلغاء القفل" + "إلغاء الكتم" + "مكالمة غير مدعومة" + "حدث غير مدعوم" + "بدأ %1$s مكالمة" + "اسم المستخدم" + "أُلغيَ التحقق" + "اكتمل التحقق" + "فشل التحقق" + "تم التحقق" + "التحقّق من الجهاز" + "تحقّق من الهوية" + "تحقّق من المستخدم" + "فيديو" + "جودة عالية" + "أفضل جودة، لكن بحجم ملف أكبر" + "جودة منخفضة" + "أسرع سرعة رفع وأصغر حجم ملف" + "جودة قياسية" + "توازن بين الجودة وسرعة الرفع" + "رسالة صوتية" + "جارٍ الانتظار…" + "في انتظار هذه الرسالة" + "في انتظار الموقع المباشر…" + "يمكن لأي شخص رؤية السجل" + "أنت" + "التنزيل غير متاح." + "لم يتم العثور على الملف" + "جاري المسح…" + "تم تعطيل المعاينة والتنزيل." + "الملف غير آمن" + "شارك %1$s (%2$s) هذه الرسالة لأنك لم تكن في الغرفة عند إرسالها." + "شارك %1$s هذه الرسالة لأنك لم تكن في الغرفة عند إرسالها." + "تم إعداد هذه الغرفة بحيث يمكن للأعضاء الجدد قراءة السجل. %1$s" + "تمت إعادة تعيين الهوية الرقمية لـ %1$s. %2$s" + "تمت إعادة تعيين الهوية الرقمية الخاصة بـ %1$s %2$s. %3$s" + "(%1$s)" + "تمت إعادة تعيين الهوية الرقمية الخاصة بـ %1$s." + "تمت إعادة تعيين الهوية الرقمية الخاصة بـ %1$s %2$s. %3$s" + "إلغاء التحقق" + "السماح بالوصول" + "ينقلك الرابط %1$s إلى موقع آخر %2$s + +هل أنت متأكد من رغبتك في المتابعة؟" + "تحقّق جيدًا من هذا الرابط" + "اختر الجودة الافتراضية للفيديوهات التي ترفعها." + "جودة رفع الفيديو" + "الحد الأقصى المسموح به لحجم الملف هو: %1$s" + "حجم الملف كبير جدًا ولا يمكن رفعه" + "تم الإبلاغ عن الغرفة" + "تم الإبلاغ عن الغرفة ومغادرتها" + "التأكيد" + "خطأ" + "تمت العملية بنجاح" + "تحذير" + "لديك تغييرات غير محفوظة." + "لم تُحفظ تغييراتك. هل أنت متأكد من أنك تريد الرجوع؟" + "حفظ التغييرات؟" + "الحد الأقصى المسموح به لحجم الملف هو: %1$s" + "اختر جودة الفيديو الذي تريد رفعه." + "اختر جودة رفع الفيديو" + "لقد سجّلت الدخول بالفعل على هذا الجهاز باسم %1$s." + "يجب ترقية خادمك المنزلي لدعم Matrix Authentication Service وإنشاء الحسابات." + "فشِل إنشاء رابطٍ دائم (permalink)" + "%1$s لم يستطع تحميل الخريطة. يرجى المحاولة مجدداً لاحقاً." + "فشِل تحميل الرسائل" + "%1$s لم يستطع الوصول إلى موقعك. يرجى المحاولة مجدداً لاحقاً." + "تعذّر رفع رسالتك الصوتية." + "لم تعد الغرفة موجودة أو لم تعد الدعوة صالحة." + "يرجى تفعيل GPS للوصول إلى الميزات المستندة إلى الموقع." + "لم يتم العثور على الرسالة" + "لا يملك %1$s الإذن بالوصول إلى موقعك. يمكنك تفعيل وصوله في الإعدادات." + "لا يملك %1$s الإذن بالوصول إلى موقعك. فعّل وصوله تحت." + "ليس لدى %1$s إذن للوصول إلى الميكروفون. اسمح بالوصول لتسجيل رسالة صوتية." + "قد يكون ذلك بسبب مشكلات في الشبكة أو الخادم." + "عنوان الغرفة هذا موجود بالفعل. يرجى محاولة تعديل حقل عنوان الغرفة أو تغيير اسم الغرفة" + "بعض الأحرف غير مسموح بها. يُسمح فقط بالحروف والأرقام والرموز التالية: ! $ & ‘ ( ) * + / ; = ? @ [ ] - . _" + "لم تُرسَل بعض الرسائل" + "عذراً، قد حدث خطأ" + "🔐️ انضم إليّ في %1$s" + "مرحباً، راسلني في %1$s: %2$s" + "مشاركة الموقع المباشر" + "جارٍ مشاركة الموقع" + "%1$s - الموقع المباشر" + "%1$s Android" + "رجّ الهاتف للإبلاغ عن خطأ" + "لقطة شاشة" + "%1$s: %2$s" + "الخيارات" + "إزالة %1$s" + "الإعدادات" + "الأصوات المسموح بها لكل شخص" + "لا يشارك أحد موقعه" + "مشاركة الموقع المباشر" + + "%1$d أشخاص" + "%1$d شخص" + "%1$d شخصان" + "%1$d أشخاص" + "%1$d شخصًا" + "%1$d شخص" + + "على الخريطة" + "تعذّر اختيار الوسائط، يرجى المحاولة مجدداً." + "اضغط على رسالة واختر “%1$s” لإضافتها هنا." + "ثبّت الرسائل المهمة لتسهيل العثور عليها" + + "%1$d رسائل مثبّتة" + "%1$d رسالة مثبّتة" + "%1$d رسالتان مثبّتتان" + "%1$d رسائل مثبّتة" + "%1$d رسالة مثبّتة" + "%1$d رسالة مثبّتة" + + "الرسائل المثبّتة" + "أنت على وشك الانتقال إلى حسابك على %1$s لإعادة تعيين هويتك الرقمية. بعد ذلك ستعود إلى التطبيق." + "لا يمكنك التأكيد؟ انتقل إلى حسابك لإعادة تعيين هويتك الرقمية." + "إلغاء التحقق والإرسال" + "يمكنك إلغاء التحقق الذي أجريته وإرسال هذه الرسالة على أي حال، أو الإلغاء في الوقت الحالي والمحاولة مرة أخرى لاحقًا بعد إعادة التحقق من %1$s." + "لم تُرسل رسالتك لأن الهوية الرقمية الموثّقة لـ %1$s أُعيد تعيينها" + "إرسال الرسالة على أي حال" + "يستخدم %1$s جهازًا واحدًا أو أكثر لم يتم التحقق منه. يمكنك إرسال الرسالة على أي حال، أو الإلغاء في الوقت الحالي والمحاولة مرة أخرى لاحقًا بعد أن يتحقق %2$s من جميع أجهزته." + "لم تُرسل رسالتك لأن %1$s لم يتحقق من جميع الأجهزة" + "لم يتم التحقق من جهاز واحد أو أكثر من أجهزتك. يمكنك إرسال الرسالة على أي حال، أو الإلغاء في الوقت الحالي والمحاولة مرة أخرى لاحقًا بعد أن تتحقق من جميع أجهزتك." + "لم تُرسل رسالتك لأنك لم تتحقق من جهاز واحد أو أكثر من أجهزتك" + "تعديل المسؤولين أو المالكين" + "تعذّرت معالجة الوسائط من أجل رفعها، يرجى المحاولة مجدداً." + "تعذّر استرداد معلومات المستخدم" + "رسالة في %1$s" + "توسيع" + "طي" + "مشاركة الموقع المباشر" + "أنت في هذه الغرفة بالفعل!" + "%1$s من %2$s" + "%1$s رسائل مثبّتة" + "جارٍ تحميل الرسالة…" + "عرض الكل" + "الدردشة" + "مشاركة موقع" + "شارك موقعي" + "فتح في Apple Maps" + "فتح في Google Maps" + "فتح في OpenStreetMap" + "مشاركة الموقع الجغرافي المُحدّد" + "خيارات المشاركة" + "المساحات التي أنشأتها أو انضممت إليها." + "%1$s • %2$s" + "أنشئ مساحات لتنظيم الغرف" + "مساحة %1$s" + "المساحات" + "تمت المشاركة %1$s" + "على الخريطة" + "لم تُرسل الرسالة لأن الهوية الرقمية الموثّقة لـ %1$s أُعيد تعيينها." + "لم تُرسل الرسالة لأن %1$s لم يتحقق من جميع الأجهزة." + "لم تُرسل الرسالة لأنك لم تتحقق من جهاز واحد أو أكثر من أجهزتك." + "الموقع" + "الإصدار: %1$s (%2$s)" + "ar" + "الرسائل السابقة غير متاحة على هذا الجهاز" + "يجب التحقق من هذا الجهاز للوصول إلى الرسائل السابقة" + "ليس لديك صلاحية الوصول إلى هذه الرسالة" + "تعذّر فك تشفير الرسالة" + "تم حظر هذه الرسالة إما لأنك لم تتحقق من جهازك أو لأن المرسل يحتاج إلى التحقق من هويتك الرقمية." + "جهازك قديم جدًا؛ يلزم جهاز يعمل بنظام Android 8 أو أحدث." + "عرض الخرائط غير مدعوم" + diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index c068e23d6c..223b8aa457 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -1,51 +1,101 @@ + "Añadir avatar" "Añadir reacción: %1$s" + "Dirección" "Avatar" + "Borrar consulta" + "Minimizar el campo de texto del mensaje" + "Disminuir el número de votos permitidos por persona" + "Aumentar el número de votos permitidos por persona" + "Emoji personalizado" "Borrar" "%1$d dígito introducido" "%1$d dígitos introducidos" + "Duración: %1$s" + "Editar avatar" + "La dirección completa será %1$s" + "Detalles de cifrado" + "Expandir campo de texto del mensaje" + "Galería, archivo adjunto %1$d" + "Galería, escaneo fallido %1$d" + "Galería, imagen %1$d" + + "Galería, %1$d elemento multimedia más" + "Galería, %1$d elementos multimedia más" + + "Galería, escaneando %1$d" + "Galería, vídeo %1$d" "Ocultar contraseña" + "Info" "Unirse a la llamada" "Ir al final" + "Ir a los mensajes no leídos" + "Ir al primer mensaje no leído" + "Desplazar el mapa a mi ubicación" "Sólo menciones" "Silenciado" + "Menciones nuevas" + "Mensajes nuevos" + "Llamada en curso" + "Avatar del otro usuario" "Página %1$d" "Pausar" "Mensaje de voz, duración: %1$s, posición actual: %2$s" + "Vista previa de la foto" "Campo PIN" + "Ubicación fijada" "Reproducir" + "Velocidad de reproducción" "Encuesta" "Encuesta finalizada" + "Posición: %1$s" + "Código QR" "Reacciona con %1$s" "Reacciona con otros emojis" - "Leído por %1$s y %2$s" + "Mensaje anterior leído por %1$s y %2$s" - "Leído por %1$s y %2$d otro" - "Leído por %1$s y %2$d otros" + "Mensaje anterior leído por %1$s y %2$d más" + "Mensaje anterior leído por %1$s y %2$d más" - "Leído por %1$s" + "Mensaje anterior leído por %1$s" + "mostrar todas las confirmaciones de lectura" "Elimina la reacción con %1$s" "Retirar la reacción con %1$s" "Avatar de la sala" "Enviar archivos" + "Ubicación del remitente" + "Enviado por %1$s a las %2$s" + "Acción de tiempo limitado requerida, tienes un minuto para hacer la verificación" + "Ajustes, acción requerida" "Mostrar contraseña" "Iniciar llamada" + "Iniciar una videollamada" + "Iniciar una llamada de voz" + "Estado" + "Hilo en %1$s" + "Hilos en %1$s" + "Sala clausurada" + "Avatar del usuario" "Menú de usuario" + "Vista previa del vídeo" "Ver avatar" "Mostrar detalles" "Mensaje de voz, duración: %1$s" "Grabar mensaje de voz" "Detener grabación" + "Tu avatar" "Aceptar" "Agregar leyenda" + "Añadir salas existentes" "Añadir a la cronología" "Atrás" "Llamar" "Cancelar" "Cancelar por ahora" + "Seleccionar archivo" "Elegir foto" "Borrar" "Cerrar" @@ -60,24 +110,34 @@ "Copiar texto" "Crear" "Crear una sala" + "Crear espacio" "Desactivar" "Desactivar cuenta" "Rechazar" "Rechazar y bloquear" + "Eliminar" + "Eliminar cuenta" + "Eliminar archivo" "Eliminar encuesta" + "Deseleccionar todo" "Desactivar" "Descartar" "Descartar" "Hecho" + "Descargar" "Editar" "Editar leyenda" "Editar encuesta" "Activar" "Finalizar encuesta" "Introducir PIN" + "Explorar espacios públicos" + "Finalizar" "¿Olvidaste tu contraseña?" "Reenviar" "Volver atrás" + "Ir a roles y permisos" + "Ir a ajustes" "Ignorar" "Invitar" "Invitar personas" @@ -89,14 +149,20 @@ "Salir" "Salir de la conversación" "Salir de la sala" + "Salir del espacio" "Cargar más" "Gestionar cuenta" + "Gestionar cuenta y dispositivos" "Administrar dispositivos" + "Gestionar salas" + "Marcar como leído" "Enviar mensaje" + "Minimizar" "Siguiente" "No" "Ahora no" "OK" + "Abrir menú contextual" "Ajustes" "Abrir con" "Fijar" @@ -108,7 +174,7 @@ "Quitar leyenda" "Eliminar mensaje" "Responder" - "Responder en el hilo" + "Responder en hilo" "Denunciar" "Informar de un error" "Denunciar contenido" @@ -120,51 +186,69 @@ "Reintentar descifrado" "Guardar" "Buscar" + "Seleccionar todo" "Enviar" + "Enviar mensaje editado" "Enviar mensaje" + "Enviar mensaje de voz" "Compartir" "Compartir enlace" + "Compartir ubicación en directo" "Mostrar" "Inicia sesión de nuevo" - "Cerrar sesión" - "Cerrar sesión de todos modos" + "Eliminar este dispositivo" + "Eliminar este dispositivo de todos modos" "Saltar" "Comenzar" "Iniciar chat" "Empezar de nuevo" "Iniciar la verificación" "Pulsa para cargar el mapa" + "Parar" "Hacer foto" "Toca para ver opciones" + "Traducir" "Intentar de nuevo" "Desprender" + "Videollamada" "Ver" "Ver en la cronología" "Ver fuente" + "Llamada de voz" "Sí" "Sí, intentar de nuevo" "Tu servidor es ahora compatible con un protocolo nuevo y más rápido. Cierra la sesión y vuelve a iniciarla para actualizar ahora. Haciéndolo ahora, evitarás tener que cerrar la sesión cuando se elimine el protocolo antiguo." "Actualización disponible" "Acerca de" "Política de uso aceptable" + "Añadir una cuenta" + "Añadir cuenta" "Añadiendo leyenda" "Ajustes avanzados" "una imagen" "Estadísticas" "Saliste de la sala" + "Se te ha desconectado de la sesión" "Apariencia" "Sonido" + "Beta" "Usuarios bloqueados" "Burbujas" + "Llamada rechazada" + "Llamada en curso" "Llamada iniciada" + "Rechazaste una llamada" "Copia de seguridad del chat" "Copiado al portapapeles" "Derechos de autor" "Creando sala…" + "Creando espacio…" "Solicitud cancelada" "Saliste de la sala" + "Saliste del espacio" "Invitación rechazada" "Error de descifrado" + "Descripción" "Opciones de desarrollador" "ID de dispositivo" "Chat directo" @@ -178,6 +262,7 @@ "Archivo vacío" "Cifrado" "Cifrado activado" + "Termina a las %1$s" "Introduce tu PIN" "Error" "Se ha producido un error, es posible que no recibas notificaciones de mensajes nuevos. Soluciona los problemas con las notificaciones desde los ajustes. @@ -187,20 +272,35 @@ Motivo: %1$s." "Falló" "Favorito" "Marcado como favorito" + "Sincronizando notificaciones…" "Archivo" "Archivo eliminado" "Archivo guardado" "Archivo guardado en Descargas" "Reenviar mensaje" "Usado frecuentemente" + "Galería" + + "%1$d archivo adjunto…" + "%1$d archivos adjuntos…" + + + "%1$d elemento multimedia…" + "%1$d elementos multimedia…" + "GIF" + "Llamada grupal en curso" "Imagen" "En respuesta a %1$s" "Instalar APK" "No se encontró este ID de Matrix, por lo que es posible que no se reciba la invitación." "Saliendo de la sala" + "Saliendo del espacio" "Línea copiada al portapapeles" "Enlace copiado al portapapeles" + "Vincular un dispositivo nuevo" + "Location en directo" + "La ubicación en tiempo real ha terminado" "Cargando…" "Cargando más…" @@ -213,17 +313,22 @@ Motivo: %1$s." "Mensaje" "Acciones del mensaje" + "El mensaje no se pudo enviar" "Diseño de los mensajes" "Mensaje eliminado" "Moderno" "Silenciar" + "Nombre" "%1$s (%2$s)" "No hay resultados" "Sala sin nombre" + "Espacio sin nombre" "No cifrado" "Sin conexión" + "En una llamada" "Licencias de código abierto" "o" + "Otras opciones" "Contraseña" "Personas" "Enlace permanente" @@ -233,27 +338,43 @@ Motivo: %1$s." "Espera, por favor…" "¿Estás seguro de que quieres finalizar esta encuesta?" "Encuesta: %1$s" + "Encuesta" "Total de votos: %1$s" "Los resultados se mostrarán una vez finalizada la encuesta" "%d voto" "%d votos" + "Preparando…" "Política de privacidad" + "Privado" "Sala privada" + "Espacio privado" + "Público" "Sala pública" + "Espacio público" "Reacción" "Reacciones" "Motivo" "Clave de recuperación" "Recargando…" + "Eliminando…" + + "%1$d respuesta" + "%1$d respuestas" + "Respondiendo a %1$s" "Informar de un problema" "Informe enviado" "Editor de texto enriquecido" + "Rol" "Sala" "Nombre de la sala" "p. ej., el nombre de tu proyecto" + + "%1$d sala" + "%1$d salas" + "Cambios guardados" "Guardando" "Bloqueo de pantalla" @@ -261,37 +382,58 @@ Motivo: %1$s." "Buscar resultados" "Seguridad" "Visto por" + "Elige una cuenta" + + "%1$d seleccionado" + "%1$d seleccionados" + "Enviar a" "Enviando…" "Fallo al enviar" "Enviado" + ". " "Servidor no compatible" + "Servidor inaccesible" "Dirección del servidor" "Ajustes" + "Compartir espacio" + "Los miembros nuevos ven el historial" + "Ubicación compartida en directo" "Ubicación compartida" - "Cerrando sesión" + "Espacio compartido" + "Eliminando dispositivo" "Algo salió mal" "Hemos encontrado un problema. Inténtalo de nuevo." + "Espacio" + "Miembros del espacio" + "¿De qué trata este espacio?" + + "%1$d espacio" + "%1$d espacios" + "Iniciando chat…" "Sticker" "Terminado" + "Sugerido" "Sugerencias" "Sincronizando" "Texto" "Avisos de terceros" "Hilo" + "Hilos" "Tema" "¿De qué trata esta sala?" "No se puede descifrar" "Enviado desde un dispositivo no seguro" "No tienes acceso a este mensaje" - "Se restableció la identidad verificada del remitente" + "Se restableció la identidad digital verificada del remitente" "Las invitaciones no se pudieron enviar a uno o más usuarios." "No se pudo enviar la(s) invitación(es)" "Desbloquear" "Dejar de silenciar" "Llamada no compatible" "Evento no compatible" + "%1$s inició una llamada" "Usuario" "Verificación cancelada" "Verificación completada" @@ -301,20 +443,41 @@ Motivo: %1$s." "Verificar identidad" "Verificar usuario" "Vídeo" + "Calidad alta" + "La mejor calidad, pero mayor tamaño de archivo" + "Calidad baja" + "Velocidad de subida más rápida y tamaño de archivo más pequeño" + "Calidad estándar" + "Equilibrio entre calidad y velocidad de subida" "Mensaje de voz" "Esperando…" "Esperando este mensaje" + "Esperando la ubicación en directo…" + "Cualquiera puede ver el historial" "Tú" - "Se restableció la identidad de %1$s. %2$s" - "Se restableció la identidad de %1$s %2$s. %3$s" + "Descarga no disponible." + "Algo salió mal" + "Escaneando…" + "Se ha desactivado la vista previa y la descarga." + "El archivo no es seguro" + "%1$s (%2$s) compartió este mensaje ya que no estabas en la sala cuando se envió." + "%1$s compartió este mensaje ya que no estabas en la sala cuando se envió." + "Esta sala se ha configurado para que los miembros nuevos puedan leer el historial. %1$s" + "Se restableció la identidad digital de %1$s. %2$s" + "Se restableció la identidad digital de %1$s %2$s. %3$s" "(%1$s)" - "Se restableció la identidad de %1$s." - "Se restableció la identidad de %1$s %2$s. %3$s" + "Se restableció la identidad digital de %1$s." + "Se restableció la identidad digital de %1$s %2$s. %3$s" "Retirar la verificación" + "Permitir acceso" "El enlace %1$s te está llevando a otro sitio %2$s ¿Estás seguro de que quieres continuar?" "Revisa este enlace" + "Selecciona la calidad por defecto de los vídeos que subas." + "Calidad de subida de vídeos" + "El tamaño máximo de archivo permitido es: %1$s" + "El archivo es demasiado grande para ser subido" "Sala denunciada" "Denunciaste y saliste de la sala" "Confirmar" @@ -324,6 +487,10 @@ Motivo: %1$s." "Tienes cambios sin guardar." "Tus cambios no se han guardado. ¿Estás seguro de que quieres volver atrás?" "¿Guardar cambios?" + "El tamaño máximo de archivo permitido es: %1$s" + "Selecciona la calidad del vídeo que quieres subir." + "Selecciona la calidad de subida del vídeo" + "Ya has iniciado sesión en este dispositivo como %1$s." "Tu servidor base debe actualizarse para admitir Matrix Authentication Service y la creación de cuentas." "No se pudo crear el enlace permanente" "%1$s no pudo cargar el mapa. Por favor vuelve a intentarlo más tarde." @@ -331,6 +498,7 @@ Motivo: %1$s." "%1$s no ha podido acceder a tu ubicación. Por favor vuelve a intentarlo más tarde." "No se pudo cargar tu mensaje de voz." "La sala ya no existe o la invitación ya no es válida." + "Activa el GPS para acceder a las funciones basadas en la ubicación." "Mensaje no encontrado" "%1$s no tiene permiso para acceder a tu ubicación. Puedes habilitar el acceso en Ajustes." "%1$s no tiene permiso para acceder a tu ubicación. Habilita el acceso a continuación." @@ -342,10 +510,34 @@ Motivo: %1$s." "Lo siento, se ha producido un error" "🔐️ Únete a mí en %1$s" "Hola, puedes hablar conmigo en %1$s: %2$s" + "Compartir la ubicación en directo" + "Se está compartiendo actualmente la ubicación" + "%1$s Ubicación en tiempo real" "%1$s Android" "Agitar con fuerza para informar de un error" + "Captura de pantalla" + "Iniciar sesión" + "Crear cuenta" + "Si tu cuenta te la proporciona tu empresa o una comunidad, cambia el proveedor de la cuenta (ejemplo: nombredeempresa.com)." + "Crea una cuenta gratuita en matrix.org. Si deseas crear una cuenta con otro proveedor, modifícala según corresponda (por ejemplo: communityname.org)." + "Proveedor de cuentas" + "ejemplo.com" + "Introduce tu proveedor de cuenta" + "Elegir proveedor de cuentas" + "%1$s: %2$s" "Opciones" + "Eliminar %1$s" "Ajustes" + "Votos permitidos por persona" + "Nadie está compartiendo su ubicación" + "Compartiendo ubicación en tiempo real" + + "%1$d persona" + "%1$d personas" + + "En el mapa" + "Tu servidor base está en tu red local. Para conectarse, %1$s necesita permiso para acceder a los dispositivos de esta red." + "Permite el acceso a la red local" "Error al seleccionar archivos multimedia, por favor inténtalo de nuevo." "Presiona sobre un mensaje y selecciona «%1$s» para incluirlo aquí." "Fija los mensajes importantes para que se puedan descubrir fácilmente" @@ -354,21 +546,23 @@ Motivo: %1$s." "%1$d mensajes fijados" "Mensajes fijados" - "Estás a punto de ir a tu cuenta de %1$s para restablecer tu identidad. Posteriormente volverás a la aplicación." - "¿No puedes confirmar? Ve a tu cuenta para restablecer tu identidad." + "Estás a punto de ir a tu cuenta de %1$s para restablecer tu identidad digital. Posteriormente volverás a la aplicación." + "¿No puedes confirmarla? Ve a tu cuenta para restablecer tu identidad digital." "Retirar la verificación y enviar" "Puedes retirar tu verificación y enviar este mensaje de todos modos, o puedes cancelarlo por ahora e intentarlo de nuevo más tarde después de volver a verificar a %1$s." - "Tu mensaje no se envió porque la identidad verificada de %1$s fue restablecida" + "Tu mensaje no se envió porque la identidad digital verificada de %1$s fue restablecida" "Enviar mensaje de todos modos" "%1$s utiliza uno o más dispositivos no verificados. Puedes enviar el mensaje de todos modos, o puedes cancelarlo por ahora y volver a intentarlo más tarde, una vez %2$s haya verificado todos sus dispositivos." "Tu mensaje no se envió porque %1$s no ha verificado todos los dispositivos" "Uno o más de tus dispositivos no están verificados. Puedes enviar el mensaje de todos modos, o puedes cancelarlo por ahora e intentarlo de nuevo más tarde, una vez hayas verificado todos tus dispositivos." "Tu mensaje no se envió porque no has verificado uno o más de tus dispositivos" + "Editar administradores o propietarios" "Error al procesar el contenido multimedia, por favor inténtalo de nuevo." "No se pudieron recuperar los detalles del usuario" "Mensaje en %1$s" "Expandir" "Reducir" + "Compartiendo ubicación en tiempo real" "¡Ya estás viendo esta sala!" "%1$s de %2$s" "%1$s mensajes fijados" @@ -380,8 +574,20 @@ Motivo: %1$s." "Abrir en Apple Maps" "Abrir en Google Maps" "Abrir en OpenStreetMap" - "Compartir esta ubicación" - "Mensaje no enviado porque la identidad verificada de %1$s fue restablecida." + "Compartir ubicación seleccionada" + "Cómo compartir" + "Espacios que has creado o a los que te has unido." + "%1$s • %2$s" + "Crea espacios para organizar salas" + "Espacio %1$s" + "Espacios" + "Compartida %1$s" + "En el mapa" + + "Se unió %1$d" + "Se unieron %1$d" + + "Mensaje no enviado porque la identidad digital verificada de %1$s fue restablecida." "Mensaje no enviado porque %1$s no ha verificado todos los dispositivos." "Mensaje no enviado porque no has verificado uno o más de tus dispositivos." "Ubicación" @@ -391,5 +597,7 @@ Motivo: %1$s." "Debes verificar este dispositivo para acceder a los mensajes históricos" "No tienes acceso a este mensaje" "No se ha podido descifrar el mensaje" - "Este mensaje fue bloqueado bien sea porque no verificaste tu dispositivo o porque el remitente necesita verificar tu identidad." + "Este mensaje fue bloqueado bien sea porque no verificaste tu dispositivo o porque el remitente necesita verificar tu identidad digital." + "Tu dispositivo es demasiado antiguo; necesitas un dispositivo con Android 8 o posterior." + "No se admite el renderizado de mapas" diff --git a/libraries/ui-strings/src/main/res/values-fa/translations.xml b/libraries/ui-strings/src/main/res/values-fa/translations.xml index 03d1172040..49a790c2ea 100644 --- a/libraries/ui-strings/src/main/res/values-fa/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fa/translations.xml @@ -1,18 +1,30 @@ + "اضافه کردن آواتار" "افزودن واکنش: %1$s" + "آدرس" "چهرک" + "پاک کردن فیلد جستجو" + "کوچک کردن فیلد متن پیام" + "کاهش آرای مجاز برای هر نفر" + "افزایش تعداد آرای مجاز برای هر نفر" + "ایموجی‌های سفارشی" "حذف" "%1$d رقم وارد شده" "%1$d رقم وارد شده" + "مدت زمان: %1$s" "ویرایش چهرک" "نشانی کامل %1$s خواهد بود" "جزییات رمزنگاری" + "فیلد متن پیام را گسترش دهید" "نهفتن گذرواژه" + "اطلاعات" "پیوستن به تماس" "به پایین بروید" + "پرش به خوانده نشده" + "پرش به اولین پیام خوانده نشده" "جابه‌جایی نقشه به مکانم" "فقط اشاره‌ها" "خموش" @@ -23,10 +35,15 @@ "صفحهٔ %1$d" "مکث" "پیام صوتی. طول %1$s. موقعیت کنونی: %2$s" + "پیش‌نمایش عکس" "زمینهٔ پین" + "مکان پین شده" "پخش" + "سرعت پخش" "نظرسنجی" "نظرسنجی پایان یافته" + "موقعیت: %1$s" + "کد QR" "واکنش با %1$s" "واکنش با شکلک‌های دیگر" "پیام پیشین به دست %1$s و %2$s خوانده شد" @@ -35,15 +52,26 @@ "پیام پیشین به دست %1$s و %2$d دیگر خوانده شد" "پیام قبلی به دست %1$s خوانده شد" + "نمایش همه رسیدهای خوانده شده" "برداشتن واکنش با %1$s" "برداشتن واکنش با %1$s" "چهرک اتاق" "ارسال پرونده‌ها" - "نیازمند کنش محدود به زمان" + "مکان فرستنده" + "ارسال شده توسط %1$s در %2$s" + "اقدام مورد نیاز با محدودیت زمانی، شما یک دقیقه برای تأیید فرصت دارید" + "تنظیمات، اقدام مورد نیاز" "نمایش گذرواژه" "آغاز یک تماس" + "شروع تماس ویدیویی" + "شروع تماس صوتی" + "وضعیت" + "رزوه در %1$s" + "موضوعات در %1$s" + "اتاق سنگ قبر" "چهرک کاربر" "فهرست کاربر" + "پیش‌نمایش ویدیو" "دیدن چهرک" "دیدن جزییات" "پیام صوتی. طول %1$s" @@ -52,11 +80,13 @@ "چهرکتان" "پذیرش" "افزودن عنوان" + "افزودن اتاق‌های موجود" "افزودن به خط زمانی" "بازگشت" "تماس" "لغو" "لغو برای امنون" + "انتخاب فایل" "گزینش عکس" "پاک سازی" "بستن" @@ -71,26 +101,34 @@ "رونوشت از متن" "ایجاد" "ایجاد اتاق" + "ایجاد فضا" "غیرفعّال" "غیرفعّال‌سازی حساب" "رد" "رد و انسداد" + "پاک‌کردن" + "حذف حساب کاربری" + "حذف فایل" "حذف نظرسنجی" "ناگزینش همه" "از کار انداختن" "دور ریختن" "دورانداختن" "انجام شد" + "دانلود" "ویرایش" "ویرایش عنوان" "ویرایش نظرسنجی" "به کار انداختن" "پایان نظرسنجی" "ورود پین" + "کاوش در فضاهای عمومی" "پایان" "گذرواژه را فراموش کردید؟" "پیشروی" "پس‌روی" + "به نقش‌ها و مجوزها بروید" + "به تنظیمات بروید" "نادیده‌گرفتن" "دعوت" "دعوت افراد" @@ -105,8 +143,12 @@ "ترک فضا" "بار کردن بیش‌تر" "مدیریت حساب" + "مدیریت حساب و دستگاه‌ها" "مدیریت افزاره‌ها" + "مدیریت اتاق‌ها" + "علامت‌گذاری به عنوان خوانده شده" "پیام" + "کوچک کردن" "بعدی" "نه" "الآن نه" @@ -120,8 +162,8 @@ "واکنش" "رد کردن" "حذف" - "برداشتن عنوان" - "برداشتن پیام‌ها" + "حذف عنوان" + "حذف پیام" "پاسخ" "پاسخ در رشته" "گزارش" @@ -142,25 +184,31 @@ "فرستادن پیام صوتی" "هم‌رسانی" "هم‌رسانی پیوند" + "اشتراک گذاری موقعیت مکانی زنده" "نمایش" "ورود دوباره" "برداشتن این افزاره" "به هر حال این دستگاه را حذف کنید" - "پرش" + "رد شدن" "آغاز" "آغاز گپ" "آغاز از نو" "آغاز تأیید" "زدن برای بار کردن نقشه" + "توقف" "عکس گرفتن" "زدن برای گزینه‌ها" + "ترجمه" "تلاش دوباره" "سنجاق نکردن" + "تماس تصویری" "نما" "دیدن در خط زمانی" "دیدن منبع" + "تماس صوتی" "بله" "بله. تلاش دوباره" + "سرور شما اکنون از یک پروتکل جدید و سریع‌تر پشتیبانی می‌کند. برای ارتقا، از سیستم خارج شوید و دوباره وارد شوید. انجام این کار در حال حاضر به شما کمک می‌کند تا از خروج اجباری هنگام حذف پروتکل قدیمی در آینده جلوگیری کنید." "ارتقا موجود است" "درباره" "سیاست استفادهٔ پذیرفتنی" @@ -168,18 +216,24 @@ "افزودن حسابی دیگر" "افزودن عنوان" "تنظیمات پیش‌رفته" + "یک تصویر" "تجزیه و تحلیل" "اتاق را ترک کردید" + "شما از جلسه خارج شدید" "ظاهر" "صدا" "آزمایشی" "کاربران مسدود" "حباب‌ها" + "تماس رد شد" + "تماس در جریان است" "تماس آغاز شد" + "شما یک تماس را رد کردید" "پشتیبان گپ" "در تخته‌گیره رونوشت شد" "حق رونوشت" "ایجاد کردن اتاق…" + "ایجاد فضای…" "درخواست لغو شد" "اتاق را ترک کرد" "فضا را ترک کرد" @@ -199,19 +253,34 @@ "پروندهٔ خالی" "رمزنگاری" "رمزنگاری به کار افتاده" + "در %1$s پایان می‌یابد" "پینتان را وارد کنید" "خطا" + "خطایی رخ داده است، ممکن است اعلان‌های مربوط به پیام‌های جدید را دریافت نکنید. لطفاً اعلان‌ها را از تنظیمات عیب‌یابی کنید. + +دلیل: %1$s." "هرکسی" "شکست خورد" "برگزیده" "برگزیده" + "همگام‌سازی اعلان‌ها…" "پرونده" "پرونده حذف شد" "پرونده ذخیره شد" "پرونده در بارگیری‌ها ذخیره شد" "هدایت پیام" "پراستفاده" + "گالری" + + "پیوست %1$d…" + "پیوست‌های %1$d…" + + + "مورد رسانه‌ای %1$d…" + "موارد رسانه‌ای %1$d…" + "جیف" + "تماس گروهی در جریان است" "تصویر" "در پاسخ به %1$s" "نصب APK" @@ -220,6 +289,9 @@ "ترک کردن فضا" "خط در تخته‌گیره رونوشت شد" "پیوند در تخته‌گیره رونوشت شد" + "اتصال دستگاه جدید" + "مکان زنده" + "مکان زنده به پایان رسید" "بار کردن…" "بار کردن بیش‌تر…" @@ -232,18 +304,22 @@ "پیام" "کنش‌های پیام" + "پیام ارسال نشد" "جینش پیام" "پیام برداشته شد" "نوین" "بی‌صدا" + "نام" "%1$s ‏(%2$s)" "بدون نتیجه" "بدون نام اتاق" "بدون نام فضا" "رمزنگاری نشده" "برون‌خط" + "در یک تماس" "پروانه‌های نرم‌افزاری آزاد" "یا" + "گزینه‌های دیگر" "گذرواژه" "افراد" "پایاپیوند" @@ -253,6 +329,7 @@ "لطفا صبر کنید…" "مطئنید که می‌خواهید این نظرسنجی را پایان دهید؟" "نظرسنجی: %1$s" + "نظرسنجی" "مجموع آرا: %1$s" "نتیجه‌ها پس از پایان نظرسنجی نشان داده خواهند شد" @@ -261,8 +338,10 @@ "آماده سازی…" "سیاست محرمانگی" + "خصوصی" "اتاق خصوصی" "فضای خصوصی" + "عمومی" "اتاق عمومی" "فضای عمومی" "واکنش" @@ -270,13 +349,23 @@ "دلیل" "کلید بازیابی" "تازه سازی…" + "در حال حذف…" + + "پاسخ %1$d" + "پاسخ‌های %1$d" + "پاسخ دادن به %1$s" "گزارش مشکل" "گزارش ثبت شد" "ویرایشگر متن غنی" + "نقش" "اتاق" "نام اتاق" "برای نمونه نام پروژه‌تان" + + "اتاق %1$d" + "اتاق‌های %1$d" + "تغییرات ذخیره شده" "ذخیره کردن" "قفل صفحه" @@ -285,6 +374,10 @@ "امنیت" "دیده شده به دست" "گزینش حساب" + + "%1$d انتخاب شد" + "%1$d انتخاب شد" + "فرستادن به" "فرستادن…" "فرستادن شکست خورد" @@ -295,29 +388,43 @@ "نشانی کارساز" "تنظیمات" "هم‌رسانی فضا" + "اعضای جدید تاریخچه را می‌بینند" + "مکان زنده به اشتراک‌گذاری شده" "مکان هم‌رسانده" "فضای اشتراکی" "خارج شدن" "چیزی اشتباه پیش رفت" + "با مشکلی مواجه شدیم. لطفا دوباره امتحان کنید." "فاصله" + "اعضای فضا" + "این فضا مربوط به چیست؟" + + "فضای %1$d" + "فضاهای %1$d" + "آغازیدن گپ…" "عکس برگردان" "موفّقیت" + "پیشنهادی" "پیشنهادها" "هم‌گام ساختن" "متن" "تذکّرهای سوم‌شخص" "رشته" + "موضوعات" "موضوع" "این اتاق دربارهٔ چیست؟" "ناتوان در رمزگشایی" + "ارسال شده از یک دستگاه ناامن" "به این پیام دسترسی ندارید" + "هویت دیجیتال تأیید شده فرستنده بازنشانی شد" "دعوت‌ها نتوانستند به کاربرانی برسند." "ناتوان در فرستادن دعوت(ها)" "قفل‌گشایی" "باصدا" "تماس پشتیبانی نشده" "رویداد پشتیبانی نشده" + "%1$s یک تماس را آغاز کرد" "نام کاربری" "تأیید لغو شد" "تأیید کامل شد" @@ -328,17 +435,40 @@ "تأیید کاربر" "ویدیو" "کیفیت بالا" + "بهترین کیفیت اما حجم فایل بیشتر" "کیفیت پایین" + "سریع‌ترین سرعت آپلود و کمترین حجم فایل" "کیفیت استاندارد" + "تعادل بین کیفیت و سرعت آپلود" "پیام صوتی" "در انتظار…" "در انتظار این پیام" + "در انتظار موقعیت مکانی زنده…" + "هر کسی می‌تواند تاریخچه را ببیند" "شما" - "هویت %1$s بازنشانی شد. %2$s" + "دانلود در دسترس نیست." + "چیزی اشتباه پیش رفت" + "اسکن…" + "پیش‌نمایش و دانلود غیرفعال شده‌اند." + "فایل امن نیست" + "%1$s (%2$s) این پیام را به اشتراک گذاشته است زیرا شما هنگام ارسال آن در اتاق حضور نداشتید." + "%1$s این پیام را به اشتراک گذاشت زیرا شما هنگام ارسال آن در اتاق نبودید." + "این اتاق طوری پیکربندی شده است که اعضای جدید بتوانند تاریخچه را بخوانند. %1$s" + "هویت دیجیتال %1$s بازنشانی شد. %2$s" + "هویت دیجیتال %2$s مربوط به %1$s بازنشانی شد. %3$s" "(%1$s)" + "هویت دیجیتال %1$s بازنشانی شد." + "هویت دیجیتال %2$s مربوط به %1$s بازنشانی شد. %3$s" "انصراف از تأیید" + "اجازه دسترسی" + "لینک %1$s شما را به سایت دیگری هدایت می‌کند %2$s + +آیا مطمئنید که می‌خواهید ادامه دهید؟" "این پیوند را دوباره بررسی کنید" + "کیفیت پیش‌فرض ویدیوهایی که آپلود می‌کنید را انتخاب کنید." "کیفیت بارگذاری ویدیو" + "حداکثر اندازه فایل مجاز: %1$s" + "حجم فایل برای آپلود بسیار زیاد است" "اتاق گزارش شد" "گزارش و از اتاق خارج شد" "تایید" @@ -348,35 +478,75 @@ "تغییراتی ذخیره نشده دارید." "تغییراتتان ذخیره نشده‌اند. مطمئنید که می‌خواهید برگردید؟" "ذخیرهٔ تغییرات؟" + "حداکثر اندازه فایل مجاز: %1$s" + "کیفیت ویدیویی که می‌خواهید آپلود کنید را انتخاب کنید." + "کیفیت آپلود ویدیو را انتخاب کنید" + "شما قبلاً با نام کاربری %1$s در این دستگاه وارد سیستم شده‌اید." + "سرور خانگی شما باید ارتقا یابد تا از سرویس احراز هویت ماتریکس و ایجاد حساب کاربری پشتیبانی کند." "شکست در ایجاد پایاپیوند" "%1$s نتوانست نقشه را بارگیری کند. لطفا بعدا دوباره امتحان کنید." "شکست در بار کردن پیام‌ها" "‏%1$s نمی تواند به مکانتان دسنترسی داشته باشد. لطفا بعدا دوباره امتحان کنید." "شکست در بارگذاری پیام صوتیتان." + "اتاق دیگر وجود ندارد یا دعوتنامه دیگر معتبر نیست." + "برای دسترسی به ویژگی‌های مبتنی بر موقعیت مکانی، لطفاً GPS خود را فعال کنید." "پیام پیدا نشد" "‏%1$s اجازهٔ دسترسی به مکانتان را ندارد. می توانید دسترسی را در تنظیمات به کار بیندازید." "%1$s اجازهٔ دسترسی به مکانتان را ندارد. دسترسی را در زیر به کار بیندازید." "%1$s اجازه دسترسی به میکروفون شما را ندارد. دسترسی را برای ضبط پیام صوتی فعال کنید." + "این مشکل ممکن است به دلیل مشکلات شبکه یا سرور باشد." + "این آدرس اتاق از قبل وجود دارد. لطفاً فیلد آدرس اتاق را ویرایش کنید یا نام اتاق را تغییر دهید." + "بعضی از کاراکترها مجاز نیستند. فقط حروف، ارقام و نمادهای زیر پشتیبانی می‌شوند! $ &amp; ' ( ) * + / ; = ? @ [ ] - . _" "برخی پیام‌ها ارسال نشده‌اند" "متأسفیم ، خطایی رخ داد" "🔐️ پییوستن به من روی %1$s" "درود. با من روی %1$s صحبت کن: %2$s" + "اشتراک‌گذاری موقعیت مکانی زنده" + "اشتراک‌گذاری موقعیت مکانی در حال انجام است" + "موقعیت مکانی زنده %1$s" "%1$s اندروید" "Rageshake برای گزارش اشکال" "نماگرفت" "%1$s: %2$s" "گزینه‌ها" + "حذف %1$s" "تنظیمات" + "تعداد آرای مجاز برای هر نفر" + "هیچ‌کس موقعیت مکانی خود را به اشتراک نمی‌گذارد" + "اشتراک‌گذاری موقعیت مکانی زنده" + + "شخص %1$d" + "افراد %1$d" + + "روی نقشه" + "سرور خانگی شما در شبکه محلی شما قرار دارد. برای اتصال، %1$s برای دسترسی به دستگاه‌های این شبکه به مجوز نیاز دارد." + "اجازه دسترسی به شبکه محلی" "گزینش رسانه شکست خورد. لطفاً دوباره تلاش کنید." + "روی یک پیام کلیک کنید و «%1$s» را برای گنجاندن در اینجا انتخاب کنید." + "پیام‌های مهم را سنجاق کنید تا به راحتی قابل مشاهده باشند" + + "پیام پین شده %1$d" + "پیام‌های پین‌شده %1$d" + "پیام‌های سنجاق شده" - "داردید برای بازنشانی هویتتان به حساب %1$s می‌روید. پس از آن به کاره برگردانده خواهید شد." + "شما در شرف رفتن به حساب %1$s خود برای تنظیم مجدد هویت دیجیتال خود هستید. پس از آن به برنامه بازگردانده خواهید شد." + "نمی‌توانید تأیید کنید؟ برای بازنشانی هویت دیجیتال خود به حساب کاربری خود بروید." + "تأیید را لغو و ارسال کنید" + "شما می‌توانید تأیید خود را پس بگیرید و در هر صورت این پیام را ارسال کنید، یا می‌توانید فعلاً لغو کنید و پس از تأیید مجدد %1$s، بعداً دوباره امتحان کنید." + "پیام شما ارسال نشد زیرا هویت دیجیتال تأیید شده %1$s بازنشانی شده است." "فرستادن پیام به هر روی" + "%1$s از یک یا چند دستگاه تأیید نشده استفاده می‌کند. در هر صورت می‌توانید پیام را ارسال کنید، یا می‌توانید فعلاً لغو کنید و بعداً پس از تأیید تمام دستگاه‌های %2$s دوباره امتحان کنید." + "پیام شما ارسال نشد زیرا %1$s همه دستگاه‌ها را تأیید نکرده است." + "یک یا چند دستگاه شما تأیید نشده‌اند. در هر صورت می‌توانید پیام را ارسال کنید، یا می‌توانید فعلاً لغو کنید و بعداً پس از تأیید همه دستگاه‌هایتان دوباره امتحان کنید." + "پیام شما ارسال نشد زیرا یک یا چند دستگاه خود را تأیید نکرده‌اید" "ویرایش مدیران یا مالکان" "پردازش رسانه برای بارگذاری شکست خورد. لطفاً دوباره تلاش کنید." "نمی توان جزئیات کاربر را بازیابی کرد" "پیام در %1$s" "گسترش" "کاهش" + "اشتراک گذاری موقعیت مکانی زنده" + "درحال مشاهده این اتاق هستید!" "%1$s از %2$s" "%1$s پیام‌های سنجاق شده" "بار کردن پیام‌ها…" @@ -388,13 +558,30 @@ "گشودن در نقشه‌های گوگل" "گشودن در اوپن‌استریت‌مپ" "هم‌رسانی مکان گزیده" + "گزینه‌های اشتراک‌گذاری" "فضاهایی که ساخته یا پیوسته‌اید." "%1$s • %2$s" + "ایجاد فضاهایی برای سازماندهی اتاق‌ها" "‏%1$s فضا" "فضاها" + "%1$s اشتراک‌گذاری شده" + "روی نقشه" + + "%1$d عضو شد" + "%1$d عضو شد" + + "پیام ارسال نشد زیرا هویت دیجیتال تأیید شده %1$s بازنشانی شده است." + "پیام ارسال نشد زیرا %1$s همه دستگاه‌ها را تأیید نکرده است." + "پیام ارسال نشد زیرا شما یک یا چند دستگاه خود را تأیید نکرده‌اید." "مکان" "نگارش : %1$s (%2$s)" "fa" "en" + "پیام‌های تاریخی در این دستگاه موجود نیست" + "برای دسترسی به پیام‌های قدیمی، باید این دستگاه را تأیید کنید" "به این پیام دسترسی ندارید" + "رمزگشایی پیام ممکن نیست" + "این پیام یا به این دلیل مسدود شده است که شما دستگاه خود را تأیید نکرده‌اید یا به این دلیل که فرستنده باید هویت دیجیتال شما را تأیید کند." + "دستگاه شما خیلی قدیمی است، دستگاهی با اندروید ۸ یا جدیدتر مورد نیاز است." + "رندر کردن نقشه‌ها پشتیبانی نمی‌شود" diff --git a/libraries/ui-strings/src/main/res/values-fi/translations.xml b/libraries/ui-strings/src/main/res/values-fi/translations.xml index 52d3bf16e5..b1cc4889a8 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -8,6 +8,7 @@ "Pienennä viestin tekstikenttä" "Vähennä sallittujen äänien määrää per henkilö" "Lisää sallittujen äänien määrää per henkilö" + "Mukautettu emoji" "Poista" "%1$d numero syötetty" @@ -18,11 +19,21 @@ "Täysi osoite tulee olemaan %1$s" "Salauksen tiedot" "Laajenna viestin tekstikenttä" + "Galleria, liite %1$d" + "Galleria, epäonnistunut skannaus %1$d" + "Galleria, kuva %1$d" + + "Galleria, %1$d media lisää" + "Galleria, %1$d mediaa lisää" + + "Galleria, skannataan %1$d" + "Galleria, video %1$d" "Piilota salasana" "Tiedot" "Liity puheluun" "Siirry loppuun" "Siirry lukemattomiin" + "Siirry ensimmäiseen lukemattomaan viestiin" "Siirrä kartta sijaintiini" "Vain maininnat" "Mykistetty" @@ -63,6 +74,7 @@ "Aloita puhelu" "Aloita videopuhelu" "Aloita äänipuhelu" + "Tila" "Viestiketju huoneessa %1$s" "Viestiketjut huoneessa %1$s" "Haudattu huone" @@ -143,6 +155,7 @@ "Hallitse tiliä ja laitteita" "Hallitse laitteita" "Huoneiden hallitseminen" + "Merkitse luetuksi" "Lähetä viesti" "Pienennä" "Seuraava" @@ -503,6 +516,14 @@ Haluatko varmasti jatkaa?" "%1$s Android" "Raivostunut ravistaminen ilmoittaa virheestä" "Näyttökuva" + "Kirjaudu sisään" + "Luo tili" + "Jos tilisi on yrityksesi tai yhteisön tarjoama, vaihda palveluntarjoaja (esimerkki: companyname.com)." + "Luo tili matrix.org -palvelimelle ilmaiseksi. Jos haluat luoda tilin eri palveluntarjoajan kautta, muuta sitä vastaavasti (esimerkiksi: communityname.org)." + "Palveluntarjoaja" + "example.com" + "Anna palveluntarjoajasi" + "Valitse palveluntarjoaja" "%1$s: %2$s" "Vaihtoehdot" "Poista %1$s" @@ -515,6 +536,8 @@ Haluatko varmasti jatkaa?" "%1$d henkilöä" "Kartalla" + "Kotipalvelimesi on paikallisverkossasi. Yhdistääksesi, %1$s tarvitsee luvan tavoittaa laitteita tässä verkossa." + "Salli pääsy paikallisverkkoon" "Median valinta epäonnistui, yritä uudelleen." "Paina viestiä ja valitse “%1$s” lisätäksesi sen tänne." "Kiinnitä tärkeät viestit, jotta ne löytyvät helposti." 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 8d550ae51e..105ced1ad5 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -6,6 +6,9 @@ "Avatar" "Effacer le champ de recherche" "Réduire la taille du composeur" + "Diminuer le nombre de votes autorisés par personne" + "Augmenter le nombre de votes autorisés par personne" + "Émojis personnalisés" "Supprimer" "%1$d chiffre saisi" @@ -16,11 +19,21 @@ "L’adresse complète sera %1$s" "Détails du chiffrement" "Augmenter la taille du composeur" + "Galerie, élément %1$d" + "Galerie, échec de l’analyse de l’élément %1$d" + "Galerie, image %1$d" + + "Galerie, %1$d média de plus" + "Galerie, %1$d média de plus" + + "Galerie, analyse en cours de l’élément %1$d" + "Galerie, vidéo %1$d" "Masquer le mot de passe" "Info" "Rejoindre l’appel" "Retourner à la fin de la conversation" "Aller aux messages non lus" + "Aller au premier message non lu" "Déplacer la carte vers ma position" "Mentions uniquement" "En sourdine" @@ -31,6 +44,7 @@ "Page %1$d" "Pause" "Message vocal, durée: %1$s, position actuelle: %2$s" + "Aperçu de la photo" "Code PIN" "Position épinglée" "Lecture" @@ -41,12 +55,13 @@ "Code QR" "Réagir avec %1$s" "Réagir avec d’autres émojis" - "Lu par %1$s et %2$s" + "Message précédent lu par %1$s et %2$s" - "Lu par %1$s et %2$d autre" - "Lu par %1$s et %2$d autres" + "Message précédent lu par %1$s et %2$d autre" + "Message précédent lu par %1$s et %2$d autres" - "Lu par %1$s" + "Message précédent lu par %1$s" + "afficher tous les accusés de réception" "Supprimer la réaction avec %1$s" "Supprimer la réaction avec %1$s" "Avatar du salon" @@ -59,11 +74,13 @@ "Démarrer un appel" "Passer un appel vidéo" "Lancer un appel vocal" + "Statut" "Discussion dans %1$s" "Discussions dans %1$s" "Salon clôturé" "Avatar de l’utilisateur" "Menu utilisateur" + "Aperçu vidéo" "Voir l’avatar" "Afficher les détails" "Message vocal, durée: %1$s" @@ -138,6 +155,7 @@ "Gérer le compte et les appareils" "Gérez les sessions" "Gérer les salons" + "Marquer comme lu" "Message" "Minimiser" "Suivant" @@ -192,9 +210,11 @@ "Traduire" "Essayer à nouveau" "Désépingler" + "Appel vidéo" "Voir" "Voir dans la discussion" "Afficher la source" + "Appel audio" "Oui" "Oui, réessayez" "Votre serveur prend désormais en charge un nouveau protocole plus rapide. Déconnectez-vous, puis reconnectez-vous pour effectuer la mise à niveau dès maintenant. En le faisant tout de suite, vous éviterez une déconnexion forcée lorsque l’ancien protocole sera supprimé." @@ -215,6 +235,7 @@ "Utilisateurs bloqués" "Bulles" "Appel rejeté" + "Appel en cours" "Appel démarré" "Vous avez rejeté un appel" "Sauvegarde des discussions" @@ -258,7 +279,17 @@ Raison : %1$s." "Fichier enregistré dans Téléchargements" "Transférer le message" "Fréquemment utilisé" + "Galerie" + + "%1$d élément…" + "%1$d éléments…" + + + "%1$d élément multimédia…" + "%1$d éléments multimédia…" + "GIF" + "Appel de groupe en cours" "Image" "En réponse à %1$s" "Installer l’APK" @@ -294,6 +325,7 @@ Raison : %1$s." "Espace sans nom" "Non chiffré" "Hors ligne" + "En appel" "Licences open source" "ou" "Autres options" @@ -401,6 +433,7 @@ Raison : %1$s." "Retirer la sourdine" "Appel non pris en charge" "Événement non pris en charge" + "%1$s a commencé un appel" "Nom d’utilisateur" "Vérification annulée" "Vérification terminée" @@ -422,6 +455,11 @@ Raison : %1$s." "En attente de la position en temps réel…" "Tout le monde peut voir les anciens messages" "Vous" + "Téléchargement indisponible." + "Une erreur est survenue" + "Analyse en cours…" + "L’aperçu et le téléchargement ont été désactivés." + "Le fichier n’est pas sûr" "%1$s (%2$s) a partagé ce message avec vous car vous n’étiez pas dans le salon lors de son envoi." "%1$s a partagé ce message avec vous car vous n’étiez pas dans le salon lors de son envoi." "Ce salon a été configuré pour que les nouveaux membres puissent lire l’historique. %1$s" @@ -478,10 +516,19 @@ Raison : %1$s." "%1$s Android" "Rageshake pour signaler un problème" "Capture d’écran" + "Se connecter" + "Créer un compte" + "Si votre fournisseur de compte est fourni par votre entreprise ou une communauté, veuillez le saisir ici (exemple : monentreprise.com)." + "Créez un compte sur matrix.org gratuitement. Si vous souhaitez créer un compte auprès d’un autre fournisseur, veuillez modifier l’adresse ici (exemple : monassociation.org)." + "Fournisseur de compte" + "exemple.com" + "Saisissez votre fournisseur de compte" + "Choisissez un fournisseur de compte" "%1$s: %2$s" "Options" "Supprimer %1$s" "Paramètres" + "Nombre de votes autorisés par personne" "Personne ne partage sa position" "Partage de la position en direct" @@ -489,6 +536,8 @@ Raison : %1$s." "%1$d personnes" "Sur la carte" + "Votre fournisseur de compte se trouve sur votre réseau local. Pour pouvoir communiquer, %1$s a besoin de l’autorisation d’accéder aux appareils de ce réseau." + "Autoriser l’accès au réseau local" "Échec de la sélection du média, veuillez réessayer." "Cliquez (clic long) sur un message et choisissez « %1$s » pour qu‘il apparaisse ici." "Épinglez les messages importants pour leur donner plus de visibilité" @@ -534,6 +583,10 @@ Raison : %1$s." "Espaces" "Partagé %1$s" "Sur la carte" + + "%1$d participant" + "%1$d participants" + "Le message n’a pas été envoyé car l’identité numérique vérifiée de %1$s a été réinitialisée." "Le message n’a pas été envoyé car %1$s n’a pas vérifié tous ses appareils." "Message non envoyé car vous n’avez pas vérifié tous vos appareils." From 5ac1fe24098815bab0833f3841a06d6257e3f4f3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 17 Aug 2026 11:35:56 +0200 Subject: [PATCH 220/338] Remove temporary string. --- .../impl/src/main/res/values/temporary.xml | 10 ---------- libraries/push/impl/src/main/res/values/temporary.xml | 11 ----------- 2 files changed, 21 deletions(-) delete mode 100644 features/roomdetails/impl/src/main/res/values/temporary.xml delete mode 100644 libraries/push/impl/src/main/res/values/temporary.xml diff --git a/features/roomdetails/impl/src/main/res/values/temporary.xml b/features/roomdetails/impl/src/main/res/values/temporary.xml deleted file mode 100644 index b3194f3323..0000000000 --- a/features/roomdetails/impl/src/main/res/values/temporary.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - Mute - diff --git a/libraries/push/impl/src/main/res/values/temporary.xml b/libraries/push/impl/src/main/res/values/temporary.xml deleted file mode 100644 index 494f2ee441..0000000000 --- a/libraries/push/impl/src/main/res/values/temporary.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - "Requested to join" - From 054d592391b4d818528754afc5d153ed2a57216e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 17 Aug 2026 12:10:39 +0200 Subject: [PATCH 221/338] Sync again --- features/roomdetails/impl/src/main/res/values/localazy.xml | 2 +- libraries/push/impl/src/main/res/values-zh/translations.xml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 8f86015147..05bdb50d6e 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -114,7 +114,7 @@ "Failed setting the mode, please try again." "Your homeserver does not support this option in encrypted rooms, you won\'t get notified in this room." "All messages" - "Mentions and Keywords only" + "Mentions and replies only" "Mute" "In this room, notify me for" "Admins" diff --git a/libraries/push/impl/src/main/res/values-zh/translations.xml b/libraries/push/impl/src/main/res/values-zh/translations.xml index eacd1a2040..bbdbabd350 100644 --- a/libraries/push/impl/src/main/res/values-zh/translations.xml +++ b/libraries/push/impl/src/main/res/values-zh/translations.xml @@ -29,6 +29,7 @@ "已邀请你聊天" "%1$s 已邀请你聊天" + "已申请加入" "已开始共享其实时位置" "提到了你:%1$s" "新消息" @@ -92,6 +93,7 @@ "错误:%1$s。" "错误,无法测试推送。" "错误,等待推送超时。" + "错误:推送网关已对此设备限流,因此通知可能不稳定。请更换其它推送网关。" "推送回路耗时 %1$d 毫秒。" "测试推送回路" From 98ef737b1d42eb2147a31e2f45e2198751c47783 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 15:52:46 +0300 Subject: [PATCH 222/338] Rename withDefaultSubtype to ensureDefaultSubtype The old name read as though the default subtype always replaced whatever the receiver carried, when a mime type that already has a concrete subtype is returned untouched. --- .../libraries/core/mimetype/MimeTypes.kt | 2 +- .../libraries/core/mimetype/MimeTypesTest.kt | 26 +++++++++---------- .../matrix/impl/media/RustMediaLoader.kt | 4 +-- .../impl/AndroidMediaPreProcessor.kt | 4 +-- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt index dee840ea48..3901a32059 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt @@ -68,7 +68,7 @@ object MimeTypes { * A mime type with a wildcard subtype is a filter, not a media type, so replace it with a * concrete default for its top level type. */ - fun String?.withDefaultSubtype(): String = when { + fun String?.ensureDefaultSubtype(): String = when { this == null -> OctetStream hasSubtype(this) -> this this == Images -> Jpeg diff --git a/libraries/core/src/test/kotlin/io/element/android/libraries/core/mimetype/MimeTypesTest.kt b/libraries/core/src/test/kotlin/io/element/android/libraries/core/mimetype/MimeTypesTest.kt index 7ba68fa9b1..f12b73b40b 100644 --- a/libraries/core/src/test/kotlin/io/element/android/libraries/core/mimetype/MimeTypesTest.kt +++ b/libraries/core/src/test/kotlin/io/element/android/libraries/core/mimetype/MimeTypesTest.kt @@ -8,28 +8,28 @@ package io.element.android.libraries.core.mimetype import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.core.mimetype.MimeTypes.withDefaultSubtype +import io.element.android.libraries.core.mimetype.MimeTypes.ensureDefaultSubtype import org.junit.Test class MimeTypesTest { @Test - fun `withDefaultSubtype keeps a mime type that already has a subtype`() { - assertThat(MimeTypes.Png.withDefaultSubtype()).isEqualTo(MimeTypes.Png) - assertThat(MimeTypes.Mp4.withDefaultSubtype()).isEqualTo(MimeTypes.Mp4) - assertThat(MimeTypes.Pdf.withDefaultSubtype()).isEqualTo(MimeTypes.Pdf) + fun `ensureDefaultSubtype keeps a mime type that already has a subtype`() { + assertThat(MimeTypes.Png.ensureDefaultSubtype()).isEqualTo(MimeTypes.Png) + assertThat(MimeTypes.Mp4.ensureDefaultSubtype()).isEqualTo(MimeTypes.Mp4) + assertThat(MimeTypes.Pdf.ensureDefaultSubtype()).isEqualTo(MimeTypes.Pdf) } @Test - fun `withDefaultSubtype replaces a wildcard subtype with a concrete one`() { - assertThat(MimeTypes.Images.withDefaultSubtype()).isEqualTo(MimeTypes.Jpeg) - assertThat(MimeTypes.Videos.withDefaultSubtype()).isEqualTo(MimeTypes.Mp4) - assertThat(MimeTypes.Audio.withDefaultSubtype()).isEqualTo(MimeTypes.Mp3) + fun `ensureDefaultSubtype replaces a wildcard subtype with a concrete one`() { + assertThat(MimeTypes.Images.ensureDefaultSubtype()).isEqualTo(MimeTypes.Jpeg) + assertThat(MimeTypes.Videos.ensureDefaultSubtype()).isEqualTo(MimeTypes.Mp4) + assertThat(MimeTypes.Audio.ensureDefaultSubtype()).isEqualTo(MimeTypes.Mp3) } @Test - fun `withDefaultSubtype falls back to octet stream`() { - assertThat(MimeTypes.Any.withDefaultSubtype()).isEqualTo(MimeTypes.OctetStream) - assertThat("text/*".withDefaultSubtype()).isEqualTo(MimeTypes.OctetStream) - assertThat(null.withDefaultSubtype()).isEqualTo(MimeTypes.OctetStream) + fun `ensureDefaultSubtype falls back to octet stream`() { + assertThat(MimeTypes.Any.ensureDefaultSubtype()).isEqualTo(MimeTypes.OctetStream) + assertThat("text/*".ensureDefaultSubtype()).isEqualTo(MimeTypes.OctetStream) + assertThat(null.ensureDefaultSubtype()).isEqualTo(MimeTypes.OctetStream) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt index 3231c29403..7b3a1d9f10 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt @@ -11,7 +11,7 @@ package io.element.android.libraries.matrix.impl.media import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.extensions.runCatchingExceptions -import io.element.android.libraries.core.mimetype.MimeTypes.withDefaultSubtype +import io.element.android.libraries.core.mimetype.MimeTypes.ensureDefaultSubtype import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaFile import io.element.android.libraries.matrix.api.media.MediaSource @@ -72,7 +72,7 @@ class RustMediaLoader( mediaSource = mediaSource, filename = filename, // Fallback to a default mime type based on the main type, so that the SDK can create a file with the correct extension. - mimeType = mimeType.withDefaultSubtype(), + mimeType = mimeType.ensureDefaultSubtype(), useCache = useCache, tempDir = cacheDirectory.path, ) diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt index 4ef8a2410c..d35fdb6189 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt @@ -26,10 +26,10 @@ import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.core.mimetype.MimeTypes.ensureDefaultSubtype import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo -import io.element.android.libraries.core.mimetype.MimeTypes.withDefaultSubtype import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.FileInfo @@ -83,7 +83,7 @@ class AndroidMediaPreProcessor( mediaOptimizationConfig: MediaOptimizationConfig, ): Result = withContext(coroutineDispatchers.computation) { runCatchingExceptions { - val resolvedMimeType = mimeType.withDefaultSubtype() + val resolvedMimeType = mimeType.ensureDefaultSubtype() val result = when { resolvedMimeType == MimeTypes.Svg -> processSvgImage(uri, resolvedMimeType) resolvedMimeType.isMimeTypeImage() -> { From 12791406ca5ec1b7a82f62f6679e2d89c3cbc21b Mon Sep 17 00:00:00 2001 From: hayyaksi <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 16:14:15 +0300 Subject: [PATCH 223/338] Explain a rate limiting push gateway in the notification troubleshooter (#7448) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Explain a rate limiting push gateway in the notification troubleshooter The push loop back test printed whatever HTTP status the gateway answered, so a user throttled by the public ntfy.sh gateway was told "Error: HTTP 507", which reads as a server fault rather than as a quota they can wait out or move away from. Map the two statuses that mean the notification was throttled onto a typed failure and give the troubleshooter a message that says what to do. * Import strings from localazy, remove `temporary.xml` in `:libraries:push:impl` --------- Co-authored-by: Jorge Martín --- .../push/api/gateway/PushGatewayFailure.kt | 5 ++ .../pushgateway/PushGatewayNotifyRequest.kt | 43 ++++++++++----- .../impl/troubleshoot/PushLoopbackTest.kt | 7 +++ .../DefaultPushGatewayNotifyRequestTest.kt | 55 +++++++++++++++++++ .../impl/troubleshoot/PushLoopbackTestTest.kt | 19 +++++++ 5 files changed, 114 insertions(+), 15 deletions(-) diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/gateway/PushGatewayFailure.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/gateway/PushGatewayFailure.kt index 975e10e134..3bf065550d 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/gateway/PushGatewayFailure.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/gateway/PushGatewayFailure.kt @@ -10,4 +10,9 @@ package io.element.android.libraries.push.api.gateway sealed class PushGatewayFailure : Exception() { class PusherRejected : PushGatewayFailure() + + /** + * The gateway refused the notification because too many were sent to it. + */ + class RateLimited : PushGatewayFailure() } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt index 6315285559..e3a7968909 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt @@ -12,6 +12,7 @@ import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.push.api.gateway.PushGatewayFailure +import retrofit2.HttpException interface PushGatewayNotifyRequest { data class Params( @@ -33,26 +34,38 @@ class DefaultPushGatewayNotifyRequest( val pushGatewayApi = pushGatewayApiFactory.create( params.url.substringBefore(PushGatewayConfig.URI_PUSH_GATEWAY_PREFIX_PATH) ) - val response = pushGatewayApi.notify( - PushGatewayNotifyBody( - PushGatewayNotification( - eventId = params.eventId.value, - roomId = params.roomId.value, - devices = listOf( - PushGatewayDevice( - params.appId, - params.pushKey, - PusherData(mapOf( - "cs" to "A_FAKE_SECRET", - )) - ) - ), + val response = try { + pushGatewayApi.notify( + PushGatewayNotifyBody( + PushGatewayNotification( + eventId = params.eventId.value, + roomId = params.roomId.value, + devices = listOf( + PushGatewayDevice( + params.appId, + params.pushKey, + PusherData(mapOf( + "cs" to "A_FAKE_SECRET", + )) + ) + ), + ) ) ) - ) + } catch (httpException: HttpException) { + if (httpException.code() in RATE_LIMITED_STATUS_CODES) { + throw PushGatewayFailure.RateLimited() + } + throw httpException + } if (response.rejectedPushKeys.contains(params.pushKey)) { throw PushGatewayFailure.PusherRejected() } } } + +/** + * 429 is the standard answer, ntfy answers 507 when a device is over its message quota. + */ +private val RATE_LIMITED_STATUS_CODES = setOf(429, 507) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTest.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTest.kt index bc89d031a8..0277d7d2c8 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTest.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTest.kt @@ -62,6 +62,13 @@ class PushLoopbackTest( ) job.cancel() return + } catch (_: PushGatewayFailure.RateLimited) { + delegate.updateState( + description = stringProvider.getString(R.string.troubleshoot_notifications_test_push_loop_back_failure_rate_limit), + status = NotificationTroubleshootTestState.Status.Failure() + ) + job.cancel() + return } catch (e: Exception) { Timber.e(e, "Failed to test push") delegate.updateState( diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/DefaultPushGatewayNotifyRequestTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/DefaultPushGatewayNotifyRequestTest.kt index 47dc027b43..97f3bdc540 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/DefaultPushGatewayNotifyRequestTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/DefaultPushGatewayNotifyRequestTest.kt @@ -12,8 +12,11 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.push.api.gateway.PushGatewayFailure import io.element.android.libraries.push.impl.test.DefaultTestPush import kotlinx.coroutines.test.runTest +import okhttp3.ResponseBody.Companion.toResponseBody import org.junit.Assert.assertThrows import org.junit.Test +import retrofit2.HttpException +import retrofit2.Response class DefaultPushGatewayNotifyRequestTest { @Test @@ -64,6 +67,58 @@ class DefaultPushGatewayNotifyRequestTest { assertThat(factory.baseUrlParameter).isEqualTo("aUrl") } + @Test + fun `notify with a rate limiting status should throw expected Exception`() { + listOf(429, 507).forEach { statusCode -> + val factory = FakePushGatewayApiFactory( + notifyResponse = { + throw HttpException(Response.error(statusCode, "".toResponseBody())) + } + ) + val pushGatewayNotifyRequest = DefaultPushGatewayNotifyRequest( + pushGatewayApiFactory = factory, + ) + assertThrows(PushGatewayFailure.RateLimited::class.java) { + runTest { + pushGatewayNotifyRequest.execute( + PushGatewayNotifyRequest.Params( + url = "aUrl", + appId = "anAppId", + pushKey = "aPushKey", + eventId = DefaultTestPush.TEST_EVENT_ID, + roomId = DefaultTestPush.TEST_ROOM_ID, + ) + ) + } + } + } + } + + @Test + fun `notify with another http error should not be mapped to a rate limit`() { + val factory = FakePushGatewayApiFactory( + notifyResponse = { + throw HttpException(Response.error(500, "".toResponseBody())) + } + ) + val pushGatewayNotifyRequest = DefaultPushGatewayNotifyRequest( + pushGatewayApiFactory = factory, + ) + assertThrows(HttpException::class.java) { + runTest { + pushGatewayNotifyRequest.execute( + PushGatewayNotifyRequest.Params( + url = "aUrl", + appId = "anAppId", + pushKey = "aPushKey", + eventId = DefaultTestPush.TEST_EVENT_ID, + roomId = DefaultTestPush.TEST_ROOM_ID, + ) + ) + } + } + } + @Test fun `notify with rejected push key should throw expected Exception`() { val factory = FakePushGatewayApiFactory( diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTestTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTestTest.kt index f83fb660ce..447fdd7eb5 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTestTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/PushLoopbackTestTest.kt @@ -59,6 +59,25 @@ class PushLoopbackTestTest { } } + @Test + fun `test PushLoopbackTest RateLimited error`() = runTest { + val sut = createPushLoopbackTest( + pushService = FakePushService( + testPushBlock = { + throw PushGatewayFailure.RateLimited() + } + ), + stringProvider = FakeStringProvider(A_FAILURE_REASON), + ) + sut.runAndTestState { + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress) + val lastItem = awaitItem() + assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure()) + assertThat(lastItem.description).isEqualTo(A_FAILURE_REASON) + } + } + @Test fun `test PushLoopbackTest PusherRejected error with quick fix`() = runTest { val rotateTokenLambda = lambdaRecorder> { Result.success(Unit) } From f94566e44f1a51813410f6299248355fcb721587 Mon Sep 17 00:00:00 2001 From: Cipherd Date: Mon, 17 Aug 2026 13:14:45 +0000 Subject: [PATCH 224/338] Fix outgoing session verification stuck with incomplete recovery setup (#7433) --- .../OutgoingVerificationStateMachine.kt | 9 ++- .../OutgoingVerificationPresenterTest.kt | 80 +++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt index 465fc20066..c97bf2cd2e 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt @@ -15,7 +15,6 @@ import com.freeletics.flowredux.dsl.FlowReduxStateMachine import io.element.android.features.verifysession.impl.util.andLogStateChange import io.element.android.features.verifysession.impl.util.logReceivedEvents import io.element.android.libraries.core.bool.orFalse -import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.verification.SessionVerificationData @@ -23,6 +22,7 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationS import io.element.android.libraries.matrix.api.verification.VerificationRequest import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.timeout @@ -95,10 +95,13 @@ class OutgoingVerificationStateMachine( // If a key backup exists, wait until it's restored or a timeout happens val hasBackup = encryptionService.doesBackupExistOnServer().getOrNull().orFalse() if (hasBackup) { - tryOrNull { - encryptionService.recoveryStateStateFlow.filter { it == RecoveryState.ENABLED } + try { + encryptionService.recoveryStateStateFlow + .filter { it == RecoveryState.ENABLED || it == RecoveryState.DISABLED || it == RecoveryState.INCOMPLETE } .timeout(10.seconds) .first() + } catch (_: TimeoutCancellationException) { + // The backup was not restored in time, proceed to the Completed state anyway } } state.override { State.Completed.andLogStateChange() } diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt index c6cbfca281..577683d12e 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt @@ -16,6 +16,7 @@ import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificat import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.EncryptionService +import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.verification.SessionVerificationData import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus @@ -216,6 +217,85 @@ class OutgoingVerificationPresenterTest { } } + @Test + fun `present - When verification is approved and a backup exists but recovery is disabled, the flow completes`() = runTest { + val service = unverifiedSessionService( + requestUserVerificationLambda = { }, + startSasVerificationLambda = { }, + approveVerificationLambda = { }, + ) + val encryptionService = FakeEncryptionService().apply { + givenDoesBackupExistOnServerResult(Result.success(true)) + recoveryStateStateFlow.value = RecoveryState.DISABLED + } + val presenter = createOutgoingVerificationPresenter( + service = service, + verificationRequest = anOutgoingUserVerificationRequest(), + encryptionService = encryptionService, + ) + presenter.test { + val state = requestVerificationAndAwaitVerifyingState(service) + state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) + assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) + service.emitVerificationFlowState(VerificationFlowState.DidFinish) + assertThat(awaitItem().step).isEqualTo(Step.Completed) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - When verification is approved and a backup exists but recovery is incomplete, the flow completes`() = runTest { + val service = unverifiedSessionService( + requestUserVerificationLambda = { }, + startSasVerificationLambda = { }, + approveVerificationLambda = { }, + ) + val encryptionService = FakeEncryptionService().apply { + givenDoesBackupExistOnServerResult(Result.success(true)) + recoveryStateStateFlow.value = RecoveryState.INCOMPLETE + } + val presenter = createOutgoingVerificationPresenter( + service = service, + verificationRequest = anOutgoingUserVerificationRequest(), + encryptionService = encryptionService, + ) + presenter.test { + val state = requestVerificationAndAwaitVerifyingState(service) + state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) + assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) + service.emitVerificationFlowState(VerificationFlowState.DidFinish) + assertThat(awaitItem().step).isEqualTo(Step.Completed) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - When verification is approved but the recovery state is not determined, the flow completes after the timeout`() = runTest { + val service = unverifiedSessionService( + requestUserVerificationLambda = { }, + startSasVerificationLambda = { }, + approveVerificationLambda = { }, + ) + val encryptionService = FakeEncryptionService().apply { + givenDoesBackupExistOnServerResult(Result.success(true)) + } + val presenter = createOutgoingVerificationPresenter( + service = service, + verificationRequest = anOutgoingUserVerificationRequest(), + encryptionService = encryptionService, + ) + presenter.test { + val state = requestVerificationAndAwaitVerifyingState(service) + state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) + assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) + service.emitVerificationFlowState(VerificationFlowState.DidFinish) + // The recovery state never leaves UNKNOWN, so the 10 seconds timeout fires and the flow completes anyway + this@runTest.advanceUntilIdle() + assertThat(awaitItem().step).isEqualTo(Step.Completed) + cancelAndIgnoreRemainingEvents() + } + } + @Test fun `present - When verification is declined, the flow is canceled`() = runTest { val service = unverifiedSessionService( From 11e3cb9b19f59a661d48ef60da58ef0cac2694a1 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 16:20:43 +0300 Subject: [PATCH 225/338] Keep a focused timeline focused until the event has been rendered Opening a room from a notification asks the timeline to focus the notified event. When that event is outside the loaded live window the controller creates a detached timeline for it, and a detached timeline always carries a forward loading indicator as its last chronological item. The items list is reversed for display, so that indicator is the bottom-most row and is composed on first layout, which unconditionally emits a forward pagination request nobody asked for. Because a notified event sits close to the live end, that one page reaches the end, and the controller responds by closing the detached timeline. The live window replaces the item list while it is still anchored at the bottom, so the user lands on the newest message and the focus request is discarded. Defer automatic forward pagination while a focus request is still pending. Once the focused event has been rendered the viewport has moved off the indicator row, so only a real scroll back to the live end switches to the live timeline, which is what the controller's existing behaviour is for. Backward pagination and the explicit jump-to-live action are untouched. --- .../impl/timeline/TimelinePresenter.kt | 21 ++++- .../impl/timeline/TimelinePresenterTest.kt | 88 +++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 7cd0d018d2..3f98d2a885 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -166,9 +166,14 @@ class TimelinePresenter( fun handleEvent(event: TimelineEvent) { when (event) { is TimelineEvent.LoadMore -> { - if (event.direction == Timeline.PaginationDirection.FORWARDS && timelineMode is Timeline.Mode.Thread) { - // Do not paginate forwards in thread mode, as it's not supported - return + if (event.direction == Timeline.PaginationDirection.FORWARDS) { + if (timelineMode is Timeline.Mode.Thread) { + // Do not paginate forwards in thread mode, as it's not supported + return + } + if (focusRequestState.value.isPending()) { + return + } } localScope.launch { timelineController.paginate(direction = event.direction) @@ -537,6 +542,16 @@ private fun FocusRequestState.onFocusEventRender(): FocusRequestState { } } +private fun FocusRequestState.isPending(): Boolean { + return when (this) { + is FocusRequestState.Requested, + is FocusRequestState.Loading -> true + is FocusRequestState.Success -> !rendered + FocusRequestState.None, + is FocusRequestState.Failure -> false + } +} + // Workaround for not having the server names available, get possible server names from the user ids of the room members private fun calculateServerNamesForRoom(room: JoinedRoom): List { // If we have no room members, return right ahead diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index 3b0794305f..3b90196974 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -1059,6 +1059,69 @@ class TimelinePresenterTest { } } + @Test + fun `present - a forward pagination while the focused event is not rendered yet is ignored`() = runTest { + val paginateLambda = lambdaRecorder(ensureNeverCalled = true) { _: Timeline.PaginationDirection -> + Result.success(true) + } + val presenter = createTimelinePresenter( + room = aRoomWithDetachedTimeline(paginateLambda = paginateLambda), + ) + presenter.test { + val initialState = awaitFirstItem() + initialState.eventSink.invoke(TimelineEvent.FocusOnEvent(AN_EVENT_ID)) + val focusedState = consumeItemsUntilPredicate { it.focusRequestState == FocusRequestState.Success(AN_EVENT_ID) }.last() + assertThat(focusedState.isLive).isFalse() + initialState.eventSink.invoke(TimelineEvent.LoadMore(Timeline.PaginationDirection.FORWARDS)) + advanceUntilIdle() + assert(paginateLambda).isNeverCalled() + assertThat(expectMostRecentItem().isLive).isFalse() + } + } + + @Test + fun `present - a forward pagination is performed once the focused event has been rendered`() = runTest { + val paginateLambda = lambdaRecorder { _: Timeline.PaginationDirection -> + Result.success(true) + } + val presenter = createTimelinePresenter( + room = aRoomWithDetachedTimeline(paginateLambda = paginateLambda), + ) + presenter.test { + val initialState = awaitFirstItem() + initialState.eventSink.invoke(TimelineEvent.FocusOnEvent(AN_EVENT_ID)) + consumeItemsUntilPredicate { it.focusRequestState == FocusRequestState.Success(AN_EVENT_ID) } + initialState.eventSink.invoke(TimelineEvent.OnFocusEventRender) + initialState.eventSink.invoke(TimelineEvent.LoadMore(Timeline.PaginationDirection.FORWARDS)) + advanceUntilIdle() + assert(paginateLambda) + .isCalledOnce() + .with(value(Timeline.PaginationDirection.FORWARDS)) + assertThat(expectMostRecentItem().isLive).isTrue() + } + } + + @Test + fun `present - a backward pagination while the focused event is not rendered yet is performed`() = runTest { + val paginateLambda = lambdaRecorder { _: Timeline.PaginationDirection -> + Result.success(false) + } + val presenter = createTimelinePresenter( + room = aRoomWithDetachedTimeline(paginateLambda = paginateLambda), + ) + presenter.test { + val initialState = awaitFirstItem() + initialState.eventSink.invoke(TimelineEvent.FocusOnEvent(AN_EVENT_ID)) + consumeItemsUntilPredicate { it.focusRequestState == FocusRequestState.Success(AN_EVENT_ID) } + initialState.eventSink.invoke(TimelineEvent.LoadMore(Timeline.PaginationDirection.BACKWARDS)) + advanceUntilIdle() + assert(paginateLambda) + .isCalledOnce() + .with(value(Timeline.PaginationDirection.BACKWARDS)) + cancelAndIgnoreRemainingEvents() + } + } + @Test fun `present - focus on known event retrieves the event from cache`() = runTest { val timelineItemIndexer = TimelineItemIndexer() @@ -1552,6 +1615,31 @@ class TimelinePresenterTest { return awaitItem() } + private fun aRoomWithDetachedTimeline( + paginateLambda: (Timeline.PaginationDirection) -> Result, + ): FakeJoinedRoom { + val detachedTimeline = FakeTimeline( + timelineItems = flowOf( + listOf( + MatrixTimelineItem.Event( + uniqueId = A_UNIQUE_ID, + event = anEventTimelineItem(eventId = AN_EVENT_ID), + ) + ) + ) + ).apply { + this.paginateLambda = paginateLambda + } + return FakeJoinedRoom( + liveTimeline = FakeTimeline(timelineItems = flowOf(emptyList())), + createTimelineResult = { Result.success(detachedTimeline) }, + baseRoom = FakeBaseRoom( + roomPermissions = roomPermissions(), + threadRootIdForEventResult = { _ -> Result.success(null) }, + ), + ) + } + private fun roomPermissions( canRedactOther: Boolean = false, canRedactOwn: Boolean = true, From 8b2c2077286697e6e10f99caf3278e8d74c3fd2c Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 17 Aug 2026 15:21:47 +0200 Subject: [PATCH 226/338] Move the `:libraries:wellknown` logic into `enterprise` modules (#7429) * Extract `IsEnterpriseBuild` use case Having it inside `EnterpriseService` made having DI cycles with the `BuildMeta` provider too easy * Rename `EnterpriseService.defaultHomeserverList()` to `homeserverAllowList()` This seems more aligned with the way this list works * Move most of the logic from `:libraries:wellknown` to `:enterprise:feature:enterprise`, both `shared` and `impl` modules Ideally I would have added everything into `impl`, but some of the classes needed to be shared between the different `:enterprise:features` modules, and those are in the `shared` module, lacking a better name/strategy * Add `EnterpriseService.isElementProEnforced` With this, we don't need to provide the `RemoteEnterpriseConfig` anymore to any consumers, everything goes through `EnterpriseService` or other providers * Remove `EnterpriseService.overriddenElementWellKnown` This leaks the remote enterprise config/element well known contents to the app in a public interface. * Create `CustomMapTilerConfigProvider` to avoid leaking the `RemoteEnterpriseConfig` in public APIs This commit also starts moving some of the well-known related values to the enterprise modules * Add missing docs to `EnterpriseService` and `SessionEnterpriseService` * Fix tests * Fix lint issues * Bump enterprise module --- app/build.gradle.kts | 1 + .../io/element/android/x/di/AppModule.kt | 11 +- .../android/appnav/LoggedInFlowNode.kt | 6 +- enterprise | 2 +- .../enterprise/api/EnterpriseService.kt | 26 +- .../enterprise/api/IsEnterpriseBuild.kt | 18 ++ .../api/SessionEnterpriseService.kt | 6 +- .../CustomMapTilerConfigProvider.kt | 15 ++ .../api/remoteconfig/MapTilerConfig.kt | 19 +- .../remoteconfig/RemoteEnterpriseConfig.kt | 17 -- .../RemoteEnterpriseConfigProvider.kt | 35 --- .../RemoteEnterpriseConfigResult.kt | 15 +- .../impl/DefaultEnterpriseService.kt | 8 +- .../impl/DefaultIsEnterpriseBuild.kt | 17 ++ .../DefaultCustomMapTilerConfigProvider.kt | 18 ++ .../DefaultRemoteEnterpriseConfigProvider.kt | 30 --- .../impl/DefaultEnterpriseServiceTest.kt | 10 +- .../enterprise/test/FakeEnterpriseService.kt | 14 +- .../FakeRemoteEnterpriseConfigProvider.kt | 21 -- ...keRemoteEnterpriseConfigProviderFactory.kt | 17 -- .../enterprise/test/remoteconfig/Fixtures.kt | 22 ++ features/location/api/build.gradle.kts | 1 + .../location/api/LocalMapTilerConfig.kt | 2 +- .../features/location/api/StaticMapView.kt | 2 +- .../internal/MapTilerStaticMapUrlBuilder.kt | 2 +- .../MapTilerTileServerStyleUriBuilder.kt | 2 +- .../api/internal/StaticMapUrlBuilder.kt | 3 +- .../api/internal/TileServerStyleUriBuilder.kt | 2 +- .../MapTilerTileServerStyleUriBuilderTest.kt | 2 +- .../impl/common/ui/MapBottomSheetScaffold.kt | 2 +- .../impl/share/ShareLocationPresenter.kt | 6 +- .../location/impl/share/ShareLocationState.kt | 2 +- .../impl/share/ShareLocationStateProvider.kt | 2 +- .../impl/show/ShowLocationPresenter.kt | 11 +- .../location/impl/show/ShowLocationState.kt | 2 +- .../impl/show/ShowLocationStateProvider.kt | 2 +- .../DefaultShareLocationEntryPointTest.kt | 3 +- .../impl/share/ShareLocationPresenterTest.kt | 15 +- .../show/DefaultShowLocationEntryPointTest.kt | 5 +- .../impl/show/ShowLocationPresenterTest.kt | 28 +- .../DefaultAccountProviderAccessControl.kt | 31 +-- .../AccountProviderDataSource.kt | 2 +- .../ChangeAccountProviderPresenter.kt | 2 +- .../ChooseAccountProviderPresenter.kt | 2 +- .../screens/onboarding/OnBoardingPresenter.kt | 4 +- ...DefaultAccountProviderAccessControlTest.kt | 76 +++--- .../changeserver/ChangeServerPresenterTest.kt | 38 ++- .../onboarding/OnBoardingPresenterTest.kt | 12 +- .../developer/DeveloperSettingsPresenter.kt | 4 +- .../DeveloperSettingsPresenterTest.kt | 10 +- .../wellknown/api/CustomRecoveryPassphrase.kt | 23 -- .../wellknown/api/ElementWellKnownParser.kt | 20 -- .../wellknown/api/ElementWellknownStore.kt | 47 ---- .../api/EnterpriseRemoteConfigSource.kt | 23 -- .../wellknown/api/WellknownRetriever.kt | 39 --- .../impl/DefaultElementWellKnownParser.kt | 27 -- .../wellknown/impl/DefaultWellKnownStore.kt | 77 ------ .../impl/DefaultWellknownRetriever.kt | 142 ----------- .../impl/InternalElementWellKnown.kt | 71 ------ .../wellknown/impl/InternalWellKnown.kt | 34 --- .../impl/InternalWellKnownBaseConfig.kt | 27 -- .../libraries/wellknown/impl/Mapper.kt | 59 ----- .../wellknown/impl/SessionWellknownModule.kt | 41 --- .../libraries/wellknown/impl/WellknownAPI.kt | 19 -- .../api/CustomRecoveryPassphraseTest.kt | 33 --- .../impl/DefaultWellKnownStoreTest.kt | 174 ------------- .../impl/DefaultWellknownRetrieverTest.kt | 241 ------------------ .../test/FakeElementWellknownStore.kt | 53 ---- .../wellknown/test/FakeWellknownRetriever.kt | 24 -- .../test/FakeWellknownRetrieverFactory.kt | 17 -- .../features/wellknown/test/Fixtures.kt | 55 ---- .../posthog/PosthogEndpointConfigProvider.kt | 4 +- 72 files changed, 246 insertions(+), 1607 deletions(-) create mode 100644 features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/IsEnterpriseBuild.kt create mode 100644 features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/CustomMapTilerConfigProvider.kt rename libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt => features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/MapTilerConfig.kt (57%) delete mode 100644 features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfig.kt delete mode 100644 features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt rename libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetrieverResult.kt => features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigResult.kt (63%) create mode 100644 features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultIsEnterpriseBuild.kt create mode 100644 features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultCustomMapTilerConfigProvider.kt delete mode 100644 features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt delete mode 100644 features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProvider.kt delete mode 100644 features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt create mode 100644 features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/Fixtures.kt delete mode 100644 libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphrase.kt delete mode 100644 libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnownParser.kt delete mode 100644 libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt delete mode 100644 libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/EnterpriseRemoteConfigSource.kt delete mode 100644 libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt delete mode 100644 libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultElementWellKnownParser.kt delete mode 100644 libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt delete mode 100644 libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt delete mode 100644 libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt delete mode 100644 libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalWellKnown.kt delete mode 100644 libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalWellKnownBaseConfig.kt delete mode 100644 libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt delete mode 100644 libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt delete mode 100644 libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/WellknownAPI.kt delete mode 100644 libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseTest.kt delete mode 100644 libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt delete mode 100644 libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt delete mode 100644 libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeElementWellknownStore.kt delete mode 100644 libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt delete mode 100644 libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt delete mode 100644 libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ecd5ed8e7b..f2c7d37baa 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -259,6 +259,7 @@ dependencies { if (isEnterpriseBuild) { allEnterpriseImpl(project) implementation(projects.appicon.enterprise) + implementation(project(":enterprise:features:enterprise:shared")) } else { implementation(projects.features.enterprise.implFoss) implementation(projects.appicon.element) diff --git a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt index 7564db5d95..39c70e35d8 100644 --- a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt +++ b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt @@ -18,7 +18,7 @@ import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.Provides import dev.zacsweers.metro.SingleIn import io.element.android.appconfig.ApplicationConfig -import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.features.enterprise.api.IsEnterpriseBuild import io.element.android.libraries.androidutils.system.getVersionCodeFromManifest import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.meta.BuildMeta @@ -74,17 +74,18 @@ object AppModule { fun providesBuildMeta( @ApplicationContext context: Context, buildType: BuildType, - enterpriseService: EnterpriseService, + isEnterpriseBuild: IsEnterpriseBuild, ): BuildMeta { val applicationName = ApplicationConfig.APPLICATION_NAME.takeIf { it.isNotEmpty() } ?: context.getString(R.string.app_name) + val isEnterprise = isEnterpriseBuild() return BuildMeta( isDebuggable = BuildConfig.DEBUG, buildType = buildType, applicationName = applicationName, - productionApplicationName = if (enterpriseService.isEnterpriseBuild) applicationName else ApplicationConfig.PRODUCTION_APPLICATION_NAME, - desktopApplicationName = if (enterpriseService.isEnterpriseBuild) applicationName else ApplicationConfig.DESKTOP_APPLICATION_NAME, + productionApplicationName = if (isEnterprise) applicationName else ApplicationConfig.PRODUCTION_APPLICATION_NAME, + desktopApplicationName = if (isEnterprise) applicationName else ApplicationConfig.DESKTOP_APPLICATION_NAME, applicationId = BuildConfig.APPLICATION_ID, - isEnterpriseBuild = enterpriseService.isEnterpriseBuild, + isEnterpriseBuild = isEnterprise, // TODO EAx Config.LOW_PRIVACY_LOG_ENABLE, lowPrivacyLoggingEnabled = false, versionName = BuildConfig.VERSION_NAME, 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 d7b768132b..cd071131d9 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -51,7 +51,7 @@ import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.createroom.api.CreateRoomEntryPoint import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.SessionEnterpriseService -import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider +import io.element.android.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider import io.element.android.features.ftue.api.FtueEntryPoint import io.element.android.features.ftue.api.state.FtueService import io.element.android.features.ftue.api.state.FtueState @@ -157,7 +157,7 @@ class LoggedInFlowNode( private val analyticsRoomListStateWatcher: AnalyticsRoomListStateWatcher, private val createRoomEntryPoint: CreateRoomEntryPoint, private val activeLiveLocationShareManager: ActiveLiveLocationShareManager, - private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider, + private val customMapTilerConfigProvider: CustomMapTilerConfigProvider, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Placeholder, @@ -685,7 +685,7 @@ class LoggedInFlowNode( val currentMapTilerConfig = LocalMapTilerConfig.current val updatedMapTilerConfig by produceState(currentMapTilerConfig) { - value = remoteEnterpriseConfigProvider.get(matrixClient.sessionId).dataOrNull()?.mapTilerConfig ?: currentMapTilerConfig + value = customMapTilerConfigProvider.get().getOrNull() ?: currentMapTilerConfig } ElementThemeApp( diff --git a/enterprise b/enterprise index f5f910856c..3bf0df1f25 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit f5f910856cfd4f982d1dcce16a80f8fedfbcbd31 +Subproject commit 3bf0df1f25c4df8ee6cc226b00d9b6a675b37a09 diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt index 7d23ad0ea1..2f1cbe1187 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.graphics.Color import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.wellknown.api.ElementWellKnown import kotlinx.coroutines.flow.Flow /** @@ -21,9 +20,6 @@ import kotlinx.coroutines.flow.Flow * On a standard build this reports permissive defaults, so callers do not need to branch on the build type themselves. */ interface EnterpriseService { - /** Whether this build is an enterprise one; `false` for the public app. */ - val isEnterpriseBuild: Boolean - /** * Whether the given session belongs to an enterprise deployment, which can be true even on a standard build. * @@ -41,17 +37,26 @@ interface EnterpriseService { suspend fun tweakMasUrl(url: String, homeserver: String, urlContentFetcher: UrlContentFetcher): String /** - * The homeservers the user is allowed to sign in to; an empty list, or one containing [ANY_ACCOUNT_PROVIDER], means no restriction. + * Returns the list of homeservers the user is allowed to sign in to. + * + * If the list is empty or contains the special value [ANY_ACCOUNT_PROVIDER], the user is allowed to sign in to any homeserver. */ - fun defaultHomeserverList(): List + fun homeserverAllowList(): List /** - * Whether the user is allowed to sign in to a given homeserver, according to [defaultHomeserverList]. + * Whether the user is allowed to sign in to a given homeserver, according to [homeserverAllowList]. * * @param homeserverUrl the server the user is trying to use. */ suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean + /** + * Whether the given homeserver enforces the use of Element Pro or a derived app. + * + * @param homeserverUrl the homeserver to check. + */ + suspend fun isElementProEnforced(homeserverUrl: String): Boolean + /** * Override the brand color. * @param sessionId the session to override the brand color for, or null to set the brand color to use when there is no session. @@ -94,11 +99,6 @@ interface EnterpriseService { */ fun getNoisyNotificationChannelId(sessionId: SessionId): String? - /** - * Gets the overridden Element Well-Known data if it has been set, or null if not set. - */ - fun overriddenElementWellKnown(): ElementWellKnown? - /** * Gets the Element Server Suite (ESS) config endpoint URL for the given domain. * Returns `null` when this build does not read its configuration from an ESS deployment. @@ -113,7 +113,7 @@ interface EnterpriseService { } fun EnterpriseService.canConnectToAnyHomeserver(): Boolean { - return defaultHomeserverList().let { + return homeserverAllowList().let { it.isEmpty() || it.contains(EnterpriseService.ANY_ACCOUNT_PROVIDER) } } diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/IsEnterpriseBuild.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/IsEnterpriseBuild.kt new file mode 100644 index 0000000000..83591ea7bd --- /dev/null +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/IsEnterpriseBuild.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.enterprise.api + +/** + * A use case to check if this build of the app is an enterprise build. + */ +fun interface IsEnterpriseBuild { + /** + * Returns `true` if this build of the app is an enterprise build, `false` otherwise. + */ + operator fun invoke(): Boolean +} diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt index 84067ce56f..94f455f2ff 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt @@ -14,6 +14,9 @@ package io.element.android.features.enterprise.api * [init] must have run before the other members return meaningful answers. */ interface SessionEnterpriseService { + /** Loads the deployment configuration of this session; to be called once when the session starts. */ + suspend fun init() + /** Whether Element Call may be used on this session, which a deployment can turn off. */ suspend fun isElementCallAvailable(): Boolean @@ -26,7 +29,4 @@ interface SessionEnterpriseService { /** Whether the homeserver forbids creating encrypted rooms, in which case the app must not offer the option. */ suspend fun isEncryptionDisabledByHomeserver(): Boolean - - /** Loads the deployment configuration of this session; to be called once when the session starts. */ - suspend fun init() } diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/CustomMapTilerConfigProvider.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/CustomMapTilerConfigProvider.kt new file mode 100644 index 0000000000..9bfd3c940a --- /dev/null +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/CustomMapTilerConfigProvider.kt @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.enterprise.api.remoteconfig + +/** + * A provider of a runtime-fetched MapTiler configuration, which can be used to override the default MapTiler configuration. + */ +fun interface CustomMapTilerConfigProvider { + suspend fun get(): Result +} diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/MapTilerConfig.kt similarity index 57% rename from libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt rename to features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/MapTilerConfig.kt index 88a89e7a49..3e8b8b14e1 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/MapTilerConfig.kt @@ -1,29 +1,18 @@ /* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.wellknown.api +package io.element.android.features.enterprise.api.remoteconfig -data class ElementWellKnown( - val registrationHelperUrl: String?, - val enforceElementPro: Boolean?, - val rageshakeUrl: String?, - val brandColor: String?, - val notificationSound: String?, - val identityProviderAppScheme: String?, - val customRecoveryPassphrase: CustomRecoveryPassphrase?, - val contentScannerUrl: String?, - val forceDisableE2EE: Boolean?, - val mapTilerConfig: MapTilerConfig?, -) +import androidx.compose.runtime.Immutable /** * A `MapTiler` configuration received from the server. This configuration is used to display maps in the app. */ +@Immutable data class MapTilerConfig( /** The API key to use with MapTiler. This is always not null, but can be empty/blank. */ val apiKey: String, diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfig.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfig.kt deleted file mode 100644 index c9adf21eec..0000000000 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfig.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.enterprise.api.remoteconfig - -import io.element.android.libraries.wellknown.api.ElementWellKnown - -/** - * Typealias for the well-known configuration used for enterprise features. - * - * This will allow for easier refactoring in the future when we'll want to move as much of its logic as possible to this module. - */ -typealias RemoteEnterpriseConfig = ElementWellKnown diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt deleted file mode 100644 index 4507b1a72a..0000000000 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigProvider.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.enterprise.api.remoteconfig - -import io.element.android.libraries.matrix.api.UrlContentFetcher -import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult - -/** - * Wrapper to fetch the remote enterprise configuration for a given [SessionId], either from a local cache or from a remote source. - */ -interface RemoteEnterpriseConfigProvider { - /** - * Returns the configuration of a session, served from the cache when possible. - * A stale entry is reported as outdated while a refresh runs in the background, so this rarely waits on the network. - * - * @param sessionId the session whose configuration is requested. - */ - suspend fun get(sessionId: SessionId): WellknownRetrieverResult - - /** - * Creates a provider, which needs a fetcher because the configuration is read over HTTP. - */ - interface Factory { - /** - * @param urlContentFetcher used to perform the request; a client that is not authenticated yet also works. - */ - fun create(urlContentFetcher: UrlContentFetcher): RemoteEnterpriseConfigProvider - } -} diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetrieverResult.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigResult.kt similarity index 63% rename from libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetrieverResult.kt rename to features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigResult.kt index 98af981a66..5f687ff052 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetrieverResult.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/remoteconfig/RemoteEnterpriseConfigResult.kt @@ -1,33 +1,32 @@ /* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.wellknown.api +package io.element.android.features.enterprise.api.remoteconfig -sealed interface WellknownRetrieverResult { +sealed interface RemoteEnterpriseConfigResult { /** * Well-known data has been successfully retrieved. */ - data class Success(val data: T) : WellknownRetrieverResult + data class Success(val data: T) : RemoteEnterpriseConfigResult /** * Well-known data has been retrieved from the local cache but is outdated and a manual refresh has been launched. */ - data class Outdated(val data: T) : WellknownRetrieverResult + data class Outdated(val data: T) : RemoteEnterpriseConfigResult /** * Well-known data is not found (file does not exist server side, we got a 404). */ - data object NotFound : WellknownRetrieverResult + data object NotFound : RemoteEnterpriseConfigResult /** * Any other error. */ - data class Error(val exception: Exception) : WellknownRetrieverResult + data class Error(val exception: Exception) : RemoteEnterpriseConfigResult fun dataOrNull(): T? = when (this) { is Success -> data diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt index c75a2cb527..d07c0db569 100644 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt @@ -16,18 +16,16 @@ import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.wellknown.api.ElementWellKnown import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf @ContributesBinding(AppScope::class) class DefaultEnterpriseService : EnterpriseService { - override val isEnterpriseBuild = false - override suspend fun isEnterpriseUser(sessionId: SessionId) = false override suspend fun tweakMasUrl(url: String, homeserver: String, urlContentFetcher: UrlContentFetcher) = url - override fun defaultHomeserverList(): List = emptyList() + override fun homeserverAllowList(): List = emptyList() override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true + override suspend fun isElementProEnforced(homeserverUrl: String): Boolean = false override suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?) = Unit @@ -48,7 +46,5 @@ class DefaultEnterpriseService : EnterpriseService { override fun getNoisyNotificationChannelId(sessionId: SessionId): String? = null - override fun overriddenElementWellKnown(): ElementWellKnown? = null - override fun essConfigEndpointUrl(domain: String): String? = null } diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultIsEnterpriseBuild.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultIsEnterpriseBuild.kt new file mode 100644 index 0000000000..73da4d5d03 --- /dev/null +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultIsEnterpriseBuild.kt @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.enterprise.impl + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import io.element.android.features.enterprise.api.IsEnterpriseBuild + +@ContributesBinding(AppScope::class) +class DefaultIsEnterpriseBuild : IsEnterpriseBuild { + override fun invoke(): Boolean = false +} diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultCustomMapTilerConfigProvider.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultCustomMapTilerConfigProvider.kt new file mode 100644 index 0000000000..2b136d3739 --- /dev/null +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultCustomMapTilerConfigProvider.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.enterprise.impl.remoteconfig + +import dev.zacsweers.metro.ContributesBinding +import io.element.android.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig +import io.element.android.libraries.di.SessionScope + +@ContributesBinding(SessionScope::class) +class DefaultCustomMapTilerConfigProvider : CustomMapTilerConfigProvider { + override suspend fun get(): Result = Result.success(null) +} diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt deleted file mode 100644 index fbc5cd5827..0000000000 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/remoteconfig/DefaultRemoteEnterpriseConfigProvider.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.enterprise.impl.remoteconfig - -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.ContributesBinding -import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfig -import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider -import io.element.android.libraries.matrix.api.UrlContentFetcher -import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult - -@ContributesBinding(AppScope::class) -class DefaultRemoteEnterpriseConfigProvider : RemoteEnterpriseConfigProvider { - override suspend fun get(sessionId: SessionId): WellknownRetrieverResult { - return WellknownRetrieverResult.NotFound - } - - @ContributesBinding(AppScope::class) - class Factory : RemoteEnterpriseConfigProvider.Factory { - override fun create(urlContentFetcher: UrlContentFetcher): RemoteEnterpriseConfigProvider { - return DefaultRemoteEnterpriseConfigProvider() - } - } -} diff --git a/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt b/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt index 2bee3f8b4d..474990c6d9 100644 --- a/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt +++ b/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt @@ -19,15 +19,9 @@ import org.junit.Test class DefaultEnterpriseServiceTest { @Test - fun `isEnterpriseBuild is false`() { + fun homeserverWhitelist() { val defaultEnterpriseService = DefaultEnterpriseService() - assertThat(defaultEnterpriseService.isEnterpriseBuild).isFalse() - } - - @Test - fun `defaultHomeserverList should return empty list`() { - val defaultEnterpriseService = DefaultEnterpriseService() - assertThat(defaultEnterpriseService.defaultHomeserverList()).isEmpty() + assertThat(defaultEnterpriseService.homeserverAllowList()).isEmpty() } @Test diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt index 858c46e3bf..e346e5934a 100644 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt @@ -14,7 +14,6 @@ import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.matrix.api.UrlContentFetcher import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.simulateLongTask import kotlinx.coroutines.flow.Flow @@ -22,7 +21,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow class FakeEnterpriseService( - override val isEnterpriseBuild: Boolean = false, private val isEnterpriseUserResult: (SessionId) -> Boolean = { lambdaError() }, private val defaultHomeserverListResult: () -> List = { emptyList() }, private val isAllowedToConnectToHomeserverResult: (String) -> Boolean = { lambdaError() }, @@ -33,8 +31,8 @@ class FakeEnterpriseService( private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() }, private val getNoisyNotificationChannelIdResult: (SessionId?) -> String? = { lambdaError() }, private val tweakMasUrlResult: (String, String, UrlContentFetcher) -> String = { _, _, _ -> lambdaError() }, - private val overrideWellKnownResult: () -> ElementWellKnown? = { lambdaError() }, private val essConfigEndpointUrlResult: (String) -> String = { lambdaError() }, + private val isElementProEnforcedResult: (String) -> Boolean = { lambdaError() }, ) : EnterpriseService { private val brandColorState = MutableStateFlow(initialBrandColor) private val semanticColorsState = MutableStateFlow(initialSemanticColors) @@ -47,7 +45,7 @@ class FakeEnterpriseService( tweakMasUrlResult(url, homeserver, urlContentFetcher) } - override fun defaultHomeserverList(): List { + override fun homeserverAllowList(): List { return defaultHomeserverListResult() } @@ -55,6 +53,10 @@ class FakeEnterpriseService( isAllowedToConnectToHomeserverResult(homeserverUrl) } + override suspend fun isElementProEnforced(homeserverUrl: String): Boolean = simulateLongTask { + isElementProEnforcedResult(homeserverUrl) + } + override suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?) = simulateLongTask { overrideBrandColorResult(sessionId, brandColor) } @@ -84,10 +86,6 @@ class FakeEnterpriseService( return getNoisyNotificationChannelIdResult(sessionId) } - override fun overriddenElementWellKnown(): ElementWellKnown? { - return overrideWellKnownResult() - } - override fun essConfigEndpointUrl(domain: String): String { return essConfigEndpointUrlResult(domain) } diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProvider.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProvider.kt deleted file mode 100644 index 2c57dbb530..0000000000 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProvider.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.enterprise.test.remoteconfig - -import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfig -import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider -import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult - -class FakeRemoteEnterpriseConfigProvider( - private val getResult: (SessionId) -> WellknownRetrieverResult = { WellknownRetrieverResult.NotFound } -) : RemoteEnterpriseConfigProvider { - override suspend fun get(sessionId: SessionId): WellknownRetrieverResult { - return getResult(sessionId) - } -} diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt deleted file mode 100644 index a49bbba279..0000000000 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/FakeRemoteEnterpriseConfigProviderFactory.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.enterprise.test.remoteconfig - -import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider -import io.element.android.libraries.matrix.api.UrlContentFetcher - -class FakeRemoteEnterpriseConfigProviderFactory( - private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(), -) : RemoteEnterpriseConfigProvider.Factory { - override fun create(urlContentFetcher: UrlContentFetcher): RemoteEnterpriseConfigProvider = remoteEnterpriseConfigProvider -} diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/Fixtures.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/Fixtures.kt new file mode 100644 index 0000000000..fd429eada5 --- /dev/null +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/remoteconfig/Fixtures.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.enterprise.test.remoteconfig + +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig + +fun aMapTilerConfig( + apiKey: String = "test_api_key", + baseUrl: String? = null, + lightStyleId: String? = null, + darkStyleId: String? = null, +) = MapTilerConfig( + apiKey = apiKey, + baseUrl = baseUrl, + lightStyleId = lightStyleId, + darkStyleId = darkStyleId, +) diff --git a/features/location/api/build.gradle.kts b/features/location/api/build.gradle.kts index b3eeacf87f..8830b39a22 100644 --- a/features/location/api/build.gradle.kts +++ b/features/location/api/build.gradle.kts @@ -71,6 +71,7 @@ dependencies { implementation(projects.libraries.matrixui) implementation(projects.libraries.uiStrings) implementation(projects.libraries.wellknown.api) + implementation(projects.features.enterprise.api) implementation(libs.coil.compose) implementation(libs.datetime) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/LocalMapTilerConfig.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/LocalMapTilerConfig.kt index a2212119f5..752dd2401c 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/LocalMapTilerConfig.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/LocalMapTilerConfig.kt @@ -8,7 +8,7 @@ package io.element.android.features.location.api import androidx.compose.runtime.staticCompositionLocalOf -import io.element.android.libraries.wellknown.api.MapTilerConfig +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig /** * Provides a [MapTilerConfig] for the MapLibre maps. diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt index 33e454722f..b3149964e4 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt @@ -37,6 +37,7 @@ import coil3.compose.rememberAsyncImagePainter import coil3.request.ImageRequest import coil3.request.SuccessResult import io.element.android.compound.theme.ElementTheme +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig import io.element.android.features.location.api.internal.StaticMapPlaceholder import io.element.android.features.location.api.internal.centerBottomEdge import io.element.android.features.location.api.internal.rememberStaticMapBuilder @@ -45,7 +46,6 @@ import io.element.android.libraries.designsystem.components.PinVariant import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.wellknown.api.MapTilerConfig /** * Shows a static map image downloaded via a third party service's static maps API. diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt index 78eae2f633..cd339f67b0 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt @@ -8,8 +8,8 @@ package io.element.android.features.location.api.internal +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig import io.element.android.features.location.api.BuildConfig -import io.element.android.libraries.wellknown.api.MapTilerConfig import kotlin.math.roundToInt /** diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt index 10aef90116..d817a3307f 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt @@ -10,8 +10,8 @@ package io.element.android.features.location.api.internal +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig import io.element.android.features.location.api.BuildConfig -import io.element.android.libraries.wellknown.api.MapTilerConfig internal class MapTilerTileServerStyleUriBuilder( private val baseUrl: String, diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt index 212da08647..31992b1dd6 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt @@ -10,8 +10,7 @@ package io.element.android.features.location.api.internal import androidx.compose.runtime.Composable import androidx.compose.runtime.remember -import io.element.android.libraries.wellknown.api.MapTilerConfig - +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig /** * Builds an URL for a 3rd party service provider static maps API. */ diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt index 9676005f59..67936525e5 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt @@ -11,7 +11,7 @@ package io.element.android.features.location.api.internal import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import io.element.android.compound.theme.ElementTheme -import io.element.android.libraries.wellknown.api.MapTilerConfig +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig /** * Builds a style URI for a MapLibre compatible tile server. diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt index 09aae4f310..c153ab1ce4 100644 --- a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt +++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt @@ -9,7 +9,7 @@ package io.element.android.features.location.api.internal import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.wellknown.api.MapTilerConfig +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig import org.junit.Test class MapTilerTileServerStyleUriBuilderTest { diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt index eaa238bed0..84c4ab4fba 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt @@ -43,6 +43,7 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig import io.element.android.features.location.api.internal.rememberTileStyleUrl import io.element.android.features.location.impl.common.MapDefaults import io.element.android.libraries.architecture.AsyncData @@ -50,7 +51,6 @@ import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.theme.components.BottomSheetScaffold import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator -import io.element.android.libraries.wellknown.api.MapTilerConfig import org.maplibre.compose.camera.CameraState import org.maplibre.compose.camera.rememberCameraState import org.maplibre.compose.map.MapOptions diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt index d388e0c5d2..55f1e088d2 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt @@ -22,7 +22,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Composer -import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider +import io.element.android.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider import io.element.android.features.location.api.live.ActiveLiveLocationShareManager import io.element.android.features.location.impl.common.LocationConstraintsCheck import io.element.android.features.location.impl.common.MapDefaults @@ -73,7 +73,7 @@ class ShareLocationPresenter( private val liveLocationShareManager: ActiveLiveLocationShareManager, private val liveLocationStore: LiveLocationStore, private val userLocationStateFactory: UserLocationState.Factory, - private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider, + private val customMapTilerConfigProvider: CustomMapTilerConfigProvider, ) : Presenter { @AssistedFactory fun interface Factory { @@ -96,7 +96,7 @@ class ShareLocationPresenter( val currentUser by client.userProfile.collectAsState() val customMapConfig by produceState(AsyncData.Loading()) { // Ignore errors - value = AsyncData.Success(remoteEnterpriseConfigProvider.get(client.sessionId).dataOrNull()?.mapTilerConfig) + value = AsyncData.Success(customMapTilerConfigProvider.get().getOrNull()) } val sendLiveLocationPermissions by room.permissionsAsState(SendLiveLocationPermissions.DEFAULT) { perms -> perms.sendLiveLocationPermissions() diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt index d2cce94ef8..b29228f866 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt @@ -8,12 +8,12 @@ package io.element.android.features.location.impl.share +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.features.location.impl.common.userlocation.UserLocationState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.wellknown.api.MapTilerConfig import kotlinx.collections.immutable.ImmutableList data class ShareLocationState( diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt index 6d7ff5e612..9214d5a017 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt @@ -9,13 +9,13 @@ package io.element.android.features.location.impl.share import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.features.location.impl.common.userlocation.UserLocationState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.wellknown.api.MapTilerConfig import kotlinx.collections.immutable.persistentListOf import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt index 2d9a4900dd..bbec7b449f 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt @@ -22,7 +22,7 @@ import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject -import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider +import io.element.android.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider import io.element.android.features.location.api.Location import io.element.android.features.location.api.ShowLocationMode import io.element.android.features.location.api.live.ActiveLiveLocationShareManager @@ -47,7 +47,6 @@ import io.element.android.libraries.dateformatter.api.DateFormatter import io.element.android.libraries.dateformatter.api.DateFormatterMode import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.getBestName import io.element.android.libraries.matrix.api.room.joinedRoomMembers @@ -58,18 +57,18 @@ import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch -@AssistedInject class ShowLocationPresenter( +@AssistedInject +class ShowLocationPresenter( @Assisted private val mode: ShowLocationMode, permissionsPresenterFactory: PermissionsPresenter.Factory, private val locationActions: LocationActions, private val buildMeta: BuildMeta, private val dateFormatter: DateFormatter, private val stringProvider: StringProvider, - private val sessionId: SessionId, private val joinedRoom: JoinedRoom, private val liveLocationShareManager: ActiveLiveLocationShareManager, private val userLocationStateFactory: UserLocationState.Factory, - private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider, + private val customMapTilerConfigProvider: CustomMapTilerConfigProvider, ) : Presenter { @AssistedFactory fun interface Factory { fun create(mode: ShowLocationMode): ShowLocationPresenter @@ -89,7 +88,7 @@ import kotlinx.coroutines.launch val customMapTilerConfig by produceState(AsyncData.Loading()) { // Ignore errors - value = AsyncData.Success(remoteEnterpriseConfigProvider.get(sessionId).dataOrNull()?.mapTilerConfig) + value = AsyncData.Success(customMapTilerConfigProvider.get().getOrNull()) } fun checkLocationConstraints() { diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt index 2ae76ef9e9..0472eb94e9 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt @@ -8,6 +8,7 @@ package io.element.android.features.location.impl.show +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig import io.element.android.features.location.api.Location import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.features.location.impl.common.ui.LocationMarkerData @@ -17,7 +18,6 @@ import io.element.android.libraries.designsystem.components.PinVariant import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.location.AssetType -import io.element.android.libraries.wellknown.api.MapTilerConfig import kotlinx.collections.immutable.ImmutableList data class ShowLocationState( diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt index a1f349d644..bf582eb59a 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt @@ -9,6 +9,7 @@ package io.element.android.features.location.impl.show import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig import io.element.android.features.location.api.Location import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.features.location.impl.common.userlocation.UserLocationState @@ -18,7 +19,6 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.location.AssetType -import io.element.android.libraries.wellknown.api.MapTilerConfig import kotlinx.collections.immutable.toImmutableList class ShowLocationStateProvider : PreviewParameterProvider { diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt index 9be168e5a2..a37011e1d9 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt @@ -11,7 +11,6 @@ package io.element.android.features.location.impl.share import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext import com.google.common.truth.Truth.assertThat -import io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider import io.element.android.features.location.impl.common.FakeUserLocationStateFactory import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter @@ -59,7 +58,7 @@ class DefaultShareLocationEntryPointTest { sessionId = room.sessionId, ), userLocationStateFactory = FakeUserLocationStateFactory(), - remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(), + customMapTilerConfigProvider = { Result.success(null) }, ) }, analyticsService = FakeAnalyticsService(), diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt index 6748c5a675..d249fdaf69 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt @@ -19,8 +19,8 @@ import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Composer -import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider -import io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider +import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig +import io.element.android.features.enterprise.test.remoteconfig.aMapTilerConfig import io.element.android.features.location.api.Location import io.element.android.features.location.impl.aPermissionsState import io.element.android.features.location.impl.common.FakeUserLocationStateFactory @@ -32,8 +32,6 @@ import io.element.android.features.location.impl.common.ui.LocationConstraintsDi import io.element.android.features.location.impl.live.LiveLocationStore import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.features.messages.test.FakeMessageComposerContext -import io.element.android.features.wellknown.test.aMapTilerConfig -import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.dateformatter.test.FakeDurationFormatter import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -55,7 +53,6 @@ import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermiss import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFactory -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.assert @@ -92,7 +89,7 @@ class ShareLocationPresenterTest { liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(), liveLocationStore: LiveLocationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId), client: FakeMatrixClient = fakeMatrixClient, - remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(), + customMapTilerConfigProvider: () -> Result = { Result.success(null) }, ): ShareLocationPresenter = ShareLocationPresenter( permissionsPresenterFactory = { fakePermissionsPresenter }, room = joinedRoom, @@ -106,7 +103,7 @@ class ShareLocationPresenterTest { liveLocationShareManager = liveLocationShareManager, liveLocationStore = liveLocationStore, userLocationStateFactory = FakeUserLocationStateFactory(), - remoteEnterpriseConfigProvider = remoteEnterpriseConfigProvider, + customMapTilerConfigProvider = customMapTilerConfigProvider, ) @Test @@ -134,9 +131,7 @@ class ShareLocationPresenterTest { client = FakeMatrixClient( sessionId = A_USER_ID, ), - remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider( - getResult = { WellknownRetrieverResult.Success(anElementWellKnown(mapTilerConfig = mapTilerConfig)) } - ) + customMapTilerConfigProvider = { Result.success(mapTilerConfig) } ) shareLocationPresenter.test { val state = awaitItem() diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt index 69451eedb7..db5c6b9f9f 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt @@ -11,7 +11,6 @@ package io.element.android.features.location.impl.show import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext import com.google.common.truth.Truth.assertThat -import io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider import io.element.android.features.location.api.Location import io.element.android.features.location.api.ShowLocationEntryPoint import io.element.android.features.location.api.ShowLocationMode @@ -21,7 +20,6 @@ import io.element.android.features.location.impl.common.permissions.FakePermissi import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.services.analytics.test.FakeAnalyticsService @@ -51,10 +49,9 @@ class DefaultShowLocationEntryPointTest { dateFormatter = FakeDateFormatter(), stringProvider = FakeStringProvider(), joinedRoom = joinedRoom, - sessionId = A_SESSION_ID, liveLocationShareManager = FakeActiveLiveLocationShareManager(), userLocationStateFactory = FakeUserLocationStateFactory(), - remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(), + customMapTilerConfigProvider = { Result.success(null) }, ) }, analyticsService = FakeAnalyticsService(), diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index 77b26eb66c..7d9b0d5e03 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -12,7 +12,8 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider +import io.element.android.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider +import io.element.android.features.enterprise.test.remoteconfig.aMapTilerConfig import io.element.android.features.location.api.Location import io.element.android.features.location.api.ShowLocationMode import io.element.android.features.location.impl.aPermissionsState @@ -23,20 +24,15 @@ import io.element.android.features.location.impl.common.permissions.PermissionsE import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.features.location.test.FakeActiveLiveLocationShareManager -import io.element.android.features.wellknown.test.aMapTilerConfig -import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.dateformatter.test.FakeDateFormatter -import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.location.LiveLocationShare -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.core.aBuildMeta import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.room.location.aLiveLocationShare -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test @@ -69,9 +65,8 @@ class ShowLocationPresenterTest { ), locationActions: FakeLocationActions = fakeLocationActions, joinedRoom: JoinedRoom = FakeJoinedRoom(), - sessionId: SessionId = A_SESSION_ID, liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(), - remoteEnterpriseConfigProvider: FakeRemoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(), + customMapTilerConfigProvider: CustomMapTilerConfigProvider = { Result.success(null) }, ) = ShowLocationPresenter( mode = mode, permissionsPresenterFactory = { fakePermissionsPresenter }, @@ -80,21 +75,15 @@ class ShowLocationPresenterTest { dateFormatter = fakeDateFormatter, stringProvider = FakeStringProvider(), joinedRoom = joinedRoom, - sessionId = sessionId, liveLocationShareManager = liveLocationShareManager, userLocationStateFactory = FakeUserLocationStateFactory(), - remoteEnterpriseConfigProvider = remoteEnterpriseConfigProvider, + customMapTilerConfigProvider = customMapTilerConfigProvider, ) @Test fun `present - non-null customMapStyleUrl`() = runTest { val mapTilerConfig = aMapTilerConfig(apiKey = "A KEY") - val shareLocationPresenter = createShowLocationPresenter( - sessionId = A_SESSION_ID, - remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider( - getResult = { WellknownRetrieverResult.Success(anElementWellKnown(mapTilerConfig = mapTilerConfig)) } - ) - ) + val shareLocationPresenter = createShowLocationPresenter(customMapTilerConfigProvider = { Result.success(mapTilerConfig) }) shareLocationPresenter.test { val state = awaitItem() assertThat(state.customMapTilerConfig.isLoading()).isTrue() @@ -131,12 +120,7 @@ class ShowLocationPresenterTest { fun `centers on user location`() = runTest { fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted)) - val presenter = createShowLocationPresenter( - sessionId = A_SESSION_ID, - remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider( - getResult = { WellknownRetrieverResult.Success(anElementWellKnown(mapTilerConfig = aMapTilerConfig())) } - ) - ) + val presenter = createShowLocationPresenter(customMapTilerConfigProvider = { Result.success(aMapTilerConfig()) }) presenter.test { val initialState = awaitItem() diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt index 119923c67d..a3a475df2f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt @@ -11,17 +11,14 @@ package io.element.android.features.login.impl.accesscontrol import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.features.enterprise.api.IsEnterpriseBuild import io.element.android.features.login.api.accesscontrol.AccountProviderAccessControl import io.element.android.features.login.impl.changeserver.AccountProviderAccessException -import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory -import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource -import io.element.android.libraries.wellknown.api.WellknownRetriever @ContributesBinding(AppScope::class) class DefaultAccountProviderAccessControl( + private val isEnterpriseBuild: IsEnterpriseBuild, private val enterpriseService: EnterpriseService, - private val wellknownRetrieverFactory: WellknownRetriever.Factory, - private val temporaryMatrixClientFactory: TemporaryMatrixClientFactory, ) : AccountProviderAccessControl { override suspend fun isAllowedToConnectToAccountProvider(accountProviderUrl: String) = try { assertIsAllowedToConnectToAccountProvider( @@ -38,29 +35,19 @@ class DefaultAccountProviderAccessControl( title: String, accountProviderUrl: String, ) { - if (enterpriseService.isEnterpriseBuild.not()) { + if (isEnterpriseBuild().not()) { // Ensure that Element Pro is not required for this account provider - val temporaryMatrixClient = temporaryMatrixClientFactory.create(accountProviderUrl).getOrThrow() - temporaryMatrixClient.use { - val wellknownRetriever = wellknownRetrieverFactory.create(temporaryMatrixClient) - val wellKnown = wellknownRetriever.getElementWellKnown( - host = accountProviderUrl, - // It's expected this is hardcoded to the well-known endpoint - source = EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT, - ).dataOrNull() - - if (wellKnown?.enforceElementPro == true) { - throw AccountProviderAccessException.NeedElementProException( - unauthorisedAccountProviderTitle = title, - applicationId = ELEMENT_PRO_APPLICATION_ID, - ) - } + if (enterpriseService.isElementProEnforced(accountProviderUrl)) { + throw AccountProviderAccessException.NeedElementProException( + unauthorisedAccountProviderTitle = title, + applicationId = ELEMENT_PRO_APPLICATION_ID, + ) } } if (enterpriseService.isAllowedToConnectToHomeserver(accountProviderUrl).not()) { throw AccountProviderAccessException.UnauthorizedAccountProviderException( unauthorisedAccountProviderTitle = title, - authorisedAccountProviderTitles = enterpriseService.defaultHomeserverList(), + authorisedAccountProviderTitles = enterpriseService.homeserverAllowList(), ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt index 931959d4ea..ad016dec26 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt @@ -23,7 +23,7 @@ class AccountProviderDataSource( enterpriseService: EnterpriseService, ) { private val defaultAccountProvider = createAccountProvider( - url = enterpriseService.defaultHomeserverList() + url = enterpriseService.homeserverAllowList() .firstOrNull { it != EnterpriseService.ANY_ACCOUNT_PROVIDER } ?: AuthenticationConfig.MATRIX_ORG_URL ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt index 41605352ef..826cca0c2b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt @@ -28,7 +28,7 @@ class ChangeAccountProviderPresenter( @Composable override fun present(): ChangeAccountProviderState { val staticAccountProviderList = remember { - enterpriseService.defaultHomeserverList() + enterpriseService.homeserverAllowList() .filter { it != EnterpriseService.ANY_ACCOUNT_PROVIDER } .map { it.ensureProtocol() } .ifEmpty { listOf(AuthenticationConfig.MATRIX_ORG_URL) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt index a9983a19d1..92c3ff0fff 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt @@ -60,7 +60,7 @@ class ChooseAccountProviderPresenter( val staticAccountProviderList = remember { // The list cannot contains ANY_ACCOUNT_PROVIDER ("*") and cannot be empty at this point - enterpriseService.defaultHomeserverList() + enterpriseService.homeserverAllowList() .map { it.ensureProtocol() } .map { url -> AccountProvider( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt index 29e4b1afe5..850e4332dd 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt @@ -62,13 +62,13 @@ class OnBoardingPresenter( val forcedAccountProvider = remember { // If defaultHomeserverList() returns a singleton list, this is the default account provider. // In this case, the user can sign in using this homeserver, or use QrCode login - enterpriseService.defaultHomeserverList().singleOrNull() + enterpriseService.homeserverAllowList().singleOrNull() } val canConnectToAnyHomeserver = remember { enterpriseService.canConnectToAnyHomeserver() } val mustChooseAccountProvider = remember { - !canConnectToAnyHomeserver && enterpriseService.defaultHomeserverList().size > 1 + !canConnectToAnyHomeserver && enterpriseService.homeserverAllowList().size > 1 } val linkAccountProvider by produceState(initialValue = null) { // Account provider from the link, if allowed by the enterprise service diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt index ee4cea0532..fbfae984fc 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt @@ -11,15 +11,9 @@ package io.element.android.features.login.impl.accesscontrol import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.changeserver.AccountProviderAccessException -import io.element.android.features.wellknown.test.FakeWellknownRetriever -import io.element.android.features.wellknown.test.FakeWellknownRetrieverFactory -import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2 import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_URL -import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClientFactory -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import kotlinx.coroutines.test.runTest import org.junit.Assert.assertThrows import org.junit.Test @@ -30,9 +24,7 @@ class DefaultAccountProviderAccessControlTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = false, isAllowedToConnectToHomeserver = true, - elementWellKnown = anElementWellKnown( - enforceElementPro = true, - ), + enterpriseService = FakeEnterpriseService(isElementProEnforcedResult = { true }), ) accessControl.expectNeedElementProException() } @@ -43,9 +35,7 @@ class DefaultAccountProviderAccessControlTest { isEnterpriseBuild = false, // false here. isAllowedToConnectToHomeserver = false, - elementWellKnown = anElementWellKnown( - enforceElementPro = true, - ), + enterpriseService = FakeEnterpriseService(isElementProEnforcedResult = { true }), ) accessControl.expectNeedElementProException() } @@ -55,8 +45,9 @@ class DefaultAccountProviderAccessControlTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = false, isAllowedToConnectToHomeserver = true, - elementWellKnown = anElementWellKnown( - enforceElementPro = false, + enterpriseService = FakeEnterpriseService( + isAllowedToConnectToHomeserverResult = { true }, + isElementProEnforcedResult = { false } ), ) accessControl.expectAllowed() @@ -67,19 +58,15 @@ class DefaultAccountProviderAccessControlTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = false, isAllowedToConnectToHomeserver = true, - elementWellKnown = anElementWellKnown( - enforceElementPro = null, - ), ) accessControl.expectAllowed() } @Test - fun `foss build should allow using account provider twith missing wellknown`() = runTest { + fun `foss build should allow using account provider with missing wellknown`() = runTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = false, isAllowedToConnectToHomeserver = true, - elementWellKnown = null, ) accessControl.expectAllowed() } @@ -90,8 +77,10 @@ class DefaultAccountProviderAccessControlTest { isEnterpriseBuild = false, isAllowedToConnectToHomeserver = false, allowedAccountProviders = listOf(AN_ACCOUNT_PROVIDER_2), - elementWellKnown = anElementWellKnown( - enforceElementPro = false, + enterpriseService = FakeEnterpriseService( + isAllowedToConnectToHomeserverResult = { false }, + isElementProEnforcedResult = { false }, + defaultHomeserverListResult = { listOf(AN_ACCOUNT_PROVIDER_2) }, ), ) accessControl.expectUnauthorizedAccountProviderException() @@ -102,8 +91,9 @@ class DefaultAccountProviderAccessControlTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = true, isAllowedToConnectToHomeserver = true, - elementWellKnown = anElementWellKnown( - enforceElementPro = true, + enterpriseService = FakeEnterpriseService( + isAllowedToConnectToHomeserverResult = { true }, + isElementProEnforcedResult = { true }, ), ) accessControl.expectAllowed() @@ -114,8 +104,9 @@ class DefaultAccountProviderAccessControlTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = true, isAllowedToConnectToHomeserver = true, - elementWellKnown = anElementWellKnown( - enforceElementPro = false, + enterpriseService = FakeEnterpriseService( + isAllowedToConnectToHomeserverResult = { true }, + isElementProEnforcedResult = { false }, ), ) accessControl.expectAllowed() @@ -127,8 +118,10 @@ class DefaultAccountProviderAccessControlTest { isEnterpriseBuild = true, isAllowedToConnectToHomeserver = false, allowedAccountProviders = listOf(AN_ACCOUNT_PROVIDER_2), - elementWellKnown = anElementWellKnown( - enforceElementPro = true, + enterpriseService = FakeEnterpriseService( + isAllowedToConnectToHomeserverResult = { false }, + isElementProEnforcedResult = { true }, + defaultHomeserverListResult = { listOf(AN_ACCOUNT_PROVIDER_2) }, ), ) accessControl.expectUnauthorizedAccountProviderException() @@ -140,8 +133,10 @@ class DefaultAccountProviderAccessControlTest { isEnterpriseBuild = true, isAllowedToConnectToHomeserver = false, allowedAccountProviders = listOf(AN_ACCOUNT_PROVIDER_2), - elementWellKnown = anElementWellKnown( - enforceElementPro = false, + enterpriseService = FakeEnterpriseService( + isAllowedToConnectToHomeserverResult = { false }, + isElementProEnforcedResult = { false }, + defaultHomeserverListResult = { listOf(AN_ACCOUNT_PROVIDER_2) }, ), ) accessControl.expectUnauthorizedAccountProviderException() @@ -151,25 +146,14 @@ class DefaultAccountProviderAccessControlTest { isEnterpriseBuild: Boolean = false, isAllowedToConnectToHomeserver: Boolean = false, allowedAccountProviders: List = emptyList(), - elementWellKnown: ElementWellKnown? = null, - ) = DefaultAccountProviderAccessControl( - enterpriseService = FakeEnterpriseService( - isEnterpriseBuild = isEnterpriseBuild, + enterpriseService: FakeEnterpriseService = FakeEnterpriseService( isAllowedToConnectToHomeserverResult = { isAllowedToConnectToHomeserver }, defaultHomeserverListResult = { allowedAccountProviders }, - ), - wellknownRetrieverFactory = FakeWellknownRetrieverFactory( - wellknownRetriever = FakeWellknownRetriever( - getElementWellKnownResult = { _, _ -> - if (elementWellKnown == null) { - WellknownRetrieverResult.NotFound - } else { - WellknownRetrieverResult.Success(elementWellKnown) - } - }, - ), - ), - temporaryMatrixClientFactory = FakeTemporaryMatrixClientFactory(), + isElementProEnforcedResult = { false }, + ) + ) = DefaultAccountProviderAccessControl( + isEnterpriseBuild = { isEnterpriseBuild }, + enterpriseService = enterpriseService, ) private fun DefaultAccountProviderAccessControl.expectNeedElementProException() { diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index c28fee77ad..71663c5e7f 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -16,22 +16,15 @@ import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.error.ChangeServerError import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate -import io.element.android.features.wellknown.test.FakeWellknownRetriever -import io.element.android.features.wellknown.test.FakeWellknownRetrieverFactory -import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_HOMESERVER_URL -import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClientFactory import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionDialog import io.element.android.libraries.permissions.test.FakeLocalNetworkPermissionAdvisor import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory -import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource -import io.element.android.libraries.wellknown.api.WellknownRetriever -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -64,6 +57,7 @@ class ChangeServerPresenterTest { authenticationService = authenticationService, enterpriseService = FakeEnterpriseService( isAllowedToConnectToHomeserverResult = { true }, + isElementProEnforcedResult = { false }, ), ).test { val initialState = awaitItem() @@ -86,6 +80,7 @@ class ChangeServerPresenterTest { createPresenter( enterpriseService = FakeEnterpriseService( isAllowedToConnectToHomeserverResult = { true }, + isElementProEnforcedResult = { false }, ), authenticationService = authenticationService, ).test { @@ -113,6 +108,7 @@ class ChangeServerPresenterTest { createPresenter( enterpriseService = FakeEnterpriseService( isAllowedToConnectToHomeserverResult = { true }, + isElementProEnforcedResult = { false }, ), authenticationService = authenticationService, ).test { @@ -136,6 +132,7 @@ class ChangeServerPresenterTest { enterpriseService = FakeEnterpriseService( isAllowedToConnectToHomeserverResult = isAllowedToConnectToHomeserverResult, defaultHomeserverListResult = { listOf("element.io") }, + isElementProEnforcedResult = { false }, ), ).test { val initialState = awaitItem() @@ -159,23 +156,19 @@ class ChangeServerPresenterTest { @Test fun `present - change server element pro required error`() = runTest { - val getElementWellKnownResult = lambdaRecorder { homeserver: String, _: EnterpriseRemoteConfigSource -> - assertThat(homeserver).isEqualTo(A_HOMESERVER_URL) - WellknownRetrieverResult.Success( - anElementWellKnown( - enforceElementPro = true, - ) - ) - } + val isEnterpriseBuildLambda = lambdaRecorder { false } createPresenter( - wellknownRetriever = FakeWellknownRetriever( - getElementWellKnownResult = getElementWellKnownResult, - ), + isEnterpriseBuild = isEnterpriseBuildLambda, + enterpriseService = FakeEnterpriseService( + isElementProEnforcedResult = { true }, + ) ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) val anAccountProvider = AccountProvider(url = A_HOMESERVER_URL) initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(anAccountProvider)) + // Skip loading state + skipItems(1) val failureState = awaitItem() assertThat( (failureState.changeServerAction.errorOrNull() as ChangeServerError.NeedElementPro).unauthorisedAccountProviderTitle @@ -183,7 +176,7 @@ class ChangeServerPresenterTest { assertThat( (failureState.changeServerAction.errorOrNull() as ChangeServerError.NeedElementPro).applicationId ).isEqualTo("io.element.enterprise") - getElementWellKnownResult.assertions().isCalledOnce() + isEnterpriseBuildLambda.assertions().isCalledOnce() } } @@ -237,7 +230,7 @@ class ChangeServerPresenterTest { val permissionsPresenter = FakePermissionsPresenter() createPresenter( authenticationService = authenticationService, - enterpriseService = FakeEnterpriseService(isAllowedToConnectToHomeserverResult = { true }), + enterpriseService = FakeEnterpriseService(isAllowedToConnectToHomeserverResult = { true }, isElementProEnforcedResult = { false }), localNetworkPermissionAdvisor = FakeLocalNetworkPermissionAdvisor(shouldPrompt = true), permissionsPresenter = permissionsPresenter, ).test { @@ -259,16 +252,15 @@ class ChangeServerPresenterTest { authenticationService: FakeMatrixAuthenticationService = FakeMatrixAuthenticationService(), accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), enterpriseService: EnterpriseService = FakeEnterpriseService(), - wellknownRetriever: WellknownRetriever = FakeWellknownRetriever(), localNetworkPermissionAdvisor: FakeLocalNetworkPermissionAdvisor = FakeLocalNetworkPermissionAdvisor(), permissionsPresenter: FakePermissionsPresenter = FakePermissionsPresenter(), + isEnterpriseBuild: () -> Boolean = { false }, ) = ChangeServerPresenter( authenticationService = authenticationService, accountProviderDataSource = accountProviderDataSource, defaultAccountProviderAccessControl = DefaultAccountProviderAccessControl( enterpriseService = enterpriseService, - wellknownRetrieverFactory = FakeWellknownRetrieverFactory(wellknownRetriever = wellknownRetriever), - temporaryMatrixClientFactory = FakeTemporaryMatrixClientFactory(), + isEnterpriseBuild = isEnterpriseBuild, ), localNetworkPermissionGate = LocalNetworkPermissionGate( advisor = localNetworkPermissionAdvisor, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt index fbee19f05e..df3e3d2200 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt @@ -12,13 +12,12 @@ import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig import io.element.android.appconfig.OnBoardingConfig import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.features.enterprise.api.IsEnterpriseBuild import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate import io.element.android.features.login.impl.login.LoginModePresenter -import io.element.android.features.wellknown.test.FakeWellknownRetriever -import io.element.android.features.wellknown.test.FakeWellknownRetrieverFactory import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService @@ -29,7 +28,6 @@ import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.A_HOMESERVER_URL_2 import io.element.android.libraries.matrix.test.A_LOGIN_HINT -import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClientFactory import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.oauth.api.OAuthActionFlow @@ -41,7 +39,6 @@ import io.element.android.libraries.permissions.test.FakePermissionsPresenterFac import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.aSessionData -import io.element.android.libraries.wellknown.api.WellknownRetriever import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test import kotlinx.coroutines.flow.Flow @@ -186,6 +183,7 @@ class OnBoardingPresenterTest { enterpriseService = FakeEnterpriseService( defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG, EnterpriseService.ANY_ACCOUNT_PROVIDER) }, isAllowedToConnectToHomeserverResult = { true }, + isElementProEnforcedResult = { false }, ), ) presenter.test { @@ -259,6 +257,7 @@ class OnBoardingPresenterTest { ), enterpriseService = FakeEnterpriseService( isAllowedToConnectToHomeserverResult = { true }, + isElementProEnforcedResult = { false }, ), loginModePresenter = createLoginModePresenter( authenticationService = authenticationService, @@ -295,7 +294,7 @@ private fun createPresenter( ), buildMeta: BuildMeta = aBuildMeta(), enterpriseService: EnterpriseService = FakeEnterpriseService(), - wellknownRetriever: WellknownRetriever = FakeWellknownRetriever(), + isEnterpriseBuild: IsEnterpriseBuild = { false }, rageshakeFeatureAvailability: () -> Flow = { flowOf(true) }, loginModePresenter: LoginModePresenter = createLoginModePresenter(), onBoardingLogoResIdProvider: OnBoardingLogoResIdProvider = OnBoardingLogoResIdProvider { null }, @@ -307,8 +306,7 @@ private fun createPresenter( enterpriseService = enterpriseService, defaultAccountProviderAccessControl = DefaultAccountProviderAccessControl( enterpriseService = enterpriseService, - wellknownRetrieverFactory = FakeWellknownRetrieverFactory(wellknownRetriever = wellknownRetriever), - temporaryMatrixClientFactory = FakeTemporaryMatrixClientFactory(), + isEnterpriseBuild = isEnterpriseBuild, ), rageshakeFeatureAvailability = rageshakeFeatureAvailability, loginModePresenter = loginModePresenter, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt index ac1ba06cf6..c5a9b430ee 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt @@ -30,6 +30,7 @@ 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.data.ByteUnit +import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.analytics.GetDatabaseSizesUseCase import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.SessionId @@ -50,6 +51,7 @@ class DeveloperSettingsPresenter( private val databaseSizesUseCase: GetDatabaseSizesUseCase, private val fileSizeFormatter: FileSizeFormatter, private val markAllRoomsAsRead: MarkAllRoomsAsRead, + private val buildMeta: BuildMeta, ) : Presenter { @Composable override fun present(): DeveloperSettingsState { @@ -117,7 +119,7 @@ class DeveloperSettingsPresenter( databaseSizes = databaseSizes.value, clearCacheAction = clearCacheAction.value, markAllRoomsAsReadAction = markAllRoomsAsReadAction.value, - isEnterpriseBuild = enterpriseService.isEnterpriseBuild, + isEnterpriseBuild = buildMeta.isEnterpriseBuild, showColorPicker = showColorPicker, deviceId = deviceId, eventSink = ::handleEvent, 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 bb688f498d..3b2645783f 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 @@ -23,12 +23,14 @@ import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.data.megaBytes +import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.analytics.GetDatabaseSizesUseCase import io.element.android.libraries.matrix.api.analytics.SdkStoreSizes import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.A_DEVICE_ID import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -108,10 +110,8 @@ class DeveloperSettingsPresenterTest { fun `present - enterprise build can change the brand color`() = runTest { val overrideBrandColorResult = lambdaRecorder { _, _ -> } val presenter = createDeveloperSettingsPresenter( - enterpriseService = FakeEnterpriseService( - isEnterpriseBuild = true, - overrideBrandColorResult = overrideBrandColorResult, - ) + enterpriseService = FakeEnterpriseService(overrideBrandColorResult = overrideBrandColorResult), + buildMeta = aBuildMeta(isEnterpriseBuild = true), ) presenter.test { skipItems(1) @@ -179,6 +179,7 @@ class DeveloperSettingsPresenterTest { vacuumStoresUseCase: VacuumStoresUseCase = VacuumStoresUseCase {}, databaseSizesUseCase: GetDatabaseSizesUseCase = GetDatabaseSizesUseCase { Result.success(SdkStoreSizes(null, null, null, null)) }, markAllRoomsAsRead: FakeMarkAllRoomsAsRead = FakeMarkAllRoomsAsRead(), + buildMeta: BuildMeta = aBuildMeta(), ): DeveloperSettingsPresenter { return DeveloperSettingsPresenter( appDeveloperSettingsPresenter = { anAppDeveloperSettingsState() }, @@ -191,6 +192,7 @@ class DeveloperSettingsPresenterTest { databaseSizesUseCase = databaseSizesUseCase, fileSizeFormatter = FakeFileSizeFormatter(), markAllRoomsAsRead = markAllRoomsAsRead, + buildMeta = buildMeta, ) } } diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphrase.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphrase.kt deleted file mode 100644 index c7bc602dde..0000000000 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphrase.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.api - -/** - * Server-driven settings for a user-chosen recovery passphrase, advertised under the well-known - * `custom_recovery_passphrase` key. Today the only rule is a minimum character count; additional - * settings can be added here as the schema grows. - */ -data class CustomRecoveryPassphrase( - val minCharacterCount: Int, -) { - /** True when [input] meets every active rule. */ - fun isSatisfiedBy(input: String): Boolean = isSatisfiedBy(input.length) - - /** True when an input of [length] characters meets every active rule. */ - fun isSatisfiedBy(length: Int): Boolean = length >= minCharacterCount -} diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnownParser.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnownParser.kt deleted file mode 100644 index 1466a5b422..0000000000 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnownParser.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.api - -/** - * Parses the raw JSON of an Element `.well-known` document. - */ -fun interface ElementWellKnownParser { - /** - * Parses the document, failing when it is not valid JSON; unknown fields are ignored so a newer server does not break an older client. - * - * @param json the raw document as served by the server. - */ - fun parse(json: String): Result -} diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt deleted file mode 100644 index 93256fa6f1..0000000000 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellknownStore.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.api - -/** - * On-disk cache of the Element `.well-known` documents already fetched, so the app has a configuration to work with while offline. - */ -interface ElementWellknownStore { - /** - * Reads the cached document of a domain. - * Entries older than a day are reported as outdated rather than missing, so the caller can use them while refreshing; - * a document that no longer parses is reported as an error. - * - * @param domain the server whose cached document is requested. - */ - suspend fun get(domain: String): WellknownRetrieverResult - - /** - * Stores the document of a domain and stamps it as fetched now, replacing any previous entry. - * - * @param domain the server the document belongs to. - * @param wellknown the raw document, stored as-is rather than parsed. - */ - suspend fun update(domain: String, wellknown: String): Result - - /** - * Removes the cached document of a domain, used when the stored value turns out to be unusable. - * - * @param domain the server whose entry is removed. - */ - suspend fun delete(domain: String): Result - - /** - * Creates a store, one per configuration source so that their entries never collide. - */ - fun interface Factory { - /** - * @param prefix namespaces the cache keys, or `null` for the default well-known endpoint. - */ - fun create(prefix: String?): ElementWellknownStore - } -} diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/EnterpriseRemoteConfigSource.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/EnterpriseRemoteConfigSource.kt deleted file mode 100644 index 147f31269d..0000000000 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/EnterpriseRemoteConfigSource.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.api - -/** - * The source from which the enterprise remote configuration is retrieved. - */ -enum class EnterpriseRemoteConfigSource { - /** - * Retrieve the enterprise remote configuration from the Element Server Suite configuration (ESS_CONFIG) endpoint. - */ - ESS_CONFIG, - - /** - * Retrieve the enterprise remote configuration from the Element server's well-known endpoint. - */ - WELLKNOWN_ENDPOINT, -} diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt deleted file mode 100644 index 9e265bc213..0000000000 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.api - -import io.element.android.libraries.matrix.api.UrlContentFetcher - -/** - * Retrieves the Element specific `.well-known` configuration of a server, which is how a deployment advertises its own settings. - */ -interface WellknownRetriever { - /** - * Returns the configuration of a host, served from the local cache when possible. - * A cached value that has gone stale is returned as outdated while a refresh runs in the background, so this rarely waits on the network. - * An enterprise build with a hardcoded configuration always short-circuits and returns that instead. - * - * @param host the server to look up; a full URL is accepted and reduced to its host. - * @param source which endpoint to read the configuration from, which also selects the cache it is stored in. - */ - suspend fun getElementWellKnown( - host: String, - source: EnterpriseRemoteConfigSource - ): WellknownRetrieverResult - - /** - * Creates a retriever, which needs a fetcher because the configuration is read over HTTP. - */ - interface Factory { - /** - * @param urlContentFetcher used to perform the request; a client that is not authenticated yet also works. - */ - fun create(urlContentFetcher: UrlContentFetcher): WellknownRetriever - } -} diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultElementWellKnownParser.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultElementWellKnownParser.kt deleted file mode 100644 index 4cf72366e0..0000000000 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultElementWellKnownParser.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.impl - -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.ContributesBinding -import io.element.android.libraries.androidutils.json.JsonProvider -import io.element.android.libraries.core.extensions.runCatchingExceptions -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.ElementWellKnownParser - -@ContributesBinding(AppScope::class) -class DefaultElementWellKnownParser( - private val jsonProvider: JsonProvider, -) : ElementWellKnownParser { - override fun parse(json: String): Result { - return runCatchingExceptions { - val decoder = jsonProvider() - decoder.decodeFromString(json).map() - } - } -} diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt deleted file mode 100644 index 4d58e70db4..0000000000 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.impl - -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.AssistedInject -import dev.zacsweers.metro.ContributesBinding -import io.element.android.libraries.cachestore.api.CacheData -import io.element.android.libraries.cachestore.api.CacheStore -import io.element.android.libraries.core.extensions.runCatchingExceptions -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.ElementWellKnownParser -import io.element.android.libraries.wellknown.api.ElementWellknownStore -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult -import io.element.android.services.toolbox.api.systemclock.SystemClock - -@AssistedInject -class DefaultWellKnownStore( - @Assisted - private val prefix: String?, - private val cacheStore: CacheStore, - private val elementWellKnownParser: ElementWellKnownParser, - private val systemClock: SystemClock, -) : ElementWellknownStore { - @AssistedFactory - @ContributesBinding(AppScope::class) - fun interface Factory : ElementWellknownStore.Factory { - override fun create(prefix: String?): DefaultWellKnownStore - } - override suspend fun get(domain: String): WellknownRetrieverResult { - return runCatchingExceptions { - val cachedData = cacheStore.getData(key(domain)) - if (cachedData != null) { - val data = elementWellKnownParser.parse(cachedData.value).getOrThrow() - if (systemClock.epochMillis() > cachedData.updatedAt + CACHE_VALIDITY_MILLIS) { - WellknownRetrieverResult.Outdated(data) - } else { - WellknownRetrieverResult.Success(data) - } - } else { - WellknownRetrieverResult.NotFound - } - }.recover { - WellknownRetrieverResult.Error(it as Exception) - }.getOrThrow() - } - - override suspend fun update(domain: String, wellknown: String): Result { - return runCatchingExceptions { - cacheStore.storeData(key(domain), CacheData(wellknown, systemClock.epochMillis())) - } - } - - override suspend fun delete(domain: String): Result { - return runCatchingExceptions { - cacheStore.deleteData(key(domain)) - } - } - - private fun key(domain: String): String = if (prefix != null) { - "$prefix-$domain" - } else { - "https://$domain/.well-known/element/element.json" - } - - companion object { - // 1 day - private const val CACHE_VALIDITY_MILLIS = 1 * 24 * 60 * 60 * 1000L - } -} diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt deleted file mode 100644 index 60e8c621e2..0000000000 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.impl - -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.AssistedInject -import dev.zacsweers.metro.ContributesBinding -import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.libraries.androidutils.text.takeIfNotBlank -import io.element.android.libraries.core.extensions.mapCatchingExceptions -import io.element.android.libraries.core.extensions.runCatchingExceptions -import io.element.android.libraries.core.uri.ensureProtocol -import io.element.android.libraries.di.annotations.AppCoroutineScope -import io.element.android.libraries.matrix.api.UrlContentFetcher -import io.element.android.libraries.matrix.api.exception.ClientException -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.ElementWellKnownParser -import io.element.android.libraries.wellknown.api.ElementWellknownStore -import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource -import io.element.android.libraries.wellknown.api.WellknownRetriever -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import timber.log.Timber -import java.net.URL - -@AssistedInject -class DefaultWellknownRetriever( - private val elementWellknownStoreFactory: ElementWellknownStore.Factory, - private val enterpriseService: EnterpriseService, - private val elementWellKnownParser: ElementWellKnownParser, - @Assisted private val urlContentFetcher: UrlContentFetcher, - @AppCoroutineScope private val coroutineScope: CoroutineScope, -) : WellknownRetriever { - @ContributesBinding(AppScope::class) - @AssistedFactory - fun interface Factory : WellknownRetriever.Factory { - override fun create(urlContentFetcher: UrlContentFetcher): DefaultWellknownRetriever - } - - override suspend fun getElementWellKnown( - host: String, - source: EnterpriseRemoteConfigSource - ): WellknownRetrieverResult { - val overriddenElementWellKnown = enterpriseService.overriddenElementWellKnown() - if (overriddenElementWellKnown != null) { - return WellknownRetrieverResult.Success(overriddenElementWellKnown) - } - - // The passed `host` function may be a full URL, in that case we're only interested in the actual host - val checkedHost = runCatchingExceptions { URL(host.ensureProtocol()).host.takeIfNotBlank() } - .getOrElse { return WellknownRetrieverResult.Error(IllegalArgumentException("host parameter is not valid", it)) } - ?: host - - // We instantiate different stores for different sources, so that we can cache them separately. - // The ESS_CONFIG source is cached with a prefix to differentiate it from the WELLKNOWN_ENDPOINT. - val prefix = when (source) { - EnterpriseRemoteConfigSource.ESS_CONFIG -> "ess_config" - EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT -> null - } - val store = elementWellknownStoreFactory.create(prefix) - return when (val cacheData = store.get(checkedHost)) { - is WellknownRetrieverResult.Success -> { - Timber.d("Using cached well-known for domain $checkedHost") - cacheData - } - is WellknownRetrieverResult.Outdated -> { - // Return the outdated data but refresh in the background - // If the cache is missing or outdated, trigger a refresh in background but still return the cached value - Timber.d("Outdated cached well-known for domain $checkedHost, returning existing value and fetching new one from network") - coroutineScope.launch { fetchElementWellKnown(checkedHost, source, store) } - cacheData - } - is WellknownRetrieverResult.NotFound -> { - // Try to fetch from the server - Timber.d("No cached well-known for domain $checkedHost, fetching from network") - fetchElementWellKnown(checkedHost, source, store) - } - is WellknownRetrieverResult.Error -> { - // Return the error - Timber.e(cacheData.exception, "Error retrieving well-known for domain $checkedHost") - cacheData.exception.toWellknownRetrieverResult() - } - } - } - - private suspend fun fetchElementWellKnown( - host: String, - source: EnterpriseRemoteConfigSource, - store: ElementWellknownStore - ): WellknownRetrieverResult { - return urlContentFetcher - .getUrl(url(host, source)) - .mapCatchingExceptions { - val data = String(it) - val parsed = elementWellKnownParser.parse(data).getOrThrow() - // Also store in cache, if valid - store.update(host, data) - .onFailure { exception -> - Timber.e(exception, "Failed to parse cached Element .well-known data for $host, deleting cache") - store.delete(host) - } - parsed - } - .toWellknownRetrieverResult(host) - } - - private fun Result.toWellknownRetrieverResult(host: String): WellknownRetrieverResult = fold( - onSuccess = { - WellknownRetrieverResult.Success(it) - }, - onFailure = { - Timber.e(it, "Failed to retrieve Element .well-known from $host") - // This check on message value is not ideal but this is what we got from the SDK. - it.toWellknownRetrieverResult() - } - ) - - private fun Throwable.toWellknownRetrieverResult(): WellknownRetrieverResult { - return if ((this as? ClientException.Generic)?.message?.contains("404") == true) { - WellknownRetrieverResult.NotFound - } else { - WellknownRetrieverResult.Error(this as Exception) - } - } - - private fun url(host: String, source: EnterpriseRemoteConfigSource): String { - val elementWellKnownUrl = "https://$host/.well-known/element/element.json" - return when (source) { - EnterpriseRemoteConfigSource.ESS_CONFIG -> enterpriseService.essConfigEndpointUrl(host) ?: elementWellKnownUrl - EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT -> elementWellKnownUrl - } - } -} diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt deleted file mode 100644 index 317109a1c0..0000000000 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.impl - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -/** - * Example: - *
- * {
- *     "registration_helper_url": "https://element.io",
- *     "enforce_element_pro": true,
- *     "rageshake_url": "https://example.org/rageshake",
- *     "brand_color": "#FF0000",
- *     "notification_sound": "ring.flac",
- *     "idp_app_scheme": "io.element.app",
- *     "custom_recovery_passphrase": {
- *         "min_character_count": 8
- *     }
- * }
- * 
- * . - */ -@Serializable -data class InternalElementWellKnown( - @SerialName("registration_helper_url") - val registrationHelperUrl: String? = null, - @SerialName("enforce_element_pro") - val enforceElementPro: Boolean? = null, - @SerialName("rageshake_url") - val rageshakeUrl: String? = null, - @SerialName("brand_color") - val brandColor: String? = null, - @SerialName("notification_sound") - val notificationSound: String? = null, - @SerialName("idp_app_scheme") - val identityProviderAppScheme: String? = null, - @SerialName("custom_recovery_passphrase") - val customRecoveryPassphrase: InternalCustomRecoveryPassphrase? = null, - @SerialName("content_scanner_url") - val contentScannerUrl: String? = null, - @SerialName("force_disable_e2ee") - val forceDisableE2EE: Boolean? = null, - @SerialName("map_tiler") - val mapTilerConfig: InternalMapTilerConfig? = null, -) - -@Serializable -data class InternalCustomRecoveryPassphrase( - @SerialName("min_character_count") - val minCharacterCount: Int? = null, -) - -@Serializable -data class InternalMapTilerConfig( - @SerialName("api_key") - val apiKey: String, - @SerialName("light_style_id") - val lightStyleId: String? = null, - @SerialName("dark_style_id") - val darkStyleId: String? = null, - @SerialName("base_url") - val baseUrl: String? = null, -) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalWellKnown.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalWellKnown.kt deleted file mode 100644 index bf43356c45..0000000000 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalWellKnown.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.impl - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -/** - * https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery - *
- * {
- *     "m.homeserver": {
- *         "base_url": "https://matrix.org"
- *     },
- *     "m.identity_server": {
- *         "base_url": "https://vector.im"
- *     },
- * }
- * 
- * . - */ -@Serializable -data class InternalWellKnown( - @SerialName("m.homeserver") - val homeServer: InternalWellKnownBaseConfig? = null, - @SerialName("m.identity_server") - val identityServer: InternalWellKnownBaseConfig? = null, -) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalWellKnownBaseConfig.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalWellKnownBaseConfig.kt deleted file mode 100644 index 342cd43395..0000000000 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalWellKnownBaseConfig.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.impl - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -/** - * https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery - *
- * {
- *     "base_url": "https://element.io"
- * }
- * 
- * . - */ -@Serializable -data class InternalWellKnownBaseConfig( - @SerialName("base_url") - val baseURL: String? = null -) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt deleted file mode 100644 index 254036c5c3..0000000000 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.impl - -import io.element.android.libraries.core.log.logger.LoggerTag -import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.MapTilerConfig -import timber.log.Timber - -private val loggerTag = LoggerTag("Wellknown") - -/** - * Floor for the recovery passphrase minimum length. A value of 1 guarantees the derived passphrase is - * never empty (an empty passphrase derives a recoverable but effectively unprotected secret-storage - * key). Anything stronger than non-empty is left to the homeserver operator's configuration. - */ -private const val MINIMUM_PASSPHRASE_CHARACTER_COUNT = 1 - -fun InternalElementWellKnown.map() = ElementWellKnown( - registrationHelperUrl = registrationHelperUrl, - enforceElementPro = enforceElementPro, - rageshakeUrl = rageshakeUrl, - brandColor = brandColor, - notificationSound = notificationSound, - identityProviderAppScheme = identityProviderAppScheme, - customRecoveryPassphrase = customRecoveryPassphrase?.toPublic(), - contentScannerUrl = contentScannerUrl, - forceDisableE2EE = forceDisableE2EE, - mapTilerConfig = mapTilerConfig?.let { - MapTilerConfig( - apiKey = it.apiKey, - lightStyleId = it.lightStyleId, - darkStyleId = it.darkStyleId, - baseUrl = it.baseUrl, - ) - } -) - -private fun InternalCustomRecoveryPassphrase.toPublic(): CustomRecoveryPassphrase { - // Whenever the homeserver advertises the settings block we run the custom passphrase flow, flooring - // the minimum at 1 so the passphrase can never be empty even if the server omits min_character_count - // or advertises a non-positive value. The operator owns any stronger minimum. - val min = (minCharacterCount ?: MINIMUM_PASSPHRASE_CHARACTER_COUNT).coerceAtLeast(MINIMUM_PASSPHRASE_CHARACTER_COUNT) - if (min != minCharacterCount) { - Timber.tag(loggerTag.value).w( - "custom_recovery_passphrase.min_character_count was %s; flooring to %d", - minCharacterCount, - min, - ) - } - return CustomRecoveryPassphrase(minCharacterCount = min) -} diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt deleted file mode 100644 index 1587e95765..0000000000 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/SessionWellknownModule.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.impl - -import dev.zacsweers.metro.BindingContainer -import dev.zacsweers.metro.ContributesTo -import dev.zacsweers.metro.Provides -import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.di.annotations.SessionCoroutineScope -import io.element.android.libraries.matrix.api.UrlContentFetcher -import io.element.android.libraries.wellknown.api.ElementWellKnownParser -import io.element.android.libraries.wellknown.api.ElementWellknownStore -import io.element.android.libraries.wellknown.api.WellknownRetriever -import kotlinx.coroutines.CoroutineScope - -@BindingContainer -@ContributesTo(SessionScope::class) -object SessionWellknownModule { - @Provides - fun provideWellKnownRetriever( - elementWellknownStoreFactory: ElementWellknownStore.Factory, - enterpriseService: EnterpriseService, - elementWellKnownParser: ElementWellKnownParser, - urlContentFetcher: UrlContentFetcher, - @SessionCoroutineScope coroutineScope: CoroutineScope, - ): WellknownRetriever { - return DefaultWellknownRetriever( - elementWellknownStoreFactory = elementWellknownStoreFactory, - enterpriseService = enterpriseService, - elementWellKnownParser = elementWellKnownParser, - urlContentFetcher = urlContentFetcher, - coroutineScope = coroutineScope - ) - } -} diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/WellknownAPI.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/WellknownAPI.kt deleted file mode 100644 index 54528f7c42..0000000000 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/WellknownAPI.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.impl - -import retrofit2.http.GET - -internal interface WellknownAPI { - @GET(".well-known/matrix/client") - suspend fun getWellKnown(): InternalWellKnown - - @GET(".well-known/element/element.json") - suspend fun getElementWellKnown(): InternalElementWellKnown -} diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseTest.kt deleted file mode 100644 index d4e7ac112e..0000000000 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseTest.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.api - -import com.google.common.truth.Truth.assertThat -import org.junit.Test - -class CustomRecoveryPassphraseTest { - @Test - fun `empty input never satisfies a positive minimum`() { - assertThat(CustomRecoveryPassphrase(minCharacterCount = 1).isSatisfiedBy("")).isFalse() - } - - @Test - fun `input shorter than minimum is not satisfied`() { - assertThat(CustomRecoveryPassphrase(minCharacterCount = 8).isSatisfiedBy("abc")).isFalse() - } - - @Test - fun `input exactly at minimum is satisfied`() { - assertThat(CustomRecoveryPassphrase(minCharacterCount = 8).isSatisfiedBy("abcdefgh")).isTrue() - } - - @Test - fun `input longer than minimum is satisfied`() { - assertThat(CustomRecoveryPassphrase(minCharacterCount = 4).isSatisfiedBy("abcdefgh")).isTrue() - } -} diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt deleted file mode 100644 index dc101c41a0..0000000000 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.wellknown.impl - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.wellknown.test.anElementWellKnown -import io.element.android.libraries.androidutils.json.DefaultJsonProvider -import io.element.android.libraries.cachestore.api.CacheData -import io.element.android.libraries.sessionstorage.test.InMemoryCacheStore -import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase -import io.element.android.libraries.wellknown.api.ElementWellKnownParser -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult -import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP -import io.element.android.services.toolbox.test.systemclock.FakeSystemClock -import kotlinx.coroutines.test.runTest -import org.junit.Test - -private const val A_DOMAIN = "matrix.example.com" -private const val A_CACHE_KEY = "https://$A_DOMAIN/.well-known/element/element.json" - -// 1 day in millis -private const val CACHE_VALIDITY_MILLIS = 1 * 24 * 60 * 60 * 1000L - -class DefaultWellKnownStoreTest { - private val jsonProvider = DefaultJsonProvider() - - @Test - fun `get returns NotFound when cache is empty`() = runTest { - val sut = createDefaultWellKnownStore() - assertThat(sut.get(A_DOMAIN)).isEqualTo(WellknownRetrieverResult.NotFound) - } - - @Test - fun `get returns Success when cache has fresh data`() = runTest { - val wellKnown = InternalElementWellKnown(rageshakeUrl = "https://rageshake.example.com") - val jsonString = jsonProvider().encodeToString(wellKnown) - val clock = FakeSystemClock(epochMillisResult = A_FAKE_TIMESTAMP) - val cacheStore = InMemoryCacheStore( - initialData = mapOf(A_CACHE_KEY to CacheData(jsonString, A_FAKE_TIMESTAMP)) - ) - val sut = createDefaultWellKnownStore(cacheStore = cacheStore, systemClock = clock) - - val result = sut.get(A_DOMAIN) - - assertThat(result).isEqualTo(WellknownRetrieverResult.Success(wellKnown.map())) - } - - @Test - fun `get returns Outdated when cached data has expired`() = runTest { - val wellKnown = InternalElementWellKnown(rageshakeUrl = "https://rageshake.example.com") - val jsonString = jsonProvider().encodeToString(wellKnown) - // Store data at timestamp 0, but clock is past the validity window - val expiredTimestamp = 0L - val nowTimestamp = expiredTimestamp + CACHE_VALIDITY_MILLIS + 1 - val clock = FakeSystemClock(epochMillisResult = nowTimestamp) - val cacheStore = InMemoryCacheStore( - initialData = mapOf(A_CACHE_KEY to CacheData(jsonString, expiredTimestamp)) - ) - val sut = createDefaultWellKnownStore(cacheStore = cacheStore, systemClock = clock) - - val result = sut.get(A_DOMAIN) - - assertThat(result).isEqualTo(WellknownRetrieverResult.Outdated(wellKnown.map())) - } - - @Test - fun `get returns Error when cached data is invalid JSON`() = runTest { - val cacheStore = InMemoryCacheStore( - initialData = mapOf(A_CACHE_KEY to CacheData("not valid json at all", A_FAKE_TIMESTAMP)) - ) - val sut = createDefaultWellKnownStore(cacheStore = cacheStore) - - val result = sut.get(A_DOMAIN) - - assertThat(result).isInstanceOf(WellknownRetrieverResult.Error::class.java) - } - - @Test - fun `update stores data and get returns Success`() = runTest { - val wellKnown = """ - { - "registration_helper_url": "https://element.io", - "enforce_element_pro": true, - "rageshake_url": "https://example.org/rageshake", - "brand_color": "#FF0000", - "notification_sound": "ring.flac", - "idp_app_scheme": "io.element.app", - "custom_recovery_passphrase": { - "min_character_count": 8 - } - } - """.trimIndent() - val clock = FakeSystemClock(epochMillisResult = A_FAKE_TIMESTAMP) - val cacheStore = InMemoryCacheStore() - val sut = createDefaultWellKnownStore(cacheStore = cacheStore, systemClock = clock) - - val updateResult = sut.update(A_DOMAIN, wellKnown) - assertThat(updateResult.isSuccess).isTrue() - - val result = sut.get(A_DOMAIN) - assertThat(result).isEqualTo(WellknownRetrieverResult.Success(anElementWellKnown( - registrationHelperUrl = "https://element.io", - enforceElementPro = true, - rageshakeUrl = "https://example.org/rageshake", - brandColor = "#FF0000", - notificationSound = "ring.flac", - identityProviderAppScheme = "io.element.app", - customRecoveryPassphrase = CustomRecoveryPassphrase(8), - ))) - } - - @Test - fun `delete removes data and get returns NotFound`() = runTest { - val wellKnown = InternalElementWellKnown(rageshakeUrl = "https://rageshake.example.com") - val jsonString = jsonProvider().encodeToString(wellKnown) - val clock = FakeSystemClock(epochMillisResult = A_FAKE_TIMESTAMP) - val cacheStore = InMemoryCacheStore( - initialData = mapOf(A_CACHE_KEY to CacheData(jsonString, A_FAKE_TIMESTAMP)) - ) - val sut = createDefaultWellKnownStore(cacheStore = cacheStore, systemClock = clock) - - val deleteResult = sut.delete(A_DOMAIN) - assertThat(deleteResult.isSuccess).isTrue() - - assertThat(sut.get(A_DOMAIN)).isEqualTo(WellknownRetrieverResult.NotFound) - } - - @Test - fun `get returns data at the exact cache validity boundary as Success`() = runTest { - val wellKnown = InternalElementWellKnown(notificationSound = "ping.flac") - val jsonString = jsonProvider().encodeToString(wellKnown) - // Exactly at the boundary: epochMillis == updatedAt + CACHE_VALIDITY_MILLIS → not expired - val storedAt = 1000L - val nowAtBoundary = storedAt + CACHE_VALIDITY_MILLIS - val clock = FakeSystemClock(epochMillisResult = nowAtBoundary) - val cacheStore = InMemoryCacheStore( - initialData = mapOf(A_CACHE_KEY to CacheData(jsonString, storedAt)) - ) - val sut = createDefaultWellKnownStore(cacheStore = cacheStore, systemClock = clock) - - val result = sut.get(A_DOMAIN) - - assertThat(result).isEqualTo(WellknownRetrieverResult.Success(wellKnown.map())) - } - - @Test - fun `prefix is used`() = runTest { - val prefix = "test-prefix" - val clock = FakeSystemClock(epochMillisResult = A_FAKE_TIMESTAMP) - val cacheStore = InMemoryCacheStore( - initialData = mapOf("$prefix-$A_DOMAIN" to CacheData("{}", A_FAKE_TIMESTAMP)) - ) - val sut = createDefaultWellKnownStore(prefix = prefix, cacheStore = cacheStore, systemClock = clock) - - assertThat(sut.get(A_DOMAIN)).isInstanceOf(WellknownRetrieverResult.Success::class.java) - } - - private fun createDefaultWellKnownStore( - prefix: String? = null, - cacheStore: InMemoryCacheStore = InMemoryCacheStore(), - systemClock: FakeSystemClock = FakeSystemClock(), - elementWellKnownParser: ElementWellKnownParser = DefaultElementWellKnownParser(jsonProvider), - ) = DefaultWellKnownStore( - prefix = prefix, - cacheStore = cacheStore, - systemClock = systemClock, - elementWellKnownParser = elementWellKnownParser, - ) -} diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt deleted file mode 100644 index a58aa0a347..0000000000 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -@file:OptIn(ExperimentalCoroutinesApi::class) - -package io.element.android.libraries.wellknown.impl - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.enterprise.test.FakeEnterpriseService -import io.element.android.features.wellknown.test.FakeElementWellknownStore -import io.element.android.features.wellknown.test.aMapTilerConfig -import io.element.android.features.wellknown.test.anElementWellKnown -import io.element.android.libraries.core.uri.ensureProtocol -import io.element.android.libraries.matrix.api.UrlContentFetcher -import io.element.android.libraries.matrix.api.exception.ClientException -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.ElementWellKnownParser -import io.element.android.libraries.wellknown.api.ElementWellknownStore -import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult -import io.element.android.tests.testutils.lambda.lambdaRecorder -import io.element.android.tests.testutils.lambda.value -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Test -import java.net.URL - -class DefaultWellknownRetrieverTest { - @Test - fun `get element wellknown calls resolver and parser`() = runTest { - val parser = lambdaRecorder> { Result.success(anElementWellKnown()) } - val resolver = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) } - val sut = createDefaultWellknownRetriever( - elementWellKnownParser = parser, - urlResolver = resolver, - ) - - sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT) - - resolver.assertions().isCalledOnce().with(value(WELLKNOWN_URL)) - parser.assertions().isCalledOnce().with(value(WELLKNOWN_CONTENT)) - } - - @Test - fun `get element wellknown parser error`() = runTest { - val sut = createDefaultWellknownRetriever( - elementWellKnownParser = { Result.failure(IllegalStateException("Invalid JSON")) }, - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) - .isInstanceOf(WellknownRetrieverResult.Error::class.java) - } - - @Test - fun `get element wellknown network error`() = runTest { - val resolver = SpyUrlContentFetcher { Result.failure(IllegalStateException("Network error")) } - val sut = createDefaultWellknownRetriever(urlResolver = resolver) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) - .isInstanceOf(WellknownRetrieverResult.Error::class.java) - } - - @Test - fun `get element wellknown 404 http error counts as not found`() = runTest { - val resolver = SpyUrlContentFetcher { Result.failure(ClientException.Generic("Http error: 404", null)) } - val sut = createDefaultWellknownRetriever(urlResolver = resolver) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) - .isInstanceOf(WellknownRetrieverResult.NotFound::class.java) - } - - @Test - fun `get element wellknown will return an error if the provided host is not valid`() = runTest { - val sut = createDefaultWellknownRetriever() - assertThat(sut.getElementWellKnown("!not valid", EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) - .isInstanceOf(WellknownRetrieverResult.Error::class.java) - } - - @Test - fun `get element wellknown will return a cached value if present instead of fetching`() = runTest { - val host = URL(WELLKNOWN_URL).host.ensureProtocol() - val sut = createDefaultWellknownRetriever( - cacheStoreFactory = { FakeElementWellknownStore(initialData = mapOf(host to WellknownRetrieverResult.Success(expectedElementWellKnown))) } - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) - .isInstanceOf(WellknownRetrieverResult.Success::class.java) - } - - @Test - fun `get element wellknown will return an outdated value first then fetch the current one`() = runTest { - val host = URL(WELLKNOWN_URL).host.ensureProtocol() - val resolver = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) } - val sut = createDefaultWellknownRetriever( - urlResolver = resolver, - cacheStoreFactory = { FakeElementWellknownStore(initialData = mapOf(host to WellknownRetrieverResult.Outdated(expectedElementWellKnown))) } - ) - // The first call returns the outdated value - assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) - .isInstanceOf(WellknownRetrieverResult.Outdated::class.java) - // The resolver is not called yet because the fetch is done in the background - resolver.assertions().isNeverCalled() - - // We give it some time to run the background fetch - runCurrent() - - // The resolver is called during the background fetch - resolver.assertions().isCalledOnce().with(value(WELLKNOWN_URL)) - } - - @Test - fun `get element wellknown will return an error if it happens when trying to retrieve the cached value`() = runTest { - val host = URL(WELLKNOWN_URL).host.ensureProtocol() - val sut = createDefaultWellknownRetriever( - cacheStoreFactory = { FakeElementWellknownStore(initialData = mapOf(host to WellknownRetrieverResult.Error(IllegalStateException("BOOM")))) } - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) - .isInstanceOf(WellknownRetrieverResult.Error::class.java) - } - - @Test - fun `get element wellknown will return an error if it fails to serialize the retrieved value into the cache`() = runTest { - val sut = createDefaultWellknownRetriever( - elementWellKnownParser = { Result.failure(IllegalStateException("BOOM")) }, - ) - assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)) - .isInstanceOf(WellknownRetrieverResult.Error::class.java) - } - - @Test - fun `WELLKNOWN_ENDPOINT source creates a store with no prefix`() = runTest { - val storeFactory = lambdaRecorder { FakeElementWellknownStore() } - val sut = createDefaultWellknownRetriever(cacheStoreFactory = storeFactory) - - sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT) - - storeFactory.assertions().isCalledOnce().with(value(null)) - } - - @Test - fun `ESS_CONFIG source creates a store with ess prefix and checks the ESS config endpoint`() = runTest { - val essConfigUrl = "https://ess-config.example.com" - val storeFactory = lambdaRecorder { FakeElementWellknownStore() } - val resolver = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) } - val sut = createDefaultWellknownRetriever( - cacheStoreFactory = storeFactory, - enterpriseService = FakeEnterpriseService( - overrideWellKnownResult = { null }, - essConfigEndpointUrlResult = { essConfigUrl }, - ), - urlResolver = resolver, - ) - - sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.ESS_CONFIG) - - storeFactory.assertions().isCalledOnce().with(value("ess_config")) - resolver.assertions().isCalledOnce().with(value(essConfigUrl)) - } - - @Test - fun `get element wellknown was overridden`() = runTest { - val wellKnown = anElementWellKnown() - - val resolver = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) } - - val sut = createDefaultWellknownRetriever( - enterpriseService = FakeEnterpriseService( - overrideWellKnownResult = { wellKnown }, - ), - urlResolver = resolver, - ) - - // The overridden value is returned - assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)).isEqualTo( - WellknownRetrieverResult.Success(wellKnown) - ) - - // And the endpoint is never hit - resolver.assertions().isNeverCalled() - } - - private fun TestScope.createDefaultWellknownRetriever( - cacheStoreFactory: ElementWellknownStore.Factory = { FakeElementWellknownStore() }, - elementWellKnownParser: ElementWellKnownParser = { Result.success(expectedElementWellKnown) }, - enterpriseService: FakeEnterpriseService = FakeEnterpriseService(overrideWellKnownResult = { null }), - urlResolver: UrlContentFetcher = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) }, - ) = DefaultWellknownRetriever( - elementWellknownStoreFactory = cacheStoreFactory, - enterpriseService = enterpriseService, - elementWellKnownParser = elementWellKnownParser, - urlContentFetcher = urlResolver, - coroutineScope = backgroundScope, - ) - - companion object { - private const val WELLKNOWN_URL = "https://user.domain.org/.well-known/element/element.json" - private const val WELLKNOWN_CONTENT = """{ - "registration_helper_url": "a_registration_url", - "enforce_element_pro": true, - "rageshake_url": "a_rageshake_url", - "brand_color": "#FF0000", - "notification_sound": "a_notification_sound.flac", - "idp_app_scheme": "an_app_scheme", - "content_scanner_url": "https://content-scanner.example.com", - "force_disable_e2ee": false, - "map_tiler": { - "api_key": "test_api_key", - "base_url": "https://tile-server.example.com" - } - }""" - - private val expectedElementWellKnown = ElementWellKnown( - registrationHelperUrl = "a_registration_url", - enforceElementPro = true, - rageshakeUrl = "a_rageshake_url", - brandColor = "#FF0000", - notificationSound = "a_notification_sound.flac", - identityProviderAppScheme = "an_app_scheme", - contentScannerUrl = "https://content-scanner.example.com", - customRecoveryPassphrase = null, - forceDisableE2EE = false, - mapTilerConfig = aMapTilerConfig( - apiKey = "test_api_key", - baseUrl = "https://tile-server.example.com", - ) - ) - } -} - -private class SpyUrlContentFetcher(lambda: (String) -> Result) : UrlContentFetcher { - private val spy = lambdaRecorder> { lambda(it) } - - override suspend fun getUrl(url: String): Result { - return spy(url) - } - - fun assertions() = spy.assertions() -} diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeElementWellknownStore.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeElementWellknownStore.kt deleted file mode 100644 index c5e2d2ae5b..0000000000 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeElementWellknownStore.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.wellknown.test - -import io.element.android.libraries.androidutils.json.DefaultJsonProvider -import io.element.android.libraries.androidutils.json.JsonProvider -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.ElementWellknownStore -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult -import io.element.android.libraries.wellknown.impl.InternalElementWellKnown -import io.element.android.libraries.wellknown.impl.map -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.json.JsonDecodingException -import java.net.URL - -class FakeElementWellknownStore( - initialData: Map> = emptyMap(), - private val jsonProvider: JsonProvider = DefaultJsonProvider(), -) : ElementWellknownStore { - private val cachedData = mutableMapOf>() - - init { - initialData.forEach { (url, result) -> - cachedData[URL(url).host] = result - } - } - - override suspend fun get(domain: String): WellknownRetrieverResult { - return cachedData[domain] ?: WellknownRetrieverResult.NotFound - } - - @OptIn(ExperimentalSerializationApi::class) - override suspend fun update(domain: String, wellknown: String): Result { - val json = jsonProvider() - val mapped = try { - json.decodeFromString(wellknown).map() - } catch (e: JsonDecodingException) { - return Result.failure(e) - } - cachedData[domain] = WellknownRetrieverResult.Success(mapped) - return Result.success(Unit) - } - - override suspend fun delete(domain: String): Result { - cachedData.remove(domain) - return Result.success(Unit) - } -} diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt deleted file mode 100644 index e08969d0f9..0000000000 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.wellknown.test - -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource -import io.element.android.libraries.wellknown.api.WellknownRetriever -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult - -class FakeWellknownRetriever( - private val getElementWellKnownResult: (String, EnterpriseRemoteConfigSource) -> WellknownRetrieverResult = { _, _ -> - WellknownRetrieverResult.NotFound - }, -) : WellknownRetriever { - override suspend fun getElementWellKnown(host: String, source: EnterpriseRemoteConfigSource): WellknownRetrieverResult { - return getElementWellKnownResult(host, source) - } -} diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt deleted file mode 100644 index 718649dd5d..0000000000 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetrieverFactory.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.wellknown.test - -import io.element.android.libraries.matrix.api.UrlContentFetcher -import io.element.android.libraries.wellknown.api.WellknownRetriever - -class FakeWellknownRetrieverFactory( - private val wellknownRetriever: WellknownRetriever = FakeWellknownRetriever(), -) : WellknownRetriever.Factory { - override fun create(urlContentFetcher: UrlContentFetcher): WellknownRetriever = wellknownRetriever -} diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt deleted file mode 100644 index 36d9f7bc82..0000000000 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.wellknown.test - -import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase -import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.MapTilerConfig - -fun anElementWellKnown( - registrationHelperUrl: String? = null, - enforceElementPro: Boolean? = null, - rageshakeUrl: String? = null, - brandColor: String? = null, - notificationSound: String? = null, - identityProviderAppScheme: String? = null, - customRecoveryPassphrase: CustomRecoveryPassphrase? = null, - contentScannerUrl: String? = null, - forceDisableE2EE: Boolean? = null, - mapTilerConfig: MapTilerConfig? = null, -) = ElementWellKnown( - registrationHelperUrl = registrationHelperUrl, - enforceElementPro = enforceElementPro, - rageshakeUrl = rageshakeUrl, - brandColor = brandColor, - notificationSound = notificationSound, - identityProviderAppScheme = identityProviderAppScheme, - customRecoveryPassphrase = customRecoveryPassphrase, - contentScannerUrl = contentScannerUrl, - forceDisableE2EE = forceDisableE2EE, - mapTilerConfig = mapTilerConfig, -) - -fun aCustomRecoveryPassphrase( - minCharacterCount: Int = 8, -) = CustomRecoveryPassphrase( - minCharacterCount = minCharacterCount, -) - -fun aMapTilerConfig( - apiKey: String = "test_api_key", - baseUrl: String? = null, - lightStyleId: String? = null, - darkStyleId: String? = null, -) = MapTilerConfig( - apiKey = apiKey, - baseUrl = baseUrl, - lightStyleId = lightStyleId, - darkStyleId = darkStyleId, -) diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogEndpointConfigProvider.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogEndpointConfigProvider.kt index eb59579d79..01f4329111 100644 --- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogEndpointConfigProvider.kt +++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogEndpointConfigProvider.kt @@ -9,7 +9,6 @@ package io.element.android.services.analyticsproviders.posthog import dev.zacsweers.metro.Inject -import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.core.extensions.isElement import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildType @@ -17,10 +16,9 @@ import io.element.android.libraries.core.meta.BuildType @Inject class PosthogEndpointConfigProvider( private val buildMeta: BuildMeta, - private val enterpriseService: EnterpriseService, ) { fun provide(): PosthogEndpointConfig? { - return if (enterpriseService.isEnterpriseBuild) { + return if (buildMeta.isEnterpriseBuild) { PosthogEndpointConfig( host = BuildConfig.POSTHOG_HOST, apiKey = BuildConfig.POSTHOG_APIKEY, From 8e02f0354347baa2af17d8d34cd56486c999dd00 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 16:35:31 +0300 Subject: [PATCH 227/338] Cover the branches left untested by the uri short circuit An intent carrying uris is now accepted before the mime type list is reached, so the image check that every uri share used to exercise is only covered by an intent which has nothing to send. Add one case per listed mime type without a uri. Cover the rest of the handler while it is under test: an intent with no resolvable type, a send multiple without a stream, an action which is neither send nor send multiple, an empty caption, a plain text intent without text, the mime type reported by the content resolver for the uri, and the explicit intent built for the application which will read it, both when the read permission can be granted and when it cannot. --- .../impl/DefaultShareIntentHandlerTest.kt | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) diff --git a/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandlerTest.kt b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandlerTest.kt index 8cde31bd0c..d81bbe053d 100644 --- a/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandlerTest.kt +++ b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareIntentHandlerTest.kt @@ -7,15 +7,26 @@ package io.element.android.features.share.impl +import android.content.ComponentName +import android.content.ContentProvider +import android.content.ContentValues +import android.content.Context +import android.content.ContextWrapper import android.content.Intent +import android.content.IntentFilter +import android.database.Cursor import android.net.Uri import androidx.core.net.toUri import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage import io.element.android.features.share.api.ShareIntentData import io.element.android.features.share.api.UriToShare +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.tests.testutils.robolectric.RobolectricTest import org.junit.Test +import org.robolectric.Robolectric import org.robolectric.RuntimeEnvironment +import org.robolectric.Shadows.shadowOf class DefaultShareIntentHandlerTest : RobolectricTest() { @Test @@ -101,12 +112,192 @@ class DefaultShareIntentHandlerTest : RobolectricTest() { assertThat(result).isNull() } + @Test + fun `an intent without mime type is not handled`() { + val result = createDefaultShareIntentHandler().handleIncomingShareIntent(Intent(Intent.ACTION_SEND)) + assertThat(result).isNull() + } + + @Test + fun `an intent with a listed mime type and no uri is handled`() { + listOf( + MimeTypes.Jpeg, + MimeTypes.Mp4, + MimeTypes.Mp3, + MimeTypes.Pdf, + "file/binary", + "text/html", + MimeTypes.Any, + ).forEach { type -> + val result = createDefaultShareIntentHandler().handleIncomingShareIntent( + Intent(Intent.ACTION_SEND).apply { this.type = type } + ) + assertWithMessage("$type was not properly handled").that(result).isEqualTo( + ShareIntentData.Uris( + text = null, + uris = emptyList(), + ) + ) + } + } + + @Test + fun `a send multiple intent without uri is handled`() { + val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply { + type = MimeTypes.Jpeg + } + val result = createDefaultShareIntentHandler().handleIncomingShareIntent(intent) + assertThat(result).isEqualTo( + ShareIntentData.Uris( + text = null, + uris = emptyList(), + ) + ) + } + + @Test + fun `an uri is ignored when the intent is neither a send nor a send multiple`() { + val intent = Intent(Intent.ACTION_VIEW).apply { + type = MimeTypes.Jpeg + putExtra(Intent.EXTRA_STREAM, "content://sender/image.jpg".toUri()) + } + val result = createDefaultShareIntentHandler().handleIncomingShareIntent(intent) + assertThat(result).isEqualTo( + ShareIntentData.Uris( + text = null, + uris = emptyList(), + ) + ) + } + + @Test + fun `an empty text is not kept as a caption`() { + val uri = "content://sender/logs.zip".toUri() + val result = createDefaultShareIntentHandler().handleIncomingShareIntent( + aSendIntent(type = "message/rfc822", uri = uri).apply { + putExtra(Intent.EXTRA_TEXT, "") + } + ) + assertThat(result).isEqualTo( + ShareIntentData.Uris( + text = null, + uris = listOf(UriToShare(uri = uri, mimeType = "message/rfc822")), + ) + ) + } + + @Test + fun `a plain text intent without text is not handled`() { + val intent = Intent(Intent.ACTION_SEND).apply { + type = MimeTypes.PlainText + } + val result = createDefaultShareIntentHandler().handleIncomingShareIntent(intent) + assertThat(result).isNull() + } + + @Test + fun `a plain text intent with an empty text is not handled`() { + val intent = Intent(Intent.ACTION_SEND).apply { + type = MimeTypes.PlainText + putExtra(Intent.EXTRA_TEXT, "") + } + val result = createDefaultShareIntentHandler().handleIncomingShareIntent(intent) + assertThat(result).isNull() + } + + @Test + fun `the mime type of the uri overrides the one of the intent`() { + Robolectric.setupContentProvider(ZipContentProvider::class.java, "sender") + val uri = "content://sender/logs.zip".toUri() + val result = createDefaultShareIntentHandler().handleIncomingShareIntent( + aSendIntent(type = MimeTypes.Any, uri = uri) + ) + assertThat(result).isEqualTo( + ShareIntentData.Uris( + text = null, + uris = listOf(UriToShare(uri = uri, mimeType = "application/zip")), + ) + ) + } + + @Test + fun `the intent is made explicit for the application which will read the uri`() { + val intent = aSendIntent(type = "message/rfc822", uri = "content://sender/logs.zip".toUri()) + givenAnApplicationHandling(intent) + createDefaultShareIntentHandler().handleIncomingShareIntent(intent) + assertThat(intent.action).isNull() + assertThat(intent.component).isEqualTo(ComponentName(A_PACKAGE_NAME, AN_ACTIVITY_NAME)) + } + + @Test + fun `the intent stays implicit when the uri permission cannot be granted`() { + val uri = "content://sender/logs.zip".toUri() + val intent = aSendIntent(type = "message/rfc822", uri = uri) + givenAnApplicationHandling(intent) + val result = DefaultShareIntentHandler( + context = FailingGrantUriPermissionContext(RuntimeEnvironment.getApplication()), + ).handleIncomingShareIntent(intent) + assertThat(intent.action).isEqualTo(Intent.ACTION_SEND) + assertThat(intent.component).isNull() + assertThat(result).isEqualTo( + ShareIntentData.Uris( + text = null, + uris = listOf(UriToShare(uri = uri, mimeType = "message/rfc822")), + ) + ) + } + private fun aSendIntent(type: String, uri: Uri) = Intent(Intent.ACTION_SEND).apply { this.type = type putExtra(Intent.EXTRA_STREAM, uri) } + private fun givenAnApplicationHandling(intent: Intent) { + val componentName = ComponentName(A_PACKAGE_NAME, AN_ACTIVITY_NAME) + shadowOf(RuntimeEnvironment.getApplication().packageManager).apply { + addActivityIfNotPresent(componentName) + addIntentFilterForActivity( + componentName, + IntentFilter(intent.action).apply { + addCategory(Intent.CATEGORY_DEFAULT) + addDataType(intent.type) + } + ) + } + } + private fun createDefaultShareIntentHandler() = DefaultShareIntentHandler( context = RuntimeEnvironment.getApplication(), ) + + private class FailingGrantUriPermissionContext(context: Context) : ContextWrapper(context) { + override fun grantUriPermission(toPackage: String, uri: Uri, modeFlags: Int) { + error("Unable to grant Uri permission") + } + } + + private class ZipContentProvider : ContentProvider() { + override fun onCreate() = true + + override fun getType(uri: Uri) = "application/zip" + + override fun query( + uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String?, + ): Cursor? = null + + override fun insert(uri: Uri, values: ContentValues?): Uri? = null + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?) = 0 + + override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?) = 0 + } + + private companion object { + const val A_PACKAGE_NAME = "io.element.android.x" + const val AN_ACTIVITY_NAME = "io.element.android.x.MainActivity" + } } From 1f7635f0e37676a8c2d7db1cfb9419d263f76940 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 17 Aug 2026 15:56:59 +0200 Subject: [PATCH 228/338] Fix API break --- .../mediaviewer/impl/local/player/ExoPlayerForPreview.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/ExoPlayerForPreview.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/ExoPlayerForPreview.kt index 4da8433cd1..e23cb790db 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/ExoPlayerForPreview.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/ExoPlayerForPreview.kt @@ -266,4 +266,5 @@ class ExoPlayerForPreview( override fun setVideoCodecParameters(codecParameters: CodecParameters) {} override fun addVideoCodecParametersChangeListener(listener: CodecParametersChangeListener, keys: List) {} override fun removeVideoCodecParametersChangeListener(listener: CodecParametersChangeListener) {} + override fun setEnforceAdPlaybackOnTimelineRefresh(enforceAdPlaybackOnTimelineRefresh: Boolean) = throw NotImplementedError() } From 03c330ee07a79cae93f2026aa2d6ff3a6c9d865e Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 17:40:33 +0300 Subject: [PATCH 229/338] Rename PreviewParameterProvider subclasses to the PreviewParam suffix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `Provider` suffix reads as a dependency provider rather than as preview data, and it collides with the real providers in the codebase — `StringProvider`, `PickerProvider`, the login feature's `AccountProvider` — so the name says nothing about which of the two a class is. `PreviewParam` says it. 195 classes are renamed, along with the 185 files named after them, the konsist rule that enforces the convention, the presentation-class file templates and the onboarding documentation. This cannot be split. `Konsist.scopeFromProduction()` scans the whole project, so flipping the assertion before every class has moved leaves the rule red, and a rule that accepted either suffix during a transition would be exactly the kind of compatibility layer this convention change is meant to remove. Two judgement calls in the rule itself. The `name.endsWith("PreviewProvider")` exclusion is dropped: its only job was to reject a redundant `Preview` before `Provider`, and the new suffix already contains it; no class currently ends with `PreviewProvider`, so removing it changes nothing today. And `EditableAvatarViewUriProvider` is renamed rather than deleted from the exclusion list even though no such class exists in this repository, since it presumably comes from the modules overlaid for the enterprise build. Nothing keyed off the old suffix: no snapshot filename contains a provider class name, so no Paparazzi image changes, and the Kover variants match on `*Presenter`, `*State` and `*ViewKt`, so coverage bounds are unaffected. --- AGENTS.md | 4 ++-- ...teProvider.kt => LoggedInStatePreviewParam.kt} | 2 +- .../android/appnav/loggedin/LoggedInView.kt | 2 +- .../appnav/room/joined/LoadingRoomNodeView.kt | 4 ++-- ...tStateProvider.kt => RootStatePreviewParam.kt} | 2 +- .../io/element/android/appnav/root/RootView.kt | 2 +- docs/_developer_onboarding.md | 8 ++++---- ...t => AnalyticsPreferencesStatePreviewParam.kt} | 2 +- .../api/preferences/AnalyticsPreferencesView.kt | 2 +- ...ider.kt => AnalyticsOptInStatePreviewParam.kt} | 2 +- .../features/analytics/impl/AnalyticsOptInView.kt | 2 +- ...ovider.kt => AnnouncementStatePreviewParam.kt} | 2 +- .../impl/fullscreen/FullscreenAnnouncementView.kt | 4 ++-- ...der.kt => CallNotificationDataPreviewParam.kt} | 2 +- ...Provider.kt => CallScreenStatePreviewParam.kt} | 2 +- .../features/call/impl/ui/CallScreenView.kt | 2 +- .../features/call/impl/ui/IncomingCallScreen.kt | 2 +- .../createroom/impl/addpeople/AddPeopleView.kt | 4 ++-- ...vider.kt => ConfigureRoomStatePreviewParam.kt} | 2 +- .../impl/configureroom/ConfigureRoomView.kt | 4 ++-- ...kt => AccountDeactivationStatePreviewParam.kt} | 2 +- .../logout/impl/AccountDeactivationView.kt | 2 +- ...der.kt => ForwardMessagesStatePreviewParam.kt} | 2 +- .../features/forward/impl/ForwardMessagesView.kt | 2 +- ....kt => NotificationsOptInStatePreviewParam.kt} | 2 +- .../impl/notifications/NotificationsOptInView.kt | 2 +- ...hooseSelfVerificationModeStatePreviewParam.kt} | 2 +- .../choosemode/ChooseSelfVerificationModeView.kt | 2 +- ...eStateProvider.kt => HomeStatePreviewParam.kt} | 6 +++--- .../android/features/home/impl/HomeView.kt | 2 +- .../home/impl/components/RoomListContentView.kt | 4 ++-- .../home/impl/components/RoomSummaryRow.kt | 4 ++-- ...der.kt => RoomListFiltersStatePreviewParam.kt} | 2 +- .../home/impl/filters/RoomListFiltersView.kt | 2 +- ...ider.kt => RoomListRoomSummaryPreviewParam.kt} | 2 +- ...der.kt => RoomListContentStatePreviewParam.kt} | 2 +- .../home/impl/roomlist/RoomListContextMenu.kt | 2 +- .../impl/roomlist/RoomListDeclineInviteMenu.kt | 2 +- ... RoomListStateContextMenuShownPreviewParam.kt} | 2 +- ...istStateDeclineInviteMenuShownPreviewParam.kt} | 2 +- ...teProvider.kt => RoomListStatePreviewParam.kt} | 2 +- ...ider.kt => RoomListSearchStatePreviewParam.kt} | 2 +- .../home/impl/search/RoomListSearchView.kt | 2 +- ...ovider.kt => SpaceFiltersStatePreviewParam.kt} | 2 +- .../home/impl/spacefilters/SpaceFiltersView.kt | 2 +- ...Provider.kt => HomeSpacesStatePreviewParam.kt} | 4 ++-- .../features/home/impl/spaces/HomeSpacesView.kt | 2 +- ...ceRoomProvider.kt => SpaceRoomPreviewParam.kt} | 2 +- ...kt => AcceptDeclineInviteStatePreviewParam.kt} | 0 ...kt => AcceptDeclineInviteStatePreviewParam.kt} | 2 +- .../impl/acceptdecline/AcceptDeclineInviteView.kt | 2 +- ...der.kt => DeclineAndBlockStatePreviewParam.kt} | 2 +- .../impl/declineandblock/DeclineAndBlockView.kt | 2 +- ...ovider.kt => InvitePeopleStatePreviewParam.kt} | 2 +- ...kt => DefaultInvitePeopleStatePreviewParam.kt} | 2 +- .../invitepeople/impl/InvitePeopleView.kt | 2 +- ...teProvider.kt => JoinRoomStatePreviewParam.kt} | 2 +- .../features/joinroom/impl/JoinRoomView.kt | 2 +- ...kt => KnockRequestsBannerStatePreviewParam.kt} | 2 +- .../impl/banner/KnockRequestsBannerView.kt | 2 +- ...r.kt => KnockRequestsListStatePreviewParam.kt} | 2 +- .../impl/list/KnockRequestsListView.kt | 2 +- ...r.kt => InternalLeaveRoomStatePreviewParam.kt} | 2 +- .../features/leaveroom/impl/LeaveRoomView.kt | 2 +- ...=> DependencyLicensesListStatePreviewParam.kt} | 2 +- .../impl/list/DependencyLicensesListView.kt | 2 +- ...vider.kt => DesktopNoticeStatePreviewParam.kt} | 2 +- .../impl/screens/desktop/DesktopNoticeView.kt | 2 +- ...Provider.kt => ErrorScreenTypePreviewParam.kt} | 2 +- .../linknewdevice/impl/screens/error/ErrorView.kt | 2 +- ...rovider.kt => EnterNumberStatePreviewParam.kt} | 2 +- .../impl/screens/number/EnterNumberView.kt | 2 +- ...Provider.kt => ShowQrCodeStatePreviewParam.kt} | 2 +- .../impl/screens/qrcode/ShowQrCodeView.kt | 2 +- ...r.kt => LinkNewDeviceRootStatePreviewParam.kt} | 2 +- .../impl/screens/root/LinkNewDeviceRootView.kt | 2 +- ...Provider.kt => ScanQrCodeStatePreviewParam.kt} | 2 +- .../impl/screens/scan/ScanQrCodeView.kt | 2 +- ...vider.kt => ShareLocationStatePreviewParam.kt} | 2 +- .../location/impl/share/ShareLocationView.kt | 2 +- ...ovider.kt => ShowLocationStatePreviewParam.kt} | 2 +- .../location/impl/show/ShowLocationView.kt | 2 +- ....kt => LockScreenSettingsStatePreviewParam.kt} | 2 +- .../impl/settings/LockScreenSettingsView.kt | 2 +- ...ider.kt => SetupBiometricStatePreviewParam.kt} | 2 +- .../impl/setup/biometric/SetupBiometricView.kt | 2 +- ...teProvider.kt => SetupPinStatePreviewParam.kt} | 2 +- .../lockscreen/impl/setup/pin/SetupPinView.kt | 2 +- ...eProvider.kt => PinUnlockStatePreviewParam.kt} | 4 ++-- .../lockscreen/impl/unlock/PinUnlockView.kt | 6 +++--- ...Provider.kt => AccountProviderPreviewParam.kt} | 2 +- .../impl/accountprovider/AccountProviderView.kt | 2 +- ...ovider.kt => ChangeServerStatePreviewParam.kt} | 2 +- .../login/impl/changeserver/ChangeServerView.kt | 2 +- ...ovider.kt => ChangeServerErrorPreviewParam.kt} | 2 +- .../features/login/impl/login/LoginModeView.kt | 2 +- ...vider.kt => LoginModeViewErrorPreviewParam.kt} | 6 +++--- ... => ChangeAccountProviderStatePreviewParam.kt} | 2 +- .../ChangeAccountProviderView.kt | 4 +++- ... => ChooseAccountProviderStatePreviewParam.kt} | 2 +- .../ChooseAccountProviderView.kt | 4 +++- ...er.kt => LoginWithClassicStatePreviewParam.kt} | 2 +- .../loginwithclassic/LoginWithClassicView.kt | 2 +- ...er.kt => MissingKeyBackupStatePreviewParam.kt} | 2 +- .../missingkeybackup/MissingKeyBackupView.kt | 2 +- ...=> ConfirmAccountProviderStatePreviewParam.kt} | 2 +- .../ConfirmAccountProviderView.kt | 2 +- ...vider.kt => LoginPasswordStatePreviewParam.kt} | 2 +- .../screens/loginpassword/LoginPasswordView.kt | 2 +- ...Provider.kt => OnBoardingStatePreviewParam.kt} | 2 +- .../impl/screens/onboarding/OnBoardingView.kt | 2 +- ...r.kt => QrCodeConfirmationStepPreviewParam.kt} | 2 +- .../qrcode/confirmation/QrCodeConfirmationView.kt | 2 +- .../impl/screens/qrcode/error/QrCodeErrorView.kt | 4 ++-- ...rovider.kt => QrCodeIntroStatePreviewParam.kt} | 2 +- .../impl/screens/qrcode/intro/QrCodeIntroView.kt | 2 +- ...Provider.kt => QrCodeScanStatePreviewParam.kt} | 2 +- .../impl/screens/qrcode/scan/QrCodeScanView.kt | 2 +- ... => SearchAccountProviderStatePreviewParam.kt} | 2 +- .../SearchAccountProviderView.kt | 4 +++- ...ovider.kt => DirectLogoutStatePreviewParam.kt} | 2 +- ...tateProvider.kt => LogoutStatePreviewParam.kt} | 2 +- .../android/features/logout/impl/LogoutView.kt | 2 +- .../logout/impl/direct/DefaultDirectLogoutView.kt | 4 ++-- ...t => VoiceMessageComposerStatePreviewParam.kt} | 2 +- ...teProvider.kt => MessagesStatePreviewParam.kt} | 2 +- .../features/messages/impl/MessagesView.kt | 2 +- ...Provider.kt => ActionListStatePreviewParam.kt} | 2 +- .../messages/impl/actionlist/ActionListView.kt | 2 +- ....kt => AttachmentsPreviewStatePreviewParam.kt} | 2 +- .../attachments/preview/AttachmentsPreviewView.kt | 2 +- ... => AttachmentImageEditorStatePreviewParam.kt} | 2 +- .../imageeditor/AttachmentImageEditorView.kt | 2 +- ...ider.kt => IdentityChangeStatePreviewParam.kt} | 2 +- .../crypto/identity/IdentityChangeStateView.kt | 2 +- .../MessagesViewWithIdentityChangePreview.kt | 2 +- ...veVerifiedUserSendFailureStatePreviewParam.kt} | 2 +- .../resolve/ResolveVerifiedUserSendFailureView.kt | 2 +- ...kStateProvider.kt => LinkStatePreviewParam.kt} | 2 +- .../features/messages/impl/link/LinkView.kt | 2 +- ...der.kt => MessageComposerStatePreviewParam.kt} | 2 +- .../impl/messagecomposer/MessageComposerView.kt | 6 +++--- ...t => PinnedMessagesBannerStatePreviewParam.kt} | 2 +- .../pinned/banner/PinnedMessagesBannerView.kt | 4 +++- ....kt => PinnedMessagesListStatePreviewParam.kt} | 2 +- .../impl/pinned/list/PinnedMessagesListView.kt | 2 +- ...vider.kt => ReportMessageStatePreviewParam.kt} | 2 +- .../messages/impl/report/ReportMessageView.kt | 2 +- .../messages/impl/timeline/TimelineView.kt | 4 ++-- .../impl/timeline/components/CallMenuItem.kt | 4 ++-- .../timeline/components/MessageEventBubble.kt | 4 ++-- .../timeline/components/MessagesReactionButton.kt | 4 ++-- .../components/TimelineEventTimestampView.kt | 4 +++- ...ItemEventContentForGalleryViewPreviewParam.kt} | 2 +- ...elineItemEventForTimestampViewPreviewParam.kt} | 2 +- .../TimelineItemEventRowDisambiguatedPreview.kt | 4 ++-- .../TimelineItemEventRowGalleryPreview.kt | 2 +- .../TimelineItemEventRowTimestampPreview.kt | 2 +- .../TimelineItemEventRowWithRRPreview.kt | 4 ++-- ...lineItemEventRowWithReplyInformativePreview.kt | 4 ++-- .../TimelineItemEventRowWithReplyOtherPreview.kt | 4 ++-- .../TimelineItemEventRowWithReplyPreview.kt | 4 ++-- ...TimelineItemAttachmentsContentPreviewParam.kt} | 2 +- .../event/TimelineItemAttachmentsListView.kt | 2 +- .../components/event/TimelineItemAudioView.kt | 4 ++-- .../components/event/TimelineItemEncryptedView.kt | 4 ++-- .../components/event/TimelineItemFileView.kt | 4 ++-- ... => TimelineItemGalleryContentPreviewParam.kt} | 2 +- .../components/event/TimelineItemGalleryView.kt | 2 +- .../components/event/TimelineItemImageView.kt | 4 ++-- .../components/event/TimelineItemLocationView.kt | 4 ++-- .../components/event/TimelineItemPollView.kt | 4 ++-- .../components/event/TimelineItemStickerView.kt | 6 ++++-- .../components/event/TimelineItemTextView.kt | 4 ++-- .../components/event/TimelineItemVideoView.kt | 4 ++-- .../components/event/TimelineItemVoiceView.kt | 14 +++++++------- ...der.kt => ReactionSummaryStatePreviewParam.kt} | 2 +- .../reactionsummary/ReactionSummaryView.kt | 2 +- ...ewStateForTimelineItemEventRowPreviewParam.kt} | 2 +- ...der.kt => ReadReceiptViewStatePreviewParam.kt} | 2 +- .../receipt/TimelineItemReadReceiptView.kt | 2 +- .../receipt/bottomsheet/ReadReceiptBottomSheet.kt | 2 +- ...=> ReadReceiptBottomSheetStatePreviewParam.kt} | 8 ++++---- .../virtual/TimelineItemDaySeparatorView.kt | 4 ++-- ...ovider.kt => FocusRequestStatePreviewParam.kt} | 2 +- .../impl/timeline/focus/FocusRequestStateView.kt | 2 +- ...vider.kt => AggregatedReactionPreviewParam.kt} | 2 +- .../model/TimelineItemReactionsProvider.kt | 4 ++-- ...tateProvider.kt => BubbleStatePreviewParam.kt} | 2 +- ...kt => TimelineItemAudioContentPreviewParam.kt} | 2 +- ...> TimelineItemEncryptedContentPreviewParam.kt} | 2 +- ...kt => TimelineItemEventContentPreviewParam.kt} | 4 ++-- ....kt => TimelineItemFileContentPreviewParam.kt} | 2 +- ...kt => TimelineItemImageContentPreviewParam.kt} | 2 +- ...=> TimelineItemLocationContentPreviewParam.kt} | 2 +- ....kt => TimelineItemPollContentPreviewParam.kt} | 2 +- ... => TimelineItemStickerContentPreviewParam.kt} | 2 +- ...kt => TimelineItemVideoContentPreviewParam.kt} | 2 +- ...kt => TimelineItemVoiceContentPreviewParam.kt} | 2 +- ... TimelineItemDaySeparatorModelPreviewParam.kt} | 2 +- ...atioProvider.kt => AspectRatioPreviewParam.kt} | 2 +- .../impl/timeline/protection/ProtectedView.kt | 2 +- ....kt => TypingNotificationStatePreviewParam.kt} | 2 +- .../impl/typing/TypingNotificationView.kt | 2 +- ...eProvider.kt => MigrationStatePreviewParam.kt} | 2 +- .../features/migration/impl/MigrationView.kt | 2 +- ...Provider.kt => CreatePollStatePreviewParam.kt} | 2 +- .../features/poll/impl/create/CreatePollView.kt | 2 +- ...rovider.kt => PollHistoryStatePreviewParam.kt} | 2 +- .../features/poll/impl/history/PollHistoryView.kt | 2 +- ...StateProvider.kt => AboutStatePreviewParam.kt} | 2 +- .../features/preferences/impl/about/AboutView.kt | 2 +- ...er.kt => AdvancedSettingsStatePreviewParam.kt} | 2 +- .../impl/advanced/AdvancedSettingsView.kt | 6 +++--- ...r.kt => AnalyticsSettingsStatePreviewParam.kt} | 2 +- .../impl/analytics/AnalyticsSettingsView.kt | 2 +- ...ovider.kt => BlockedUsersStatePreviewParam.kt} | 2 +- .../impl/blockedusers/BlockedUsersView.kt | 2 +- ...r.kt => DeveloperSettingsStatePreviewParam.kt} | 2 +- .../impl/developer/DeveloperSettingsView.kt | 2 +- ...t => AppDeveloperSettingsStatePreviewParam.kt} | 2 +- .../appsettings/AppDeveloperSettingsView.kt | 2 +- ...sStateProvider.kt => LabsStatePreviewParam.kt} | 2 +- .../features/preferences/impl/labs/LabsView.kt | 2 +- ...t => NotificationSettingsStatePreviewParam.kt} | 2 +- .../notifications/NotificationSettingsView.kt | 4 +++- ...efaultNotificationSettingStatePreviewParam.kt} | 2 +- .../edit/EditDefaultNotificationSettingView.kt | 2 +- ...der.kt => PreferencesRootStatePreviewParam.kt} | 2 +- .../preferences/impl/root/PreferencesRootView.kt | 4 ++-- .../preferences/impl/user/UserPreferences.kt | 4 ++-- ...der.kt => EditUserProfileStatePreviewParam.kt} | 2 +- .../impl/user/editprofile/EditUserProfileView.kt | 2 +- ...Provider.kt => UserStatusStatePreviewParam.kt} | 2 +- .../preferences/impl/userstatus/UserStatusView.kt | 2 +- ...t => RageshakePreferencesStatePreviewParam.kt} | 2 +- .../api/preferences/RageshakePreferencesView.kt | 4 +++- ...eProvider.kt => BugReportStatePreviewParam.kt} | 2 +- .../rageshake/impl/bugreport/BugReportView.kt | 4 ++-- ...Provider.kt => ReportRoomStatePreviewParam.kt} | 2 +- .../features/reportroom/impl/ReportRoomView.kt | 2 +- ... => ChangeRoomPermissionsStatePreviewParam.kt} | 2 +- .../impl/permissions/ChangeRoomPermissionsView.kt | 2 +- ...rovider.kt => ChangeRolesStatePreviewParam.kt} | 2 +- .../impl/roles/ChangeRolesView.kt | 2 +- ...kt => RolesAndPermissionsStatePreviewParam.kt} | 2 +- .../impl/root/RolesAndPermissionsView.kt | 2 +- ...r.kt => RoomAliasResolverStatePreviewParam.kt} | 2 +- .../impl/RoomAliasResolverView.kt | 2 +- ...teProvider.kt => RoomCallStatePreviewParam.kt} | 2 +- ...rovider.kt => RoomDetailsStatePreviewParam.kt} | 2 +- .../features/roomdetails/impl/RoomDetailsView.kt | 4 ++-- .../impl/invite/RoomInviteMembersView.kt | 4 ++-- ...ider.kt => RoomMemberListStatePreviewParam.kt} | 2 +- .../impl/members/RoomMemberListView.kt | 2 +- ... RoomNotificationSettingsStatePreviewParam.kt} | 2 +- .../RoomNotificationSettingsView.kt | 2 +- ...dRoomNotificationSettingsStatePreviewParam.kt} | 2 +- .../UserDefinedRoomNotificationSettingsView.kt | 2 +- ...der.kt => RoomDetailsEditStatePreviewParam.kt} | 2 +- .../roomdetailsedit/impl/RoomDetailsEditView.kt | 2 +- ...vider.kt => RoomDirectoryStatePreviewParam.kt} | 2 +- .../roomdirectory/impl/root/RoomDirectoryView.kt | 2 +- ...ernalRoomMemberModerationStatePreviewParam.kt} | 2 +- .../impl/RoomMemberModerationView.kt | 2 +- ...kt => SecureBackupDisableStatePreviewParam.kt} | 2 +- .../impl/disable/SecureBackupDisableView.kt | 2 +- ...ureBackupEnterRecoveryKeyStatePreviewParam.kt} | 2 +- .../enter/SecureBackupEnterRecoveryKeyView.kt | 2 +- ... => ResetIdentityPasswordStatePreviewParam.kt} | 2 +- .../reset/password/ResetIdentityPasswordView.kt | 2 +- ...r.kt => ResetIdentityRootStatePreviewParam.kt} | 2 +- .../impl/reset/root/ResetIdentityRootView.kt | 2 +- ...er.kt => SecureBackupRootStatePreviewParam.kt} | 2 +- .../impl/root/SecureBackupRootView.kt | 2 +- ...r.kt => SecureBackupSetupStatePreviewParam.kt} | 2 +- .../impl/setup/SecureBackupSetupView.kt | 2 +- .../setup/SecureBackupSetupViewChangePreview.kt | 2 +- .../impl/setup/views/RecoveryKeyView.kt | 2 +- ...der.kt => RecoveryKeyViewStatePreviewParam.kt} | 2 +- ...der.kt => EditRoomAddressStatePreviewParam.kt} | 2 +- .../impl/editroomaddress/EditRoomAddressView.kt | 2 +- ...=> ManageAuthorizedSpacesStatePreviewParam.kt} | 2 +- .../ManageAuthorizedSpacesView.kt | 2 +- ....kt => SecurityAndPrivacyStatePreviewParam.kt} | 2 +- .../impl/root/SecurityAndPrivacyView.kt | 4 ++-- ...StateProvider.kt => ShareStatePreviewParam.kt} | 2 +- .../android/features/share/impl/ShareView.kt | 2 +- ...eProvider.kt => SignedOutStatePreviewParam.kt} | 2 +- .../features/signedout/impl/SignedOutView.kt | 2 +- ...ider.kt => AddRoomToSpaceStatePreviewParam.kt} | 2 +- .../space/impl/addroom/AddRoomToSpaceView.kt | 2 +- ...Provider.kt => LeaveSpaceStatePreviewParam.kt} | 2 +- .../features/space/impl/leave/LeaveSpaceView.kt | 2 +- ...StateProvider.kt => SpaceStatePreviewParam.kt} | 2 +- .../android/features/space/impl/root/SpaceView.kt | 2 +- ...vider.kt => SpaceSettingsStatePreviewParam.kt} | 2 +- .../space/impl/settings/SpaceSettingsView.kt | 2 +- .../startchat/impl/components/UserListView.kt | 4 ++-- ...r.kt => JoinRoomByAddressStatePreviewParam.kt} | 2 +- .../impl/joinbyaddress/JoinRoomByAddressView.kt | 2 +- ...eProvider.kt => StartChatStatePreviewParam.kt} | 2 +- .../features/startchat/impl/root/StartChatView.kt | 2 +- ...teProvider.kt => UserListStatePreviewParam.kt} | 2 +- ...rovider.kt => UserProfileStatePreviewParam.kt} | 2 +- .../userprofile/shared/UserProfileView.kt | 2 +- ...t => IncomingVerificationStatePreviewParam.kt} | 2 +- .../impl/incoming/IncomingVerificationView.kt | 4 +++- ...t => OutgoingVerificationStatePreviewParam.kt} | 2 +- .../impl/outgoing/OutgoingVerificationView.kt | 4 +++- ...teProvider.kt => ViewFileStatePreviewParam.kt} | 2 +- .../features/viewfolder/impl/file/ViewFileView.kt | 2 +- ...Provider.kt => ViewFolderStatePreviewParam.kt} | 2 +- .../viewfolder/impl/folder/ViewFolderView.kt | 2 +- ...vider.kt => AccountSelectStatePreviewParam.kt} | 2 +- .../accountselect/impl/AccountSelectView.kt | 2 +- ...ovider.kt => DateFormatterModePreviewParam.kt} | 2 +- .../impl/previews/DateFormatterModeViewPreview.kt | 2 +- .../atomic/molecules/ComposerAlertMolecule.kt | 2 +- ...=> ComposerAlertMoleculeParamsPreviewParam.kt} | 2 +- .../libraries/designsystem/components/BigIcon.kt | 4 ++-- ...tionProvider.kt => AsyncActionPreviewParam.kt} | 2 +- .../components/async/AsyncActionView.kt | 2 +- .../designsystem/components/avatar/AvatarRow.kt | 10 +++++----- ...tioProvider.kt => OverlapRatioPreviewParam.kt} | 2 +- ...ctorProvider.kt => ImageVectorPreviewParam.kt} | 2 +- .../preferences/components/PreferenceIcon.kt | 4 ++-- ...{BooleanProvider.kt => BooleanPreviewParam.kt} | 2 +- ....kt => DefaultEmojiPickerStatePreviewParam.kt} | 2 +- .../emoji/impl/picker/EmojiPickerView.kt | 2 +- .../matrix/ui/components/AttachmentThumbnail.kt | 2 +- ....kt => AttachmentThumbnailInfoPreviewParam.kt} | 2 +- .../components/CreateDmConfirmationBottomSheet.kt | 4 ++-- .../matrix/ui/components/MatrixUserHeader.kt | 2 +- ...xUserProvider.kt => MatrixUserPreviewParam.kt} | 4 ++-- .../matrix/ui/components/MatrixUserRow.kt | 2 +- ...oProvider.kt => SelectRoomInfoPreviewParam.kt} | 2 +- .../matrix/ui/components/SelectedRoom.kt | 4 ++-- .../matrix/ui/components/SelectedUser.kt | 2 +- .../matrix/ui/components/SpaceRoomItemView.kt | 2 +- ...ceRoomProvider.kt => SpaceRoomPreviewParam.kt} | 2 +- ...rovider.kt => InReplyToDetailsPreviewParam.kt} | 8 ++++---- .../matrix/ui/messages/reply/InReplyToView.kt | 2 +- .../matrix/ui/messages/sender/SenderName.kt | 2 +- ...aProvider.kt => SenderNameDataPreviewParam.kt} | 2 +- .../libraries/matrix/ui/room/LoadingRoomState.kt | 2 +- ...omSheetStateDeleteConfirmationPreviewParam.kt} | 2 +- ...> MediaBottomSheetStateDetailsPreviewParam.kt} | 2 +- .../details/MediaDeleteConfirmationBottomSheet.kt | 2 +- .../impl/details/MediaDetailsBottomSheet.kt | 2 +- ...ovider.kt => MediaGalleryStatePreviewParam.kt} | 2 +- .../mediaviewer/impl/gallery/MediaGalleryView.kt | 2 +- .../mediaviewer/impl/gallery/ui/AudioItemView.kt | 2 +- .../mediaviewer/impl/gallery/ui/DateItemView.kt | 2 +- .../mediaviewer/impl/gallery/ui/FileItemView.kt | 2 +- ...oProvider.kt => MediaItemAudioPreviewParam.kt} | 2 +- ...r.kt => MediaItemDateSeparatorPreviewParam.kt} | 2 +- ...leProvider.kt => MediaItemFilePreviewParam.kt} | 2 +- ...oProvider.kt => MediaItemVideoPreviewParam.kt} | 2 +- ...eProvider.kt => MediaItemVoicePreviewParam.kt} | 2 +- .../mediaviewer/impl/gallery/ui/VideoItemView.kt | 2 +- .../mediaviewer/impl/gallery/ui/VoiceItemView.kt | 6 +++--- .../impl/local/audio/MediaAudioView.kt | 2 +- ...oProvider.kt => MediaInfoAudioPreviewParam.kt} | 2 +- .../mediaviewer/impl/local/file/MediaFileView.kt | 2 +- ...leProvider.kt => MediaInfoFilePreviewParam.kt} | 2 +- ... => MediaPlayerControllerStatePreviewParam.kt} | 2 +- .../local/player/MediaPlayerControllerView.kt | 2 +- ...Provider.kt => TextFileContentPreviewParam.kt} | 2 +- .../mediaviewer/impl/local/txt/TextFileView.kt | 2 +- ...rovider.kt => MediaViewerStatePreviewParam.kt} | 2 +- .../mediaviewer/impl/viewer/MediaViewerView.kt | 4 ++-- ...rovider.kt => PermissionsStatePreviewParam.kt} | 2 +- .../libraries/permissions/api/PermissionsView.kt | 2 +- ...Provider.kt => RoomSelectStatePreviewParam.kt} | 2 +- .../libraries/roomselect/impl/RoomSelectView.kt | 2 +- .../libraries/textcomposer/ComposerModeView.kt | 2 +- ... => MessageComposerModeSpecialPreviewParam.kt} | 8 ++++---- .../libraries/textcomposer/TextComposer.kt | 6 +++--- ...TroubleshootNotificationsStatePreviewParam.kt} | 2 +- .../impl/TroubleshootNotificationsView.kt | 2 +- ...rovider.kt => PushHistoryStatePreviewParam.kt} | 2 +- .../troubleshoot/impl/history/PushHistoryView.kt | 2 +- ...ovider.kt => VoiceMessageStatePreviewParam.kt} | 2 +- .../android/tests/konsist/KonsistClassNameTest.kt | 15 +++++++-------- .../Template Presentation Classes.kt.child.0.kt | 2 +- .../Template Presentation Classes.kt.child.2.kt | 2 +- .../files/options/file.template.settings.xml | 4 ++-- 388 files changed, 495 insertions(+), 476 deletions(-) rename appnav/src/main/kotlin/io/element/android/appnav/loggedin/{LoggedInStateProvider.kt => LoggedInStatePreviewParam.kt} (95%) rename appnav/src/main/kotlin/io/element/android/appnav/root/{RootStateProvider.kt => RootStatePreviewParam.kt} (95%) rename features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/{AnalyticsPreferencesStateProvider.kt => AnalyticsPreferencesStatePreviewParam.kt} (89%) rename features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/{AnalyticsOptInStateProvider.kt => AnalyticsOptInStatePreviewParam.kt} (88%) rename features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/{AnnouncementStateProvider.kt => AnnouncementStatePreviewParam.kt} (90%) rename features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/{CallNotificationDataProvider.kt => CallNotificationDataPreviewParam.kt} (94%) rename features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/{CallScreenStateProvider.kt => CallScreenStatePreviewParam.kt} (93%) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/{ConfigureRoomStateProvider.kt => ConfigureRoomStatePreviewParam.kt} (98%) rename features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/{AccountDeactivationStateProvider.kt => AccountDeactivationStatePreviewParam.kt} (94%) rename features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/{ForwardMessagesStateProvider.kt => ForwardMessagesStatePreviewParam.kt} (92%) rename features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/{NotificationsOptInStateProvider.kt => NotificationsOptInStatePreviewParam.kt} (88%) rename features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/{ChooseSelfVerificationModeStateProvider.kt => ChooseSelfVerificationModeStatePreviewParam.kt} (97%) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/{HomeStateProvider.kt => HomeStatePreviewParam.kt} (96%) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/{RoomListFiltersStateProvider.kt => RoomListFiltersStatePreviewParam.kt} (92%) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/{RoomListRoomSummaryProvider.kt => RoomListRoomSummaryPreviewParam.kt} (99%) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/{RoomListContentStateProvider.kt => RoomListContentStatePreviewParam.kt} (96%) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/{RoomListStateContextMenuShownProvider.kt => RoomListStateContextMenuShownPreviewParam.kt} (90%) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/{RoomListStateDeclineInviteMenuShownProvider.kt => RoomListStateDeclineInviteMenuShownPreviewParam.kt} (90%) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/{RoomListStateProvider.kt => RoomListStatePreviewParam.kt} (98%) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/{RoomListSearchStateProvider.kt => RoomListSearchStatePreviewParam.kt} (93%) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/{SpaceFiltersStateProvider.kt => SpaceFiltersStatePreviewParam.kt} (97%) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/{HomeSpacesStateProvider.kt => HomeSpacesStatePreviewParam.kt} (93%) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/{SpaceRoomProvider.kt => SpaceRoomPreviewParam.kt} (95%) rename features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/{AcceptDeclineInviteStateProvider.kt => AcceptDeclineInviteStatePreviewParam.kt} (100%) rename features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/{AcceptDeclineInviteStateProvider.kt => AcceptDeclineInviteStatePreviewParam.kt} (95%) rename features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/{DeclineAndBlockStateProvider.kt => DeclineAndBlockStatePreviewParam.kt} (93%) rename features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/{InvitePeopleStateProvider.kt => InvitePeopleStatePreviewParam.kt} (95%) rename features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/{DefaultInvitePeopleStateProvider.kt => DefaultInvitePeopleStatePreviewParam.kt} (98%) rename features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/{JoinRoomStateProvider.kt => JoinRoomStatePreviewParam.kt} (99%) rename features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/{KnockRequestsBannerStateProvider.kt => KnockRequestsBannerStatePreviewParam.kt} (96%) rename features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/{KnockRequestsListStateProvider.kt => KnockRequestsListStatePreviewParam.kt} (98%) rename features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/{InternalLeaveRoomStateProvider.kt => InternalLeaveRoomStatePreviewParam.kt} (94%) rename features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/{DependencyLicensesListStateProvider.kt => DependencyLicensesListStatePreviewParam.kt} (95%) rename features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/{DesktopNoticeStateProvider.kt => DesktopNoticeStatePreviewParam.kt} (92%) rename features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/{ErrorScreenTypeProvider.kt => ErrorScreenTypePreviewParam.kt} (90%) rename features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/number/{EnterNumberStateProvider.kt => EnterNumberStatePreviewParam.kt} (93%) rename features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/{ShowQrCodeStateProvider.kt => ShowQrCodeStatePreviewParam.kt} (90%) rename features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/root/{LinkNewDeviceRootStateProvider.kt => LinkNewDeviceRootStatePreviewParam.kt} (94%) rename features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/{ScanQrCodeStateProvider.kt => ScanQrCodeStatePreviewParam.kt} (91%) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/{ShareLocationStateProvider.kt => ShareLocationStatePreviewParam.kt} (98%) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/{ShowLocationStateProvider.kt => ShowLocationStatePreviewParam.kt} (97%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/{LockScreenSettingsStateProvider.kt => LockScreenSettingsStatePreviewParam.kt} (91%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/{SetupBiometricStateProvider.kt => SetupBiometricStatePreviewParam.kt} (87%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/{SetupPinStateProvider.kt => SetupPinStatePreviewParam.kt} (95%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/{PinUnlockStateProvider.kt => PinUnlockStatePreviewParam.kt} (94%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/{AccountProviderProvider.kt => AccountProviderPreviewParam.kt} (93%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/{ChangeServerStateProvider.kt => ChangeServerStatePreviewParam.kt} (96%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/{ChangeServerErrorProvider.kt => ChangeServerErrorPreviewParam.kt} (92%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/{LoginModeViewErrorProvider.kt => LoginModeViewErrorPreviewParam.kt} (80%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/{ChangeAccountProviderStateProvider.kt => ChangeAccountProviderStatePreviewParam.kt} (93%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/{ChooseAccountProviderStateProvider.kt => ChooseAccountProviderStatePreviewParam.kt} (96%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/{LoginWithClassicStateProvider.kt => LoginWithClassicStatePreviewParam.kt} (94%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/missingkeybackup/{MissingKeyBackupStateProvider.kt => MissingKeyBackupStatePreviewParam.kt} (86%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/{ConfirmAccountProviderStateProvider.kt => ConfirmAccountProviderStatePreviewParam.kt} (94%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/{LoginPasswordStateProvider.kt => LoginPasswordStatePreviewParam.kt} (94%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/{OnBoardingStateProvider.kt => OnBoardingStatePreviewParam.kt} (96%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/{QrCodeConfirmationStepProvider.kt => QrCodeConfirmationStepPreviewParam.kt} (87%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/{QrCodeIntroStateProvider.kt => QrCodeIntroStatePreviewParam.kt} (93%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/{QrCodeScanStateProvider.kt => QrCodeScanStatePreviewParam.kt} (96%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/{SearchAccountProviderStateProvider.kt => SearchAccountProviderStatePreviewParam.kt} (94%) rename features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/{DirectLogoutStateProvider.kt => DirectLogoutStatePreviewParam.kt} (92%) rename features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/{LogoutStateProvider.kt => LogoutStatePreviewParam.kt} (97%) rename features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/{VoiceMessageComposerStateProvider.kt => VoiceMessageComposerStatePreviewParam.kt} (93%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/{MessagesStateProvider.kt => MessagesStatePreviewParam.kt} (99%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/{ActionListStateProvider.kt => ActionListStatePreviewParam.kt} (99%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/{AttachmentsPreviewStateProvider.kt => AttachmentsPreviewStatePreviewParam.kt} (98%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/{AttachmentImageEditorStateProvider.kt => AttachmentImageEditorStatePreviewParam.kt} (97%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/{IdentityChangeStateProvider.kt => IdentityChangeStatePreviewParam.kt} (97%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/{ResolveVerifiedUserSendFailureStateProvider.kt => ResolveVerifiedUserSendFailureStatePreviewParam.kt} (93%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/{LinkStateProvider.kt => LinkStatePreviewParam.kt} (93%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/{MessageComposerStateProvider.kt => MessageComposerStatePreviewParam.kt} (95%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/{PinnedMessagesBannerStateProvider.kt => PinnedMessagesBannerStatePreviewParam.kt} (96%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/{PinnedMessagesListStateProvider.kt => PinnedMessagesListStatePreviewParam.kt} (98%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/{ReportMessageStateProvider.kt => ReportMessageStatePreviewParam.kt} (94%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/{TimelineItemEventContentForGalleryViewProvider.kt => TimelineItemEventContentForGalleryViewPreviewParam.kt} (98%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/{TimelineItemEventForTimestampViewProvider.kt => TimelineItemEventForTimestampViewPreviewParam.kt} (95%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/{TimelineItemAttachmentsContentProvider.kt => TimelineItemAttachmentsContentPreviewParam.kt} (96%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/{TimelineItemGalleryContentProvider.kt => TimelineItemGalleryContentPreviewParam.kt} (97%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/{ReactionSummaryStateProvider.kt => ReactionSummaryStatePreviewParam.kt} (91%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/{ReadReceiptViewStateForTimelineItemEventRowProvider.kt => ReadReceiptViewStateForTimelineItemEventRowPreviewParam.kt} (95%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/{ReadReceiptViewStateProvider.kt => ReadReceiptViewStatePreviewParam.kt} (96%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/{ReadReceiptBottomSheetStateProvider.kt => ReadReceiptBottomSheetStatePreviewParam.kt} (87%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/{FocusRequestStateProvider.kt => FocusRequestStatePreviewParam.kt} (93%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/{AggregatedReactionProvider.kt => AggregatedReactionPreviewParam.kt} (95%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/{BubbleStateProvider.kt => BubbleStatePreviewParam.kt} (94%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/{TimelineItemAudioContentProvider.kt => TimelineItemAudioContentPreviewParam.kt} (92%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/{TimelineItemEncryptedContentProvider.kt => TimelineItemEncryptedContentPreviewParam.kt} (96%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/{TimelineItemEventContentProvider.kt => TimelineItemEventContentPreviewParam.kt} (96%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/{TimelineItemFileContentProvider.kt => TimelineItemFileContentPreviewParam.kt} (92%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/{TimelineItemImageContentProvider.kt => TimelineItemImageContentPreviewParam.kt} (93%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/{TimelineItemLocationContentProvider.kt => TimelineItemLocationContentPreviewParam.kt} (95%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/{TimelineItemPollContentProvider.kt => TimelineItemPollContentPreviewParam.kt} (94%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/{TimelineItemStickerContentProvider.kt => TimelineItemStickerContentPreviewParam.kt} (92%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/{TimelineItemVideoContentProvider.kt => TimelineItemVideoContentPreviewParam.kt} (94%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/{TimelineItemVoiceContentProvider.kt => TimelineItemVoiceContentPreviewParam.kt} (95%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/{TimelineItemDaySeparatorModelProvider.kt => TimelineItemDaySeparatorModelPreviewParam.kt} (85%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/{AspectRatioProvider.kt => AspectRatioPreviewParam.kt} (88%) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/{TypingNotificationStateProvider.kt => TypingNotificationStatePreviewParam.kt} (97%) rename features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/{MigrationStateProvider.kt => MigrationStatePreviewParam.kt} (90%) rename features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/{CreatePollStateProvider.kt => CreatePollStatePreviewParam.kt} (98%) rename features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/{PollHistoryStateProvider.kt => PollHistoryStatePreviewParam.kt} (96%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/{AboutStateProvider.kt => AboutStatePreviewParam.kt} (90%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/{AdvancedSettingsStateProvider.kt => AdvancedSettingsStatePreviewParam.kt} (96%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/{AnalyticsSettingsStateProvider.kt => AnalyticsSettingsStatePreviewParam.kt} (87%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/{BlockedUsersStateProvider.kt => BlockedUsersStatePreviewParam.kt} (95%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/{DeveloperSettingsStateProvider.kt => DeveloperSettingsStatePreviewParam.kt} (96%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/{AppDeveloperSettingsStateProvider.kt => AppDeveloperSettingsStatePreviewParam.kt} (94%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/{LabsStateProvider.kt => LabsStatePreviewParam.kt} (95%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/{NotificationSettingsStateProvider.kt => NotificationSettingsStatePreviewParam.kt} (98%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/{EditDefaultNotificationSettingStateProvider.kt => EditDefaultNotificationSettingStatePreviewParam.kt} (95%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/{PreferencesRootStateProvider.kt => PreferencesRootStatePreviewParam.kt} (98%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/{EditUserProfileStateProvider.kt => EditUserProfileStatePreviewParam.kt} (95%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/{UserStatusStateProvider.kt => UserStatusStatePreviewParam.kt} (96%) rename features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/{RageshakePreferencesStateProvider.kt => RageshakePreferencesStatePreviewParam.kt} (91%) rename features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/{BugReportStateProvider.kt => BugReportStatePreviewParam.kt} (94%) rename features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/{ReportRoomStateProvider.kt => ReportRoomStatePreviewParam.kt} (93%) rename features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/{ChangeRoomPermissionsStateProvider.kt => ChangeRoomPermissionsStatePreviewParam.kt} (96%) rename features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/{ChangeRolesStateProvider.kt => ChangeRolesStatePreviewParam.kt} (98%) rename features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/{RolesAndPermissionsStateProvider.kt => RolesAndPermissionsStatePreviewParam.kt} (96%) rename features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/{RoomAliasResolverStateProvider.kt => RoomAliasResolverStatePreviewParam.kt} (93%) rename features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/{RoomCallStateProvider.kt => RoomCallStatePreviewParam.kt} (94%) rename features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/{RoomDetailsStateProvider.kt => RoomDetailsStatePreviewParam.kt} (99%) rename features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/{RoomMemberListStateProvider.kt => RoomMemberListStatePreviewParam.kt} (98%) rename features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/{RoomNotificationSettingsStateProvider.kt => RoomNotificationSettingsStatePreviewParam.kt} (95%) rename features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/{UserDefinedRoomNotificationSettingsStateProvider.kt => UserDefinedRoomNotificationSettingsStatePreviewParam.kt} (92%) rename features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/{RoomDetailsEditStateProvider.kt => RoomDetailsEditStatePreviewParam.kt} (96%) rename features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/{RoomDirectoryStateProvider.kt => RoomDirectoryStatePreviewParam.kt} (96%) rename features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/{InternalRoomMemberModerationStateProvider.kt => InternalRoomMemberModerationStatePreviewParam.kt} (97%) rename features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/{SecureBackupDisableStateProvider.kt => SecureBackupDisableStatePreviewParam.kt} (92%) rename features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/{SecureBackupEnterRecoveryKeyStateProvider.kt => SecureBackupEnterRecoveryKeyStatePreviewParam.kt} (94%) rename features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/{ResetIdentityPasswordStateProvider.kt => ResetIdentityPasswordStatePreviewParam.kt} (91%) rename features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/{ResetIdentityRootStateProvider.kt => ResetIdentityRootStatePreviewParam.kt} (88%) rename features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/{SecureBackupRootStateProvider.kt => SecureBackupRootStatePreviewParam.kt} (97%) rename features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/{SecureBackupSetupStateProvider.kt => SecureBackupSetupStatePreviewParam.kt} (95%) rename features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/{RecoveryKeyViewStateProvider.kt => RecoveryKeyViewStatePreviewParam.kt} (95%) rename features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/{EditRoomAddressStateProvider.kt => EditRoomAddressStatePreviewParam.kt} (94%) rename features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/{ManageAuthorizedSpacesStateProvider.kt => ManageAuthorizedSpacesStatePreviewParam.kt} (94%) rename features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/{SecurityAndPrivacyStateProvider.kt => SecurityAndPrivacyStatePreviewParam.kt} (98%) rename features/share/impl/src/main/kotlin/io/element/android/features/share/impl/{ShareStateProvider.kt => ShareStatePreviewParam.kt} (93%) rename features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/{SignedOutStateProvider.kt => SignedOutStatePreviewParam.kt} (94%) rename features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/{AddRoomToSpaceStateProvider.kt => AddRoomToSpaceStatePreviewParam.kt} (97%) rename features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/{LeaveSpaceStateProvider.kt => LeaveSpaceStatePreviewParam.kt} (98%) rename features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/{SpaceStateProvider.kt => SpaceStatePreviewParam.kt} (98%) rename features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/{SpaceSettingsStateProvider.kt => SpaceSettingsStatePreviewParam.kt} (94%) rename features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/{JoinRoomByAddressStateProvider.kt => JoinRoomByAddressStatePreviewParam.kt} (93%) rename features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/{StartChatStateProvider.kt => StartChatStatePreviewParam.kt} (97%) rename features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/{UserListStateProvider.kt => UserListStatePreviewParam.kt} (97%) rename features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/{UserProfileStateProvider.kt => UserProfileStatePreviewParam.kt} (97%) rename features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/{IncomingVerificationStateProvider.kt => IncomingVerificationStatePreviewParam.kt} (97%) rename features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/{OutgoingVerificationStateProvider.kt => OutgoingVerificationStatePreviewParam.kt} (97%) rename features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/{ViewFileStateProvider.kt => ViewFileStatePreviewParam.kt} (97%) rename features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/{ViewFolderStateProvider.kt => ViewFolderStatePreviewParam.kt} (92%) rename libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/{AccountSelectStateProvider.kt => AccountSelectStatePreviewParam.kt} (90%) rename libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/previews/{DateFormatterModeProvider.kt => DateFormatterModePreviewParam.kt} (86%) rename libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/{ComposerAlertMoleculeParamsProvider.kt => ComposerAlertMoleculeParamsPreviewParam.kt} (91%) rename libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/{AsyncActionProvider.kt => AsyncActionPreviewParam.kt} (90%) rename libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/{OverlapRatioProvider.kt => OverlapRatioPreviewParam.kt} (88%) rename libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/{ImageVectorProvider.kt => ImageVectorPreviewParam.kt} (89%) rename libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/{BooleanProvider.kt => BooleanPreviewParam.kt} (86%) rename libraries/emoji/impl/src/main/kotlin/io/element/android/libraries/emoji/impl/picker/{DefaultEmojiPickerStateProvider.kt => DefaultEmojiPickerStatePreviewParam.kt} (96%) rename libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/{AttachmentThumbnailInfoProvider.kt => AttachmentThumbnailInfoPreviewParam.kt} (94%) rename libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/{MatrixUserProvider.kt => MatrixUserPreviewParam.kt} (94%) rename libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/{SelectRoomInfoProvider.kt => SelectRoomInfoPreviewParam.kt} (95%) rename libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/{SpaceRoomProvider.kt => SpaceRoomPreviewParam.kt} (97%) rename libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/{InReplyToDetailsProvider.kt => InReplyToDetailsPreviewParam.kt} (95%) rename libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/{SenderNameDataProvider.kt => SenderNameDataPreviewParam.kt} (96%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/{MediaBottomSheetStateDeleteConfirmationProvider.kt => MediaBottomSheetStateDeleteConfirmationPreviewParam.kt} (90%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/{MediaBottomSheetStateDetailsProvider.kt => MediaBottomSheetStateDetailsPreviewParam.kt} (93%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/{MediaGalleryStateProvider.kt => MediaGalleryStatePreviewParam.kt} (98%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/{MediaItemAudioProvider.kt => MediaItemAudioPreviewParam.kt} (92%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/{MediaItemDateSeparatorProvider.kt => MediaItemDateSeparatorPreviewParam.kt} (88%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/{MediaItemFileProvider.kt => MediaItemFilePreviewParam.kt} (92%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/{MediaItemVideoProvider.kt => MediaItemVideoPreviewParam.kt} (90%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/{MediaItemVoiceProvider.kt => MediaItemVoicePreviewParam.kt} (92%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/{MediaInfoAudioProvider.kt => MediaInfoAudioPreviewParam.kt} (91%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/{MediaInfoFileProvider.kt => MediaInfoFilePreviewParam.kt} (91%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/{MediaPlayerControllerStateProvider.kt => MediaPlayerControllerStatePreviewParam.kt} (92%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/{TextFileContentProvider.kt => TextFileContentPreviewParam.kt} (89%) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/{MediaViewerStateProvider.kt => MediaViewerStatePreviewParam.kt} (99%) rename libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/{PermissionsStateProvider.kt => PermissionsStatePreviewParam.kt} (93%) rename libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/{RoomSelectStateProvider.kt => RoomSelectStatePreviewParam.kt} (97%) rename libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/{MessageComposerModeSpecialProvider.kt => MessageComposerModeSpecialPreviewParam.kt} (74%) rename libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/{TroubleshootNotificationsStateProvider.kt => TroubleshootNotificationsStatePreviewParam.kt} (97%) rename libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/{PushHistoryStateProvider.kt => PushHistoryStatePreviewParam.kt} (97%) rename libraries/voiceplayer/api/src/main/kotlin/io/element/android/libraries/voiceplayer/api/{VoiceMessageStateProvider.kt => VoiceMessageStatePreviewParam.kt} (95%) diff --git a/AGENTS.md b/AGENTS.md index d206be98c6..924d8bb76f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,7 +37,7 @@ PRs must meet these rules. - Create previews for **all main states** of a Composable. - Use `@PreviewsDayNight` for consistency. -- Use `PreviewParameterProvider` (e.g., `FooStateProvider`) to provide states. +- Use `PreviewParameterProvider` (e.g., `FooStatePreviewParam`) to provide states. - Wrap previews in `ElementPreview { ... }`. --- @@ -88,7 +88,7 @@ We use [Appyx](https://bumble-tech.github.io/appyx/) for navigation and [Molecul | `FooView.kt` | Stateless Composable rendering the UI from `FooState`. | | `FooState.kt` | Data class representing the immutable UI state. | | `FooEvent.kt` | Sealed interface for UI actions sent to the Presenter. | -| `FooStateProvider.kt` | Provides sample states for Previews and Screenshot tests. | +| `FooStatePreviewParam.kt` | Provides sample states for Previews and Screenshot tests. | | `FooPresenterTest.kt` | Unit tests for the Presenter logic using Turbine. | --- diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStatePreviewParam.kt similarity index 95% rename from appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt rename to appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStatePreviewParam.kt index 67fcb95c2c..b479e00279 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStatePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionDialog import io.element.android.libraries.push.api.PusherRegistrationFailure -open class LoggedInStateProvider : PreviewParameterProvider { +open class LoggedInStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLoggedInState(), diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index e57d378da4..7e607e7d92 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -124,7 +124,7 @@ private fun ForceNativeSlidingSyncMigrationDialog( @PreviewsDayNight @Composable -internal fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview { +internal fun LoggedInViewPreview(@PreviewParameter(LoggedInStatePreviewParam::class) state: LoggedInState) = ElementPreview { LoggedInView( state = state, navigateToNotificationTroubleshoot = {}, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/LoadingRoomNodeView.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/LoadingRoomNodeView.kt index a596052fca..175b662e08 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/LoadingRoomNodeView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/LoadingRoomNodeView.kt @@ -29,7 +29,7 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.DelayedVisibility import io.element.android.libraries.matrix.ui.room.LoadingRoomState -import io.element.android.libraries.matrix.ui.room.LoadingRoomStateProvider +import io.element.android.libraries.matrix.ui.room.LoadingRoomStatePreviewParam import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -84,7 +84,7 @@ private fun LoadingRoomTopBar( @PreviewsDayNight @Composable -internal fun LoadingRoomNodeViewPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = ElementPreview { +internal fun LoadingRoomNodeViewPreview(@PreviewParameter(LoadingRoomStatePreviewParam::class) state: LoadingRoomState) = ElementPreview { LoadingRoomNodeView( state = state, onBackClick = {} diff --git a/appnav/src/main/kotlin/io/element/android/appnav/root/RootStateProvider.kt b/appnav/src/main/kotlin/io/element/android/appnav/root/RootStatePreviewParam.kt similarity index 95% rename from appnav/src/main/kotlin/io/element/android/appnav/root/RootStateProvider.kt rename to appnav/src/main/kotlin/io/element/android/appnav/root/RootStatePreviewParam.kt index 26db205498..8ae3d261e8 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/root/RootStateProvider.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/root/RootStatePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.features.rageshake.api.detection.aRageshakeDetectionSt import io.element.android.services.apperror.api.AppErrorState import io.element.android.services.apperror.api.aAppErrorState -open class RootStateProvider : PreviewParameterProvider { +open class RootStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRootState().copy( diff --git a/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt b/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt index 19aa820433..aa6bcf1a88 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt @@ -60,7 +60,7 @@ fun RootView( @PreviewsDayNight @Composable -internal fun RootViewPreview(@PreviewParameter(RootStateProvider::class) rootState: RootState) = ElementPreview { +internal fun RootViewPreview(@PreviewParameter(RootStatePreviewParam::class) rootState: RootState) = ElementPreview { RootView( state = rootState, onOpenBugReport = {}, diff --git a/docs/_developer_onboarding.md b/docs/_developer_onboarding.md index dbac9107c4..e7515eb466 100644 --- a/docs/_developer_onboarding.md +++ b/docs/_developer_onboarding.md @@ -322,10 +322,10 @@ Example for a new feature called RoomDetails: 4. Verify that the structure looks ok and click on Finish 5. The modules api/impl should be created under `features/roomdetails` directory. 6. Sync project with Gradle so the modules are recognized (no need to add them to settings.gradle). -7. You can now add more Presentation classes (Events, State, StateProvider, View, Presenter) in the impl module with the `Template Presentation Classes`. +7. You can now add more Presentation classes (Events, State, StatePreviewParam, View, Presenter) in the impl module with the `Template Presentation Classes`. To use it, just right click on the package where you want to generate classes, and click on `New` / `Template Presentation Classes`. Fill the text field with the base name of the classes: the generated files are `Event.kt`, `State.kt`, - `StateProvider.kt`, `Presenter.kt`, `Node.kt` and `View.kt`. For instance + `StatePreviewParam.kt`, `Presenter.kt`, `Node.kt` and `View.kt`. For instance `PreferencesRoot` in the `features/preferences/impl/root` package. @@ -360,9 +360,9 @@ We have 3 tests frameworks in place, and this should be sufficient to guarantee - Maestro to test the global usage of the application. See the related [documentation](../.maestro/README.md). - Combination of [Showkase](https://github.com/airbnb/Showkase) and [Paparazzi](https://github.com/cashapp/paparazzi), to test UI pixel perfect. To add test, just add `@Preview` for the composable you are adding. See the related [documentation](screenshot_testing.md) and see in the template the - file [TemplateView.kt](../features/template/src/main/kotlin/io/element/android/features/template/TemplateView.kt). We create PreviewProvider to provide + file [TemplateView.kt](../features/template/src/main/kotlin/io/element/android/features/template/TemplateView.kt). We create PreviewParam classes to provide different states. See for instance the - file [TemplateStateProvider.kt](../features/template/src/main/kotlin/io/element/android/features/template/TemplateStateProvider.kt) + file [TemplateStatePreviewParam.kt](../features/template/src/main/kotlin/io/element/android/features/template/TemplateStatePreviewParam.kt) - Tests on presenter with [Molecule](https://github.com/cashapp/molecule) and [Turbine](https://github.com/cashapp/turbine). See in the template the class [TemplatePresenterTests](../features/template/src/test/kotlin/io/element/android/features/template/TemplatePresenterTests.kt). **Note** For now we want to avoid using class mocking (with library such as *mockk*), because this should be not necessary. We prefer to create Fake diff --git a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesStateProvider.kt b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesStatePreviewParam.kt similarity index 89% rename from features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesStateProvider.kt rename to features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesStatePreviewParam.kt index 02e07a86a2..89d19ebac8 100644 --- a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesStateProvider.kt +++ b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.analytics.api.preferences import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class AnalyticsPreferencesStateProvider : PreviewParameterProvider { +open class AnalyticsPreferencesStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aAnalyticsPreferencesState().copy(isEnabled = true), diff --git a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt index 1158b57673..0d141e5ed2 100644 --- a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt +++ b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt @@ -66,7 +66,7 @@ fun AnalyticsPreferencesView( @PreviewsDayNight @Composable -internal fun AnalyticsPreferencesViewPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) = +internal fun AnalyticsPreferencesViewPreview(@PreviewParameter(AnalyticsPreferencesStatePreviewParam::class) state: AnalyticsPreferencesState) = ElementPreview { AnalyticsPreferencesView( state = state, diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInStateProvider.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInStatePreviewParam.kt similarity index 88% rename from features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInStateProvider.kt rename to features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInStatePreviewParam.kt index 30a396c178..960abb7216 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInStateProvider.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.analytics.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class AnalyticsOptInStateProvider : PreviewParameterProvider { +open class AnalyticsOptInStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aAnalyticsOptInState(), diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt index c05edd825c..678d28c5c9 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt @@ -171,7 +171,7 @@ private fun AnalyticsOptInFooter( @PreviewsDayNight @Composable -internal fun AnalyticsOptInViewPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreview { +internal fun AnalyticsOptInViewPreview(@PreviewParameter(AnalyticsOptInStatePreviewParam::class) state: AnalyticsOptInState) = ElementPreview { AnalyticsOptInView( state = state, onClickTerms = {}, diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementStateProvider.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementStatePreviewParam.kt similarity index 90% rename from features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementStateProvider.kt rename to features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementStatePreviewParam.kt index 2412fee167..df7afad32f 100644 --- a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementStateProvider.kt +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.announcement.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.announcement.api.Announcement -open class AnnouncementStateProvider : PreviewParameterProvider { +open class AnnouncementStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAnnouncementState(), diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/fullscreen/FullscreenAnnouncementView.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/fullscreen/FullscreenAnnouncementView.kt index 5f7778900b..cb962f8968 100644 --- a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/fullscreen/FullscreenAnnouncementView.kt +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/fullscreen/FullscreenAnnouncementView.kt @@ -33,7 +33,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.announcement.api.Announcement import io.element.android.features.announcement.impl.AnnouncementEvent import io.element.android.features.announcement.impl.AnnouncementState -import io.element.android.features.announcement.impl.AnnouncementStateProvider +import io.element.android.features.announcement.impl.AnnouncementStatePreviewParam import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage @@ -216,7 +216,7 @@ private fun Announcement.Fullscreen.notice(): String? = when (this) { @PreviewsDayNight @Composable -internal fun FullscreenAnnouncementViewPreview(@PreviewParameter(AnnouncementStateProvider::class) state: AnnouncementState) = ElementPreview { +internal fun FullscreenAnnouncementViewPreview(@PreviewParameter(AnnouncementStatePreviewParam::class) state: AnnouncementState) = ElementPreview { FullscreenAnnouncementView( state = state, ) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallNotificationDataProvider.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallNotificationDataPreviewParam.kt similarity index 94% rename from features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallNotificationDataProvider.kt rename to features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallNotificationDataPreviewParam.kt index 9e551b3e1b..8ca3893f2b 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallNotificationDataProvider.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallNotificationDataPreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId -open class CallNotificationDataProvider : PreviewParameterProvider { +open class CallNotificationDataPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aCallNotificationData( diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStatePreviewParam.kt similarity index 93% rename from features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt rename to features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStatePreviewParam.kt index 155c5d3380..cc21197a51 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.call.impl.ui import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncData -open class CallScreenStateProvider : PreviewParameterProvider { +open class CallScreenStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aCallScreenState(), diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt index 2537eb739c..e96033806b 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt @@ -260,7 +260,7 @@ private fun WebView.dispatchEscKeyEvent() { @PreviewsDayNight @Composable internal fun CallScreenViewPreview( - @PreviewParameter(CallScreenStateProvider::class) state: CallScreenState, + @PreviewParameter(CallScreenStatePreviewParam::class) state: CallScreenState, ) = ElementPreview { CallScreenView( state = state, diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallScreen.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallScreen.kt index 72edd59ed5..1489d9d0e7 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallScreen.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallScreen.kt @@ -161,7 +161,7 @@ private fun ActionButton( @PreviewsDayNight @Composable internal fun IncomingCallScreenPreview( - @PreviewParameter(CallNotificationDataProvider::class) state: CallNotificationData, + @PreviewParameter(CallNotificationDataPreviewParam::class) state: CallNotificationData, ) = ElementPreview { IncomingCallScreen( notificationData = state, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt index e03d36363d..f9075691aa 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt @@ -23,7 +23,7 @@ import androidx.compose.ui.unit.dp import io.element.android.features.createroom.impl.R import io.element.android.features.invitepeople.api.InvitePeopleEvents import io.element.android.features.invitepeople.api.InvitePeopleState -import io.element.android.features.invitepeople.api.InvitePeopleStateProvider +import io.element.android.features.invitepeople.api.InvitePeopleStatePreviewParam import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -86,7 +86,7 @@ private fun AddPeopleTopBar( @PreviewsDayNight @Composable -internal fun AddPeopleViewPreview(@PreviewParameter(InvitePeopleStateProvider::class) state: InvitePeopleState) = ElementPreview { +internal fun AddPeopleViewPreview(@PreviewParameter(InvitePeopleStatePreviewParam::class) state: InvitePeopleState) = ElementPreview { AddPeopleView( state = state, invitePeopleView = {}, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStatePreviewParam.kt similarity index 98% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStatePreviewParam.kt index 9c2ad76a9b..151c7d13d0 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStatePreviewParam.kt @@ -21,7 +21,7 @@ import io.element.android.libraries.permissions.api.aPermissionsState import io.element.android.libraries.previewutils.room.aSpaceRoom import kotlinx.collections.immutable.toImmutableList -open class ConfigureRoomStateProvider : PreviewParameterProvider { +open class ConfigureRoomStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aConfigureRoomState(), diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index c929060e13..e39df69f97 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -349,12 +349,12 @@ private fun RoomJoinRuleOptions( @PreviewWithLargeHeight @Composable -internal fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = +internal fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStatePreviewParam::class) state: ConfigureRoomState) = ElementPreviewLight { ContentToPreview(state) } @PreviewWithLargeHeight @Composable -internal fun ConfigureRoomViewDarkPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = +internal fun ConfigureRoomViewDarkPreview(@PreviewParameter(ConfigureRoomStatePreviewParam::class) state: ConfigureRoomState) = ElementPreviewDark { ContentToPreview(state) } @ExcludeFromCoverage diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStateProvider.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStatePreviewParam.kt similarity index 94% rename from features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStateProvider.kt rename to features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStatePreviewParam.kt index 5c832a4d75..bd86af9572 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStateProvider.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.logout.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction -open class AccountDeactivationStateProvider : PreviewParameterProvider { +open class AccountDeactivationStatePreviewParam : PreviewParameterProvider { private val filledForm = aDeactivateFormState(eraseData = true, password = "password") override val values: Sequence get() = sequenceOf( diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt index 738046b3a3..a19663f50f 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt @@ -308,7 +308,7 @@ private fun String.sanitize(): String { @PreviewsDayNight @Composable internal fun AccountDeactivationViewPreview( - @PreviewParameter(AccountDeactivationStateProvider::class) state: AccountDeactivationState, + @PreviewParameter(AccountDeactivationStatePreviewParam::class) state: AccountDeactivationState, ) = ElementPreview { AccountDeactivationView( state, diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesStateProvider.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesStatePreviewParam.kt similarity index 92% rename from features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesStateProvider.kt rename to features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesStatePreviewParam.kt index bbb5b380ae..8ff621c2a6 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesStateProvider.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId -open class ForwardMessagesStateProvider : PreviewParameterProvider { +open class ForwardMessagesStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aForwardMessagesState(), diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt index 8065054b2d..f350c5b516 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt @@ -38,7 +38,7 @@ fun ForwardMessagesView( @PreviewsDayNight @Composable -internal fun ForwardMessagesViewPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) = ElementPreview { +internal fun ForwardMessagesViewPreview(@PreviewParameter(ForwardMessagesStatePreviewParam::class) state: ForwardMessagesState) = ElementPreview { ForwardMessagesView( state = state, onForwardSuccess = {} diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInStateProvider.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInStatePreviewParam.kt similarity index 88% rename from features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInStateProvider.kt rename to features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInStatePreviewParam.kt index 1b2e5d2d58..07569a238e 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInStateProvider.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.ftue.impl.notifications import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.permissions.api.aPermissionsState -open class NotificationsOptInStateProvider : PreviewParameterProvider { +open class NotificationsOptInStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aNotificationsOptInState(), diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt index 6955d8e501..e2eabe8f2b 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt @@ -178,7 +178,7 @@ private fun NotificationRow( @PreviewsDayNight @Composable internal fun NotificationsOptInViewPreview( - @PreviewParameter(NotificationsOptInStateProvider::class) state: NotificationsOptInState + @PreviewParameter(NotificationsOptInStatePreviewParam::class) state: NotificationsOptInState ) { ElementPreview { NotificationsOptInView( diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeStateProvider.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeStatePreviewParam.kt similarity index 97% rename from features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeStateProvider.kt rename to features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeStatePreviewParam.kt index b35a9624b9..1af6786289 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeStateProvider.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.logout.api.direct.aDirectLogoutState import io.element.android.libraries.architecture.AsyncData -class ChooseSelfVerificationModeStateProvider : +class ChooseSelfVerificationModeStatePreviewParam : PreviewParameterProvider { override val values = sequenceOf( aChooseSelfVerificationModeState( diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt index 1bfa10daf2..96d6b82d47 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt @@ -149,7 +149,7 @@ private fun ChooseSelfVerificationModeButtons( @PreviewsDayNight @Composable internal fun ChooseSelfVerificationModeViewPreview( - @PreviewParameter(ChooseSelfVerificationModeStateProvider::class) state: ChooseSelfVerificationModeState + @PreviewParameter(ChooseSelfVerificationModeStatePreviewParam::class) state: ChooseSelfVerificationModeState ) = ElementPreview { ChooseSelfVerificationModeView( state = state, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStatePreviewParam.kt similarity index 96% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStateProvider.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStatePreviewParam.kt index ea9ff7a234..14ab6dc7f6 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.home.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.home.impl.roomlist.RoomListState -import io.element.android.features.home.impl.roomlist.RoomListStateProvider +import io.element.android.features.home.impl.roomlist.RoomListStatePreviewParam import io.element.android.features.home.impl.roomlist.aRoomListState import io.element.android.features.home.impl.roomlist.aRoomsContentState import io.element.android.features.home.impl.roomlist.generateRoomListRoomSummaryList @@ -24,7 +24,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.toImmutableList -open class HomeStateProvider : PreviewParameterProvider { +open class HomeStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aHomeState(), @@ -43,7 +43,7 @@ open class HomeStateProvider : PreviewParameterProvider { aHomeState( currentHomeNavigationBarItem = HomeNavigationBarItem.Spaces, ), - ) + RoomListStateProvider().values.map { + ) + RoomListStatePreviewParam().values.map { aHomeState(roomListState = it) } + aHomeState( currentHomeNavigationBarItem = HomeNavigationBarItem.Spaces, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt index f5a2400f2f..734c3ecb69 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt @@ -346,7 +346,7 @@ internal fun RoomListRoomSummary.contentType() = displayType.ordinal @PreviewsDayNight @Composable -internal fun HomeViewPreview(@PreviewParameter(HomeStateProvider::class) state: HomeState) = ElementPreview { +internal fun HomeViewPreview(@PreviewParameter(HomeStatePreviewParam::class) state: HomeState) = ElementPreview { HomeView( homeState = state, onRoomClick = {}, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt index 266b6f5a59..e06fe7c46d 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt @@ -42,7 +42,7 @@ import io.element.android.features.home.impl.filters.selection.FilterSelectionSt import io.element.android.features.home.impl.model.RoomListRoomSummary import io.element.android.features.home.impl.model.RoomSummaryDisplayType import io.element.android.features.home.impl.roomlist.RoomListContentState -import io.element.android.features.home.impl.roomlist.RoomListContentStateProvider +import io.element.android.features.home.impl.roomlist.RoomListContentStatePreviewParam import io.element.android.features.home.impl.roomlist.RoomListEvent import io.element.android.features.home.impl.roomlist.SecurityBannerState import io.element.android.features.home.impl.spacefilters.SpaceFiltersState @@ -336,7 +336,7 @@ private fun EmptyScaffold( @PreviewsDayNight @Composable -internal fun RoomListContentViewPreview(@PreviewParameter(RoomListContentStateProvider::class) state: RoomListContentState) = ElementPreview { +internal fun RoomListContentViewPreview(@PreviewParameter(RoomListContentStatePreviewParam::class) state: RoomListContentState) = ElementPreview { RoomListContentView( contentState = state, filtersState = aRoomListFiltersState( diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt index 2a21518e83..883d8aff76 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt @@ -43,7 +43,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.home.impl.R import io.element.android.features.home.impl.model.LatestEvent import io.element.android.features.home.impl.model.RoomListRoomSummary -import io.element.android.features.home.impl.model.RoomListRoomSummaryProvider +import io.element.android.features.home.impl.model.RoomListRoomSummaryPreviewParam import io.element.android.features.home.impl.model.RoomSummaryDisplayType import io.element.android.features.home.impl.roomlist.RoomListEvent import io.element.android.libraries.core.extensions.orEmpty @@ -449,7 +449,7 @@ private fun MentionIndicatorAtom() { @PreviewsDayNight @Composable -internal fun RoomSummaryRowPreview(@PreviewParameter(RoomListRoomSummaryProvider::class) data: RoomListRoomSummary) = ElementPreview { +internal fun RoomSummaryRowPreview(@PreviewParameter(RoomListRoomSummaryPreviewParam::class) data: RoomListRoomSummary) = ElementPreview { RoomSummaryRow( room = data, hideInviteAvatars = false, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersStatePreviewParam.kt similarity index 92% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersStateProvider.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersStatePreviewParam.kt index 7e095512fe..8118a65e1d 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.home.impl.filters.selection.FilterSelectionState import kotlinx.collections.immutable.toImmutableList -class RoomListFiltersStateProvider : PreviewParameterProvider { +class RoomListFiltersStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomListFiltersState(), diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersView.kt index 9931a98c4e..c671eecf45 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersView.kt @@ -212,7 +212,7 @@ private fun RoomListFilterView( @PreviewsDayNight @Composable -internal fun RoomListFiltersViewPreview(@PreviewParameter(RoomListFiltersStateProvider::class) state: RoomListFiltersState) = ElementPreview { +internal fun RoomListFiltersViewPreview(@PreviewParameter(RoomListFiltersStatePreviewParam::class) state: RoomListFiltersState) = ElementPreview { RoomListFiltersView( modifier = Modifier.padding(vertical = 4.dp), state = state, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryPreviewParam.kt similarity index 99% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryProvider.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryPreviewParam.kt index 2e75858e3e..c04c3e446b 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryPreviewParam.kt @@ -24,7 +24,7 @@ import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.ui.model.InviteSender import kotlinx.collections.immutable.toImmutableList -open class RoomListRoomSummaryProvider : PreviewParameterProvider { +open class RoomListRoomSummaryPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( listOf( diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStatePreviewParam.kt similarity index 96% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStateProvider.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStatePreviewParam.kt index 0ac2b7bf62..4ca87992f9 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStatePreviewParam.kt @@ -19,7 +19,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableSet -open class RoomListContentStateProvider : PreviewParameterProvider { +open class RoomListContentStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomsContentState(), diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenu.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenu.kt index bfa6962e8b..6a334a94c2 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenu.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenu.kt @@ -199,7 +199,7 @@ private fun RoomListModalBottomSheetContent( @PreviewsDayNight @Composable internal fun RoomListContextMenuPreview( - @PreviewParameter(RoomListStateContextMenuShownProvider::class) contextMenu: RoomListState.ContextMenu.Shown + @PreviewParameter(RoomListStateContextMenuShownPreviewParam::class) contextMenu: RoomListState.ContextMenu.Shown ) = ElementPreview(fillMaxSize = true) { RoomListContextMenu( contextMenu = contextMenu, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListDeclineInviteMenu.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListDeclineInviteMenu.kt index 0a7a29ebc2..022696cbb1 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListDeclineInviteMenu.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListDeclineInviteMenu.kt @@ -126,7 +126,7 @@ private fun RoomListDeclineInviteMenuContent( @PreviewsDayNight @Composable internal fun RoomListDeclineInviteMenuPreview( - @PreviewParameter(RoomListStateDeclineInviteMenuShownProvider::class) menu: RoomListState.DeclineInviteMenu.Shown, + @PreviewParameter(RoomListStateDeclineInviteMenuShownPreviewParam::class) menu: RoomListState.DeclineInviteMenu.Shown, ) = ElementPreview { RoomListDeclineInviteMenu( menu = menu, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateContextMenuShownProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateContextMenuShownPreviewParam.kt similarity index 90% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateContextMenuShownProvider.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateContextMenuShownPreviewParam.kt index eca1b63d7d..f4bd0eac2e 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateContextMenuShownProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateContextMenuShownPreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.home.impl.roomlist import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.matrix.api.core.RoomId -open class RoomListStateContextMenuShownProvider : PreviewParameterProvider { +open class RoomListStateContextMenuShownPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aContextMenuShown(hasNewContent = true), diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateDeclineInviteMenuShownProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateDeclineInviteMenuShownPreviewParam.kt similarity index 90% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateDeclineInviteMenuShownProvider.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateDeclineInviteMenuShownPreviewParam.kt index 73d4785e96..c19da0595a 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateDeclineInviteMenuShownProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateDeclineInviteMenuShownPreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.datasource.LoremIpsum import io.element.android.features.home.impl.model.RoomListRoomSummary import io.element.android.features.home.impl.model.aRoomListRoomSummary -open class RoomListStateDeclineInviteMenuShownProvider : PreviewParameterProvider { +open class RoomListStateDeclineInviteMenuShownPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aDeclineInviteMenuShown(), diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStatePreviewParam.kt similarity index 98% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateProvider.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStatePreviewParam.kt index 97600e0247..a0db1ee0a3 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStatePreviewParam.kt @@ -31,7 +31,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -open class RoomListStateProvider : PreviewParameterProvider { +open class RoomListStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomListState(), diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchStatePreviewParam.kt similarity index 93% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchStateProvider.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchStatePreviewParam.kt index e6ec92f68b..2b857354a3 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.features.home.impl.roomlist.aRoomListRoomSummaryList import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -class RoomListSearchStateProvider : PreviewParameterProvider { +class RoomListSearchStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomListSearchState(), diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchView.kt index 0535ded5c5..ff354cf717 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchView.kt @@ -196,7 +196,7 @@ private fun RoomListSearchContent( @PreviewsDayNight @Composable -internal fun RoomListSearchContentPreview(@PreviewParameter(RoomListSearchStateProvider::class) state: RoomListSearchState) = ElementPreview { +internal fun RoomListSearchContentPreview(@PreviewParameter(RoomListSearchStatePreviewParam::class) state: RoomListSearchState) = ElementPreview { RoomListSearchContent( state = state, hideInvitesAvatars = false, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersStatePreviewParam.kt similarity index 97% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersStateProvider.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersStatePreviewParam.kt index 264d122836..335b801908 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter import io.element.android.libraries.previewutils.room.aSpaceRoom import kotlinx.collections.immutable.toImmutableList -class SpaceFiltersStateProvider : PreviewParameterProvider { +class SpaceFiltersStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSelectingSpaceFiltersState(), diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt index 280f41b909..e4573f344e 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt @@ -189,6 +189,6 @@ private fun SpaceFilterItem( @PreviewsDayNight @Composable -internal fun SpaceFiltersViewPreview(@PreviewParameter(SpaceFiltersStateProvider::class) state: SpaceFiltersState) = ElementPreview(fillMaxSize = true) { +internal fun SpaceFiltersViewPreview(@PreviewParameter(SpaceFiltersStatePreviewParam::class) state: SpaceFiltersState) = ElementPreview(fillMaxSize = true) { SpaceFiltersView(state = state) } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStatePreviewParam.kt similarity index 93% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStateProvider.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStatePreviewParam.kt index 17f2cbad31..57b219d302 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStatePreviewParam.kt @@ -15,11 +15,11 @@ import io.element.android.libraries.previewutils.room.aSpaceRoom import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableSet -open class HomeSpacesStateProvider : PreviewParameterProvider { +open class HomeSpacesStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aHomeSpacesState( - spaceRooms = SpaceRoomProvider().values.toList(), + spaceRooms = SpaceRoomPreviewParam().values.toList(), seenSpaceInvites = setOf( RoomId("!spaceId3:example.com"), ), diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt index 9125912bf0..914d08d7f6 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt @@ -171,7 +171,7 @@ private fun EmptySpaceHomeView( @PreviewsDayNight @Composable internal fun HomeSpacesViewPreview( - @PreviewParameter(HomeSpacesStateProvider::class) state: HomeSpacesState, + @PreviewParameter(HomeSpacesStatePreviewParam::class) state: HomeSpacesState, ) = ElementPreview { HomeSpacesView( state = state, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/SpaceRoomProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/SpaceRoomPreviewParam.kt similarity index 95% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/SpaceRoomProvider.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/SpaceRoomPreviewParam.kt index 4a77e45372..25744157aa 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/SpaceRoomProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/SpaceRoomPreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.previewutils.room.aSpaceRoom -class SpaceRoomProvider : PreviewParameterProvider { +class SpaceRoomPreviewParam : PreviewParameterProvider { override val values: Sequence = sequenceOf( aSpaceRoom(), aSpaceRoom( diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteStateProvider.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteStatePreviewParam.kt similarity index 100% rename from features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteStateProvider.kt rename to features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteStatePreviewParam.kt diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteStateProvider.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteStatePreviewParam.kt similarity index 95% rename from features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteStateProvider.kt rename to features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteStatePreviewParam.kt index db2e76c1c3..ad9bf01667 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteStateProvider.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteStatePreviewParam.kt @@ -18,7 +18,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.preview.ROOM_NAME import io.element.android.libraries.matrix.api.core.RoomId -open class AcceptDeclineInviteStateProvider : PreviewParameterProvider { +open class AcceptDeclineInviteStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAcceptDeclineInviteState(), diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt index da035f0ff1..c3df984604 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt @@ -123,7 +123,7 @@ private fun DeclineConfirmationDialog( @PreviewsDayNight @Composable -internal fun AcceptDeclineInviteViewPreview(@PreviewParameter(AcceptDeclineInviteStateProvider::class) state: AcceptDeclineInviteState) = +internal fun AcceptDeclineInviteViewPreview(@PreviewParameter(AcceptDeclineInviteStatePreviewParam::class) state: AcceptDeclineInviteState) = ElementPreview { AcceptDeclineInviteView( state = state, diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockStateProvider.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockStatePreviewParam.kt similarity index 93% rename from features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockStateProvider.kt rename to features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockStatePreviewParam.kt index 2e78214306..704fc9f045 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockStateProvider.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.invite.impl.declineandblock import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction -open class DeclineAndBlockStateProvider : PreviewParameterProvider { +open class DeclineAndBlockStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aDeclineAndBlockState(), diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockView.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockView.kt index 1d14df024a..fe82be5b1a 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockView.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockView.kt @@ -144,7 +144,7 @@ fun DeclineAndBlockView( @PreviewsDayNight @Composable internal fun DeclineAndBlockViewPreview( - @PreviewParameter(DeclineAndBlockStateProvider::class) state: DeclineAndBlockState + @PreviewParameter(DeclineAndBlockStatePreviewParam::class) state: DeclineAndBlockState ) = ElementPreview { DeclineAndBlockView( state = state, diff --git a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStateProvider.kt b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStatePreviewParam.kt similarity index 95% rename from features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStateProvider.kt rename to features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStatePreviewParam.kt index b233ed07ef..860efc2771 100644 --- a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStateProvider.kt +++ b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId -class InvitePeopleStateProvider : PreviewParameterProvider { +class InvitePeopleStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aPreviewInvitePeopleState(), diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleStateProvider.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleStatePreviewParam.kt similarity index 98% rename from features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleStateProvider.kt rename to features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleStatePreviewParam.kt index 1e41114406..9aad913856 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleStateProvider.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleStatePreviewParam.kt @@ -26,7 +26,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -internal class DefaultInvitePeopleStateProvider : PreviewParameterProvider { +internal class DefaultInvitePeopleStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aDefaultInvitePeopleState(), diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt index 0ee3559aa3..7dced41b13 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt @@ -316,7 +316,7 @@ private fun InvitePeopleConfirmModal( @PreviewsDayNight @Composable -internal fun InvitePeopleViewPreview(@PreviewParameter(DefaultInvitePeopleStateProvider::class) state: DefaultInvitePeopleState) = +internal fun InvitePeopleViewPreview(@PreviewParameter(DefaultInvitePeopleStatePreviewParam::class) state: DefaultInvitePeopleState) = ElementPreview { InvitePeopleView(state = state) } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStatePreviewParam.kt similarity index 99% rename from features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt rename to features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStatePreviewParam.kt index 4bfa741321..f47377b16b 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStatePreviewParam.kt @@ -30,7 +30,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.model.InviteSender import kotlinx.collections.immutable.toImmutableList -open class JoinRoomStateProvider : PreviewParameterProvider { +open class JoinRoomStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aJoinRoomState( diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index 977308178d..f1adf1705f 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -642,7 +642,7 @@ private fun JoinRoomTopBar( @PreviewsDayNight @Composable -internal fun JoinRoomViewPreview(@PreviewParameter(JoinRoomStateProvider::class) state: JoinRoomState) = ElementPreview { +internal fun JoinRoomViewPreview(@PreviewParameter(JoinRoomStatePreviewParam::class) state: JoinRoomState) = ElementPreview { JoinRoomView( state = state, onBackClick = { }, diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStateProvider.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStatePreviewParam.kt similarity index 96% rename from features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStateProvider.kt rename to features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStatePreviewParam.kt index 7210e783fe..fb05ae1fe0 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStateProvider.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStatePreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.designsystem.preview.USER_NAME_BOB import io.element.android.libraries.designsystem.preview.USER_NAME_CHARLIE import kotlinx.collections.immutable.toImmutableList -class KnockRequestsBannerStateProvider : PreviewParameterProvider { +class KnockRequestsBannerStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aKnockRequestsBannerState(), diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerView.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerView.kt index 431bd14d10..037ec16d00 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerView.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerView.kt @@ -227,7 +227,7 @@ private fun KnockRequestAvatarListView( @Composable @PreviewsDayNight -internal fun KnockRequestsBannerViewPreview(@PreviewParameter(KnockRequestsBannerStateProvider::class) state: KnockRequestsBannerState) = ElementPreview { +internal fun KnockRequestsBannerViewPreview(@PreviewParameter(KnockRequestsBannerStatePreviewParam::class) state: KnockRequestsBannerState) = ElementPreview { KnockRequestsBannerView( state = state, onViewRequestsClick = {}, diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStateProvider.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStatePreviewParam.kt similarity index 98% rename from features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStateProvider.kt rename to features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStatePreviewParam.kt index 85fc0675ad..b51223a5e9 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStateProvider.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStatePreviewParam.kt @@ -18,7 +18,7 @@ import io.element.android.libraries.matrix.api.core.UserId import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -open class KnockRequestsListStateProvider : PreviewParameterProvider { +open class KnockRequestsListStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aKnockRequestsListState( diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt index db40596892..d750927f74 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt @@ -491,7 +491,7 @@ private fun KnockRequestsListTopBar(onBackClick: () -> Unit) { @PreviewsDayNight @Composable internal fun KnockRequestsListViewPreview( - @PreviewParameter(KnockRequestsListStateProvider::class) state: KnockRequestsListState + @PreviewParameter(KnockRequestsListStatePreviewParam::class) state: KnockRequestsListState ) = ElementPreview { KnockRequestsListView( state = state, diff --git a/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/InternalLeaveRoomStateProvider.kt b/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/InternalLeaveRoomStatePreviewParam.kt similarity index 94% rename from features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/InternalLeaveRoomStateProvider.kt rename to features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/InternalLeaveRoomStatePreviewParam.kt index 769015f301..5f68a6f192 100644 --- a/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/InternalLeaveRoomStateProvider.kt +++ b/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/InternalLeaveRoomStatePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId -class InternalLeaveRoomStateProvider : PreviewParameterProvider { +class InternalLeaveRoomStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLeaveRoomState(), diff --git a/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomView.kt b/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomView.kt index 131801b014..c3a916b141 100644 --- a/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomView.kt +++ b/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomView.kt @@ -139,7 +139,7 @@ private fun LeaveRoomProgressDialog(modifier: Modifier = Modifier) { @PreviewsDayNight @Composable internal fun LeaveRoomViewPreview( - @PreviewParameter(InternalLeaveRoomStateProvider::class) state: InternalLeaveRoomState + @PreviewParameter(InternalLeaveRoomStatePreviewParam::class) state: InternalLeaveRoomState ) = ElementPreview { Box( modifier = Modifier.size(300.dp, 300.dp), diff --git a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListStateProvider.kt b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListStatePreviewParam.kt similarity index 95% rename from features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListStateProvider.kt rename to features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListStatePreviewParam.kt index 6a74cc01eb..b984c43398 100644 --- a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListStateProvider.kt +++ b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.architecture.AsyncData import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -open class DependencyLicensesListStateProvider : PreviewParameterProvider { +open class DependencyLicensesListStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aDependencyLicensesListState( diff --git a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListView.kt b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListView.kt index 943f57ab3b..17f82cd14e 100644 --- a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListView.kt +++ b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListView.kt @@ -126,7 +126,7 @@ fun DependencyLicensesListView( @PreviewsDayNight @Composable internal fun DependencyLicensesListViewPreview( - @PreviewParameter(DependencyLicensesListStateProvider::class) state: DependencyLicensesListState + @PreviewParameter(DependencyLicensesListStatePreviewParam::class) state: DependencyLicensesListState ) = ElementPreview { DependencyLicensesListView( state = state, diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeStateProvider.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeStatePreviewParam.kt similarity index 92% rename from features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeStateProvider.kt rename to features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeStatePreviewParam.kt index 194bd6fafc..c539563fc7 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeStateProvider.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeStatePreviewParam.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.permissions.api.PermissionsState import io.element.android.libraries.permissions.api.aPermissionsState -open class DesktopNoticeStateProvider : PreviewParameterProvider { +open class DesktopNoticeStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aDesktopNoticeState(), diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeView.kt index a66ea7b391..935c848e42 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeView.kt @@ -118,7 +118,7 @@ fun DesktopNoticeView( @PreviewsDayNight @Composable internal fun DesktopNoticeViewPreview( - @PreviewParameter(DesktopNoticeStateProvider::class) state: DesktopNoticeState, + @PreviewParameter(DesktopNoticeStatePreviewParam::class) state: DesktopNoticeState, ) = ElementPreview { DesktopNoticeView( state = state, diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenTypeProvider.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenTypePreviewParam.kt similarity index 90% rename from features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenTypeProvider.kt rename to features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenTypePreviewParam.kt index 5946eb9ab2..bfaf511e66 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenTypeProvider.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenTypePreviewParam.kt @@ -9,7 +9,7 @@ package io.element.android.features.linknewdevice.impl.screens.error import androidx.compose.ui.tooling.preview.PreviewParameterProvider -class ErrorScreenTypeProvider : PreviewParameterProvider { +class ErrorScreenTypePreviewParam : PreviewParameterProvider { override val values: Sequence = sequenceOf( ErrorScreenType.Cancelled, ErrorScreenType.Declined, diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt index 7b830626f6..90e9c4616b 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt @@ -165,7 +165,7 @@ private fun ColumnScope.Buttons( @PreviewsDayNight @Composable -internal fun ErrorViewPreview(@PreviewParameter(ErrorScreenTypeProvider::class) errorScreenType: ErrorScreenType) { +internal fun ErrorViewPreview(@PreviewParameter(ErrorScreenTypePreviewParam::class) errorScreenType: ErrorScreenType) { ElementPreview { ErrorView( errorScreenType = errorScreenType, diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberStateProvider.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberStatePreviewParam.kt similarity index 93% rename from features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberStateProvider.kt rename to features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberStatePreviewParam.kt index 126bfeee52..56071f3e8f 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberStateProvider.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberStatePreviewParam.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.linknewdevice.ErrorType -open class EnterNumberStateProvider : PreviewParameterProvider { +open class EnterNumberStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aEnterNumberState(), diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberView.kt index 240a3143af..b60fd285af 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberView.kt @@ -117,7 +117,7 @@ fun EnterNumberView( @PreviewsDayNight @Composable internal fun EnterNumberViewPreview( - @PreviewParameter(EnterNumberStateProvider::class) state: EnterNumberState, + @PreviewParameter(EnterNumberStatePreviewParam::class) state: EnterNumberState, ) = ElementPreview { EnterNumberView( state = state, diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStatePreviewParam.kt similarity index 90% rename from features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt rename to features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStatePreviewParam.kt index e6d33c2544..bc424746ae 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.linknewdevice.impl.screens.qrcode import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncData -class ShowQrCodeStateProvider : PreviewParameterProvider { +class ShowQrCodeStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aShowQrCodeState(), diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt index f2cd07f4a5..4cd22e37d2 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt @@ -117,7 +117,7 @@ private fun QrCodeOrLoading( @PreviewsDayNight @Composable internal fun ShowQrCodeViewPreview( - @PreviewParameter(ShowQrCodeStateProvider::class) state: ShowQrCodeState, + @PreviewParameter(ShowQrCodeStatePreviewParam::class) state: ShowQrCodeState, ) = ElementPreview { ShowQrCodeView( state = state, diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootStateProvider.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootStatePreviewParam.kt similarity index 94% rename from features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootStateProvider.kt rename to features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootStatePreviewParam.kt index f1bb7ad455..20721e4a85 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootStateProvider.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootStatePreviewParam.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.linknewdevice.ErrorType -open class LinkNewDeviceRootStateProvider : PreviewParameterProvider { +open class LinkNewDeviceRootStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLinkNewDeviceRootState(), diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootView.kt index 5778abfb2c..b27cce17d9 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootView.kt @@ -140,7 +140,7 @@ fun LinkNewDeviceRootView( @PreviewsDayNight @Composable internal fun LinkNewDeviceRootViewPreview( - @PreviewParameter(LinkNewDeviceRootStateProvider::class) state: LinkNewDeviceRootState + @PreviewParameter(LinkNewDeviceRootStatePreviewParam::class) state: LinkNewDeviceRootState ) = ElementPreview { LinkNewDeviceRootView( state = state, diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeStateProvider.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeStatePreviewParam.kt similarity index 91% rename from features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeStateProvider.kt rename to features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeStatePreviewParam.kt index 40e7df8884..a088bf7bd3 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeStateProvider.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.linknewdevice.impl.screens.scan import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction -open class ScanQrCodeStateProvider : PreviewParameterProvider { +open class ScanQrCodeStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aScanQrCodeState(), diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeView.kt index 937aee08b4..4cd30fd904 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeView.kt @@ -166,7 +166,7 @@ private fun ColumnScope.Buttons( @PreviewsDayNight @Composable -internal fun ScanQrCodeViewPreview(@PreviewParameter(ScanQrCodeStateProvider::class) state: ScanQrCodeState) = ElementPreview { +internal fun ScanQrCodeViewPreview(@PreviewParameter(ScanQrCodeStatePreviewParam::class) state: ScanQrCodeState) = ElementPreview { ScanQrCodeView( state = state, onBackClick = {}, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStatePreviewParam.kt similarity index 98% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStatePreviewParam.kt index 6d7ff5e612..0867136651 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStatePreviewParam.kt @@ -22,7 +22,7 @@ import kotlin.time.Duration.Companion.minutes private const val APP_NAME = "ApplicationName" -class ShareLocationStateProvider : PreviewParameterProvider { +class ShareLocationStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aShareLocationState( diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt index 53e7874936..36827cc919 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt @@ -332,7 +332,7 @@ private fun LiveLocationDurationDialog( @PreviewsDayNight @Composable internal fun ShareLocationViewPreview( - @PreviewParameter(ShareLocationStateProvider::class) state: ShareLocationState + @PreviewParameter(ShareLocationStatePreviewParam::class) state: ShareLocationState ) = ElementPreview { ShareLocationView( state = state, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStatePreviewParam.kt similarity index 97% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStatePreviewParam.kt index a1f349d644..7330a9a5e1 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStatePreviewParam.kt @@ -21,7 +21,7 @@ import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.wellknown.api.MapTilerConfig import kotlinx.collections.immutable.toImmutableList -class ShowLocationStateProvider : PreviewParameterProvider { +class ShowLocationStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aShowLocationState(), diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 45e4c7f4cc..26eb1ee27a 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -200,7 +200,7 @@ fun ShowLocationView( @PreviewsDayNight @Composable -internal fun ShowLocationViewPreview(@PreviewParameter(ShowLocationStateProvider::class) state: ShowLocationState) = ElementPreview { +internal fun ShowLocationViewPreview(@PreviewParameter(ShowLocationStatePreviewParam::class) state: ShowLocationState) = ElementPreview { ShowLocationView( state = state, onBackClick = {}, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsStatePreviewParam.kt similarity index 91% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsStateProvider.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsStatePreviewParam.kt index 43f20c1c52..8929138077 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.lockscreen.impl.settings import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class LockScreenSettingsStateProvider : PreviewParameterProvider { +open class LockScreenSettingsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLockScreenSettingsState(), diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt index d8655e29a3..291906caf0 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt @@ -84,7 +84,7 @@ fun LockScreenSettingsView( @PreviewsDayNight @Composable internal fun LockScreenSettingsViewPreview( - @PreviewParameter(LockScreenSettingsStateProvider::class) state: LockScreenSettingsState, + @PreviewParameter(LockScreenSettingsStatePreviewParam::class) state: LockScreenSettingsState, ) { ElementPreview { LockScreenSettingsView( diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricStatePreviewParam.kt similarity index 87% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricStateProvider.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricStatePreviewParam.kt index d725d2de97..6ad44f3273 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.lockscreen.impl.setup.biometric import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class SetupBiometricStateProvider : PreviewParameterProvider { +open class SetupBiometricStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSetupBiometricState(), diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt index 70a1046e36..13fad4e9d0 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt @@ -79,7 +79,7 @@ private fun SetupBiometricFooter( @Composable @PreviewsDayNight -internal fun SetupBiometricViewPreview(@PreviewParameter(SetupBiometricStateProvider::class) state: SetupBiometricState) { +internal fun SetupBiometricViewPreview(@PreviewParameter(SetupBiometricStatePreviewParam::class) state: SetupBiometricState) { ElementPreview { SetupBiometricView( state = state, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinStatePreviewParam.kt similarity index 95% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinStateProvider.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinStatePreviewParam.kt index f50643e367..706d49ce82 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.lockscreen.impl.pin.model.PinEntry import io.element.android.features.lockscreen.impl.setup.pin.validation.SetupPinFailure -open class SetupPinStateProvider : PreviewParameterProvider { +open class SetupPinStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSetupPinState(), diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt index 508d3c1fbb..7b0f86e386 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt @@ -145,7 +145,7 @@ private fun SetupPinFailure.title(): String { @Composable @PreviewsDayNight -internal fun SetupPinViewPreview(@PreviewParameter(SetupPinStateProvider::class) state: SetupPinState) { +internal fun SetupPinViewPreview(@PreviewParameter(SetupPinStatePreviewParam::class) state: SetupPinState) { ElementPreview { SetupPinView( state = state, 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/PinUnlockStatePreviewParam.kt similarity index 94% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStatePreviewParam.kt index aef2c057a2..dcc61e83ed 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/PinUnlockStatePreviewParam.kt @@ -16,7 +16,7 @@ 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 { +open class PinUnlockStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aPinUnlockState(), @@ -42,7 +42,7 @@ open class PinUnlockStateProvider : PreviewParameterProvider { ) } -open class PinUnlockStateCompactProvider : PreviewParameterProvider { +open class PinUnlockStateCompactPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aPinUnlockState(), 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 0292b29a0f..e83b106d88 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 @@ -381,7 +381,7 @@ private fun PinUnlockFooter( @Composable @PreviewsDayNight -internal fun PinUnlockViewInAppPreview(@PreviewParameter(PinUnlockStateProvider::class) state: PinUnlockState) { +internal fun PinUnlockViewInAppPreview(@PreviewParameter(PinUnlockStatePreviewParam::class) state: PinUnlockState) { ElementPreview { PinUnlockView( state = state, @@ -393,7 +393,7 @@ internal fun PinUnlockViewInAppPreview(@PreviewParameter(PinUnlockStateProvider: @Composable @PreviewsDayNight -internal fun PinUnlockViewPreview(@PreviewParameter(PinUnlockStateProvider::class) state: PinUnlockState) { +internal fun PinUnlockViewPreview(@PreviewParameter(PinUnlockStatePreviewParam::class) state: PinUnlockState) { ElementPreview { PinUnlockView( state = state, @@ -405,7 +405,7 @@ internal fun PinUnlockViewPreview(@PreviewParameter(PinUnlockStateProvider::clas @Composable @Preview(heightDp = 480, widthDp = 800) -internal fun PinUnlockViewCompactPreview(@PreviewParameter(PinUnlockStateCompactProvider::class) state: PinUnlockState) { +internal fun PinUnlockViewCompactPreview(@PreviewParameter(PinUnlockStateCompactPreviewParam::class) state: PinUnlockState) { ElementPreview { PinUnlockView( state = state, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderPreviewParam.kt similarity index 93% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderPreviewParam.kt index 745939233f..c14827d1a6 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderPreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.login.impl.accountprovider import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.appconfig.AuthenticationConfig -open class AccountProviderProvider : PreviewParameterProvider { +open class AccountProviderPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAccountProvider(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt index fcf4dac3b1..09b423f713 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt @@ -116,7 +116,7 @@ fun AccountProviderView( @PreviewsDayNight @Composable -internal fun AccountProviderViewPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) = ElementPreview { +internal fun AccountProviderViewPreview(@PreviewParameter(AccountProviderPreviewParam::class) item: AccountProvider) = ElementPreview { AccountProviderView( item = item, onClick = { } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStatePreviewParam.kt similarity index 96% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStatePreviewParam.kt index 183158bceb..5f46d877a2 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStatePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.features.login.impl.error.ChangeServerError import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionDialog -open class ChangeServerStateProvider : PreviewParameterProvider { +open class ChangeServerStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aChangeServerState(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt index 30ae644a70..8e726c7fe1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt @@ -135,7 +135,7 @@ fun ChangeServerView( @PreviewsDayNight @Composable -internal fun ChangeServerViewPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) = ElementPreview { +internal fun ChangeServerViewPreview(@PreviewParameter(ChangeServerStatePreviewParam::class) state: ChangeServerState) = ElementPreview { ChangeServerView( state = state, onLearnMoreClick = {}, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerErrorProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerErrorPreviewParam.kt similarity index 92% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerErrorProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerErrorPreviewParam.kt index 59fb42fe0c..76c8b18409 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerErrorProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerErrorPreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.login.impl.error import androidx.compose.ui.tooling.preview.PreviewParameterProvider -class ChangeServerErrorProvider : PreviewParameterProvider { +class ChangeServerErrorPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( ChangeServerError.InvalidServer, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt index 8b59db1d61..80784d2b13 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt @@ -129,7 +129,7 @@ fun LoginModeView( @PreviewsDayNight @Composable -internal fun LoginModeViewPreview(@PreviewParameter(LoginModeViewErrorProvider::class) error: Throwable) { +internal fun LoginModeViewPreview(@PreviewParameter(LoginModeViewErrorPreviewParam::class) error: Throwable) { ElementPreview { LoginModeView( loginMode = AsyncData.Failure(error), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeViewErrorProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeViewErrorPreviewParam.kt similarity index 80% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeViewErrorProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeViewErrorPreviewParam.kt index 513d0a8a04..4706beefbb 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeViewErrorProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeViewErrorPreviewParam.kt @@ -9,11 +9,11 @@ package io.element.android.features.login.impl.login import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.login.impl.error.ChangeServerErrorProvider +import io.element.android.features.login.impl.error.ChangeServerErrorPreviewParam import io.element.android.libraries.matrix.api.auth.AuthenticationException -class LoginModeViewErrorProvider : PreviewParameterProvider { +class LoginModeViewErrorPreviewParam : PreviewParameterProvider { override val values: Sequence - get() = ChangeServerErrorProvider().values + + get() = ChangeServerErrorPreviewParam().values + AuthenticationException.AccountAlreadyLoggedIn("@alice:matrix.org") } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderStatePreviewParam.kt similarity index 93% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderStatePreviewParam.kt index 5cec1961a4..389531fffb 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.features.login.impl.changeserver.ChangeServerState import io.element.android.features.login.impl.changeserver.aChangeServerState import kotlinx.collections.immutable.toImmutableList -open class ChangeAccountProviderStateProvider : PreviewParameterProvider { +open class ChangeAccountProviderStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aChangeAccountProviderState(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt index 212bab515d..54bd0ea763 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt @@ -118,7 +118,9 @@ fun ChangeAccountProviderView( @PreviewsDayNight @Composable -internal fun ChangeAccountProviderViewPreview(@PreviewParameter(ChangeAccountProviderStateProvider::class) state: ChangeAccountProviderState) = ElementPreview { +internal fun ChangeAccountProviderViewPreview(@PreviewParameter( + ChangeAccountProviderStatePreviewParam::class +) state: ChangeAccountProviderState) = ElementPreview { ChangeAccountProviderView( state = state, onBackClick = { }, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStatePreviewParam.kt similarity index 96% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStatePreviewParam.kt index 3fea548fca..207f434b42 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStatePreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.features.login.impl.login.aLoginModeState import io.element.android.libraries.architecture.AsyncData import kotlinx.collections.immutable.toImmutableList -open class ChooseAccountProviderStateProvider : PreviewParameterProvider { +open class ChooseAccountProviderStatePreviewParam : PreviewParameterProvider { private val server1 = anAccountProvider( url = "https://server1.io", subtitle = null, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt index 0d9d8cfba7..fdc69ca295 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt @@ -152,7 +152,9 @@ fun ChooseAccountProviderView( @PreviewsDayNight @Composable -internal fun ChooseAccountProviderViewPreview(@PreviewParameter(ChooseAccountProviderStateProvider::class) state: ChooseAccountProviderState) = ElementPreview { +internal fun ChooseAccountProviderViewPreview(@PreviewParameter( + ChooseAccountProviderStatePreviewParam::class +) state: ChooseAccountProviderState) = ElementPreview { ChooseAccountProviderView( state = state, onBackClick = { }, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicStatePreviewParam.kt similarity index 94% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicStatePreviewParam.kt index 6dfc292f6f..056d3d704a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.matrix.api.core.UserId -open class LoginWithClassicStateProvider : PreviewParameterProvider { +open class LoginWithClassicStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLoginWithClassicState(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt index 81638b4be0..845c8ec8ee 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt @@ -217,7 +217,7 @@ fun LoginWithClassicView( @PreviewsDayNight @Composable -internal fun LoginWithClassicViewPreview(@PreviewParameter(LoginWithClassicStateProvider::class) state: LoginWithClassicState) = ElementPreview { +internal fun LoginWithClassicViewPreview(@PreviewParameter(LoginWithClassicStatePreviewParam::class) state: LoginWithClassicState) = ElementPreview { LoginWithClassicView( state = state, onOtherOptionsClick = {}, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/missingkeybackup/MissingKeyBackupStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/missingkeybackup/MissingKeyBackupStatePreviewParam.kt similarity index 86% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/missingkeybackup/MissingKeyBackupStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/missingkeybackup/MissingKeyBackupStatePreviewParam.kt index 2c6a09b3ed..08d26bcee1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/missingkeybackup/MissingKeyBackupStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/missingkeybackup/MissingKeyBackupStatePreviewParam.kt @@ -9,7 +9,7 @@ package io.element.android.features.login.impl.screens.classic.missingkeybackup import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class MissingKeyBackupStateProvider : PreviewParameterProvider { +open class MissingKeyBackupStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMissingKeyBackupState(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/missingkeybackup/MissingKeyBackupView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/missingkeybackup/MissingKeyBackupView.kt index c4c9c5f286..9297fc4a51 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/missingkeybackup/MissingKeyBackupView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/missingkeybackup/MissingKeyBackupView.kt @@ -76,7 +76,7 @@ private fun ColumnScope.Buttons( @PreviewsDayNight @Composable -internal fun MissingKeyBackupViewPreview(@PreviewParameter(MissingKeyBackupStateProvider::class) state: MissingKeyBackupState) = ElementPreview { +internal fun MissingKeyBackupViewPreview(@PreviewParameter(MissingKeyBackupStatePreviewParam::class) state: MissingKeyBackupState) = ElementPreview { MissingKeyBackupView( state = state, onBackClick = {}, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStatePreviewParam.kt similarity index 94% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStatePreviewParam.kt index f597ad6b54..ad2628194b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStatePreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.features.login.impl.login.aLoginModeState import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported import io.element.android.libraries.architecture.AsyncData -open class ConfirmAccountProviderStateProvider : PreviewParameterProvider { +open class ConfirmAccountProviderStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aConfirmAccountProviderState(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index 3702b195a1..414a7e30e0 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -123,7 +123,7 @@ fun ConfirmAccountProviderView( @PreviewsDayNight @Composable internal fun ConfirmAccountProviderViewPreview( - @PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState + @PreviewParameter(ConfirmAccountProviderStatePreviewParam::class) state: ConfirmAccountProviderState ) = ElementPreview { ConfirmAccountProviderView( state = state, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStatePreviewParam.kt similarity index 94% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStatePreviewParam.kt index 2183790365..4d7201dbc8 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStatePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.features.login.impl.accountprovider.anAccountProvider import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.SessionId -open class LoginPasswordStateProvider : PreviewParameterProvider { +open class LoginPasswordStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLoginPasswordState(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index 06a1641644..343c770c84 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -291,7 +291,7 @@ private fun LoginErrorDialog(error: Throwable, onDismiss: () -> Unit) { @PreviewsDayNight @Composable -internal fun LoginPasswordViewPreview(@PreviewParameter(LoginPasswordStateProvider::class) state: LoginPasswordState) = ElementPreview { +internal fun LoginPasswordViewPreview(@PreviewParameter(LoginPasswordStatePreviewParam::class) state: LoginPasswordState) = ElementPreview { LoginPasswordView( state = state, onBackClick = {}, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStatePreviewParam.kt similarity index 96% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStatePreviewParam.kt index 41e4a81818..d04f85f2a0 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStatePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.features.login.impl.login.LoginModeState import io.element.android.features.login.impl.login.aLoginModeState import io.element.android.libraries.designsystem.R -open class OnBoardingStateProvider : PreviewParameterProvider { +open class OnBoardingStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anOnBoardingState(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt index 10fe3a854c..78faed7543 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt @@ -351,7 +351,7 @@ private fun OnBoardingButtons( @PreviewsDayNight @Composable internal fun OnBoardingViewPreview( - @PreviewParameter(OnBoardingStateProvider::class) state: OnBoardingState + @PreviewParameter(OnBoardingStatePreviewParam::class) state: OnBoardingState ) = ElementPreview { OnBoardingView( state = state, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepPreviewParam.kt similarity index 87% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepPreviewParam.kt index 17864f68a1..700c6a87b7 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepPreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.login.impl.screens.qrcode.confirmation import androidx.compose.ui.tooling.preview.PreviewParameterProvider -class QrCodeConfirmationStepProvider : PreviewParameterProvider { +class QrCodeConfirmationStepPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( QrCodeConfirmationStep.DisplayCheckCode("12"), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationView.kt index 8de93bcb0f..2c29f4a667 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationView.kt @@ -148,7 +148,7 @@ private fun Buttons( @PreviewsDayNight @Composable -internal fun QrCodeConfirmationViewPreview(@PreviewParameter(QrCodeConfirmationStepProvider::class) step: QrCodeConfirmationStep) { +internal fun QrCodeConfirmationViewPreview(@PreviewParameter(QrCodeConfirmationStepPreviewParam::class) step: QrCodeConfirmationStep) { ElementPreview { QrCodeConfirmationView( step = step, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt index 6b5da8f300..4d58b9329a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt @@ -143,7 +143,7 @@ private fun ColumnScope.Buttons( @PreviewsDayNight @Composable -internal fun QrCodeErrorViewPreview(@PreviewParameter(QrCodeErrorScreenTypeProvider::class) errorScreenType: QrCodeErrorScreenType) { +internal fun QrCodeErrorViewPreview(@PreviewParameter(QrCodeErrorScreenTypePreviewParam::class) errorScreenType: QrCodeErrorScreenType) { ElementPreview { QrCodeErrorView( errorScreenType = errorScreenType, @@ -154,7 +154,7 @@ internal fun QrCodeErrorViewPreview(@PreviewParameter(QrCodeErrorScreenTypeProvi } } -class QrCodeErrorScreenTypeProvider : PreviewParameterProvider { +class QrCodeErrorScreenTypePreviewParam : PreviewParameterProvider { override val values: Sequence = sequenceOf( QrCodeErrorScreenType.Cancelled, QrCodeErrorScreenType.Declined, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroStatePreviewParam.kt similarity index 93% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroStatePreviewParam.kt index 22c7fd67f6..b9c286dc16 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroStatePreviewParam.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.permissions.api.PermissionsState import io.element.android.libraries.permissions.api.aPermissionsState -open class QrCodeIntroStateProvider : PreviewParameterProvider { +open class QrCodeIntroStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aQrCodeIntroState(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroView.kt index 767dc49999..08a40b63c5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroView.kt @@ -98,7 +98,7 @@ private fun ColumnScope.Buttons( @PreviewsDayNight @Composable -internal fun QrCodeIntroViewPreview(@PreviewParameter(QrCodeIntroStateProvider::class) state: QrCodeIntroState) = ElementPreview { +internal fun QrCodeIntroViewPreview(@PreviewParameter(QrCodeIntroStatePreviewParam::class) state: QrCodeIntroState) = ElementPreview { QrCodeIntroView( state = state, onBackClick = {}, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanStatePreviewParam.kt similarity index 96% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanStatePreviewParam.kt index 0d467efb71..19385b6df1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanStatePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.QrLoginException -open class QrCodeScanStateProvider : PreviewParameterProvider { +open class QrCodeScanStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aQrCodeScanState(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanView.kt index 6fa3d1b0c7..72d624a921 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanView.kt @@ -218,7 +218,7 @@ private fun ColumnScope.Buttons( @PreviewsDayNight @Composable -internal fun QrCodeScanViewPreview(@PreviewParameter(QrCodeScanStateProvider::class) state: QrCodeScanState) = ElementPreview { +internal fun QrCodeScanViewPreview(@PreviewParameter(QrCodeScanStatePreviewParam::class) state: QrCodeScanState) = ElementPreview { QrCodeScanView( state = state, onQrCodeDataReady = {}, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStatePreviewParam.kt similarity index 94% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStatePreviewParam.kt index 2a8512c38a..373cb1d972 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStatePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.features.login.impl.changeserver.aChangeServerState import io.element.android.features.login.impl.resolver.HomeserverData import io.element.android.libraries.architecture.AsyncData -open class SearchAccountProviderStateProvider : PreviewParameterProvider { +open class SearchAccountProviderStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSearchAccountProviderState(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt index 8335dde112..7530bd8be1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt @@ -204,7 +204,9 @@ private fun HomeserverData.toAccountProvider(): AccountProvider { @PreviewsDayNight @Composable -internal fun SearchAccountProviderViewPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) = ElementPreview { +internal fun SearchAccountProviderViewPreview(@PreviewParameter( + SearchAccountProviderStatePreviewParam::class +) state: SearchAccountProviderState) = ElementPreview { SearchAccountProviderView( state = state, onBackClick = {}, diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStateProvider.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStatePreviewParam.kt similarity index 92% rename from features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStateProvider.kt rename to features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStatePreviewParam.kt index feaf21ef4a..c208978fd9 100644 --- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStateProvider.kt +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.logout.api.direct import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction -open class DirectLogoutStateProvider : PreviewParameterProvider { +open class DirectLogoutStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aDirectLogoutState(), diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStateProvider.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStatePreviewParam.kt similarity index 97% rename from features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStateProvider.kt rename to features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStatePreviewParam.kt index a3b59860b5..7bd55faa2c 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStateProvider.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.matrix.api.encryption.BackupUploadState import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.encryption.SteadyStateException -open class LogoutStateProvider : PreviewParameterProvider { +open class LogoutStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLogoutState(), diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt index 9700e54f5c..befabe7421 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt @@ -186,7 +186,7 @@ private fun Content( @PreviewsDayNight @Composable internal fun LogoutViewPreview( - @PreviewParameter(LogoutStateProvider::class) state: LogoutState, + @PreviewParameter(LogoutStatePreviewParam::class) state: LogoutState, ) = ElementPreview { LogoutView( state, diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt index 7fb483a447..a8cad6ffba 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import dev.zacsweers.metro.ContributesBinding import io.element.android.features.logout.api.direct.DirectLogoutEvents import io.element.android.features.logout.api.direct.DirectLogoutState -import io.element.android.features.logout.api.direct.DirectLogoutStateProvider +import io.element.android.features.logout.api.direct.DirectLogoutStatePreviewParam import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.features.logout.impl.ui.LogoutActionDialog import io.element.android.libraries.designsystem.preview.ElementPreview @@ -43,7 +43,7 @@ class DefaultDirectLogoutView : DirectLogoutView { @PreviewsDayNight @Composable internal fun DefaultDirectLogoutViewPreview( - @PreviewParameter(DirectLogoutStateProvider::class) state: DirectLogoutState, + @PreviewParameter(DirectLogoutStatePreviewParam::class) state: DirectLogoutState, ) = ElementPreview { DefaultDirectLogoutView().Render(state = state) } diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerStateProvider.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerStatePreviewParam.kt similarity index 93% rename from features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerStateProvider.kt rename to features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerStatePreviewParam.kt index 7b264a3ddb..2cf698dd84 100644 --- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerStateProvider.kt +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerStatePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.designsystem.components.media.WaveFormSample import io.element.android.libraries.textcomposer.model.VoiceMessageState import kotlin.time.Duration.Companion.seconds -open class VoiceMessageComposerStateProvider : PreviewParameterProvider { +open class VoiceMessageComposerStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aVoiceMessageComposerState(voiceMessageState = VoiceMessageState.Recording(duration = 61.seconds, levels = WaveFormSamples.allRangeWaveForm)), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStatePreviewParam.kt similarity index 99% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStatePreviewParam.kt index 7315ace96e..8ab7e64121 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStatePreviewParam.kt @@ -57,7 +57,7 @@ import io.element.android.libraries.textcomposer.model.aTextEditorStateRich import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf -open class MessagesStateProvider : PreviewParameterProvider { +open class MessagesStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMessagesState(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 5911de7372..ee374e005d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -662,7 +662,7 @@ private fun SuccessorRoomBanner( @PreviewsDayNight @Composable -internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class) state: MessagesState) = ElementPreview { +internal fun MessagesViewPreview(@PreviewParameter(MessagesStatePreviewParam::class) state: MessagesState) = ElementPreview { MessagesView( state = state, onBackClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStatePreviewParam.kt similarity index 99% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStatePreviewParam.kt index 56bac5be33..8474b4833d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStatePreviewParam.kt @@ -28,7 +28,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -open class ActionListStateProvider : PreviewParameterProvider { +open class ActionListStatePreviewParam : PreviewParameterProvider { private val suggestedEmojis = persistentListOf("👍️", "👎️", "🔥", "❤️", "👏") override val values: Sequence diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index 669a696111..24a325b641 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -514,7 +514,7 @@ private fun EmojiButton( @PreviewsDayNight @Composable internal fun ActionListViewContentPreview( - @PreviewParameter(ActionListStateProvider::class) state: ActionListState + @PreviewParameter(ActionListStatePreviewParam::class) state: ActionListState ) = ElementPreview { ActionListViewContent( state = state, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStatePreviewParam.kt similarity index 98% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStatePreviewParam.kt index a7d4e14436..cdcc37008d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStatePreviewParam.kt @@ -30,7 +30,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import java.io.File -open class AttachmentsPreviewStateProvider : PreviewParameterProvider { +open class AttachmentsPreviewStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAttachmentsPreviewState(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index 077d086720..86b8a85557 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -553,7 +553,7 @@ private fun AttachmentsPreviewBottomActions( // Only preview in dark, dark theme is forced on the Node. @Preview @Composable -internal fun AttachmentsPreviewViewPreview(@PreviewParameter(AttachmentsPreviewStateProvider::class) state: AttachmentsPreviewState) = ElementPreviewDark { +internal fun AttachmentsPreviewViewPreview(@PreviewParameter(AttachmentsPreviewStatePreviewParam::class) state: AttachmentsPreviewState) = ElementPreviewDark { AttachmentsPreviewView( state = state, localMediaRenderer = SampleMediaRenderer(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStatePreviewParam.kt similarity index 97% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStatePreviewParam.kt index 7c542fd1a8..fb9901e7ec 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.core.net.toUri import io.element.android.libraries.mediaviewer.api.anImageMediaInfo import io.element.android.libraries.mediaviewer.api.local.LocalMedia -open class AttachmentImageEditorStateProvider : PreviewParameterProvider { +open class AttachmentImageEditorStatePreviewParam : PreviewParameterProvider { private val caterpillarCrop = NormalizedCropRect( left = 0.3f, top = 0.3f, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt index a5317b96c6..70a3375fa8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt @@ -676,7 +676,7 @@ private fun DrawScope.drawEdgeHandle( @Preview @Composable internal fun AttachmentImageEditorViewPreview( - @PreviewParameter(AttachmentImageEditorStateProvider::class) state: AttachmentImageEditorState, + @PreviewParameter(AttachmentImageEditorStatePreviewParam::class) state: AttachmentImageEditorState, ) = ElementPreviewDark { AttachmentImageEditorView( state = state, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePreviewParam.kt similarity index 97% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePreviewParam.kt index be53de5f66..bc1d587709 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePreviewParam.kt @@ -18,7 +18,7 @@ import io.element.android.libraries.matrix.ui.room.IdentityRoomMember import io.element.android.libraries.matrix.ui.room.RoomMemberIdentityStateChange import kotlinx.collections.immutable.toImmutableList -class IdentityChangeStateProvider : PreviewParameterProvider { +class IdentityChangeStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anIdentityChangeState(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt index 21684206b5..1c7bb38588 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt @@ -122,7 +122,7 @@ private fun ViolationAlert( @PreviewsDayNight @Composable internal fun IdentityChangeStateViewPreview( - @PreviewParameter(IdentityChangeStateProvider::class) state: IdentityChangeState, + @PreviewParameter(IdentityChangeStatePreviewParam::class) state: IdentityChangeState, ) = ElementPreview { IdentityChangeStateView( state = state, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt index 4e9bf9249a..5e5afb7f60 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt @@ -20,7 +20,7 @@ import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown @PreviewsDayNight @Composable internal fun MessagesViewWithIdentityChangePreview( - @PreviewParameter(IdentityChangeStateProvider::class) identityChangeState: IdentityChangeState + @PreviewParameter(IdentityChangeStatePreviewParam::class) identityChangeState: IdentityChangeState ) = ElementPreview { MessagesView( state = aMessagesState( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureStatePreviewParam.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureStatePreviewParam.kt index ee4b749c8f..624882b509 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureStatePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUser import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE -open class ResolveVerifiedUserSendFailureStateProvider : PreviewParameterProvider { +open class ResolveVerifiedUserSendFailureStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aResolveVerifiedUserSendFailureState(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureView.kt index a430d4621f..2d745df474 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureView.kt @@ -158,7 +158,7 @@ private fun VerifiedUserSendFailure.resolveAction(): String { @PreviewsDayNight @Composable internal fun ResolveVerifiedUserSendFailureViewPreview( - @PreviewParameter(ResolveVerifiedUserSendFailureStateProvider::class) state: ResolveVerifiedUserSendFailureState + @PreviewParameter(ResolveVerifiedUserSendFailureStatePreviewParam::class) state: ResolveVerifiedUserSendFailureState ) = ElementPreview { ResolveVerifiedUserSendFailureView(state) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkStatePreviewParam.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkStatePreviewParam.kt index 2e021ec276..f72d9e2517 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.wysiwyg.link.Link -open class LinkStateProvider : PreviewParameterProvider { +open class LinkStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLinkState(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkView.kt index 69e6ee8673..950765f40f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkView.kt @@ -66,7 +66,7 @@ fun LinkView( @PreviewsDayNight @Composable -internal fun LinkViewPreview(@PreviewParameter(LinkStateProvider::class) state: LinkState) = ElementPreview { +internal fun LinkViewPreview(@PreviewParameter(LinkStatePreviewParam::class) state: LinkState) = ElementPreview { LinkView( state = state, onLinkValid = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStatePreviewParam.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStatePreviewParam.kt index b08fea28ec..31ef5fed50 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStatePreviewParam.kt @@ -18,7 +18,7 @@ import io.element.android.wysiwyg.display.TextDisplay import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -open class MessageComposerStateProvider : PreviewParameterProvider { +open class MessageComposerStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMessageComposerState(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt index cd20f3e58b..58842b6824 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt @@ -20,7 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerEvent import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerState -import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerStateProvider +import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerStatePreviewParam import io.element.android.features.messages.api.timeline.voicemessages.composer.aVoiceMessageComposerState import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.preview.ElementPreview @@ -129,7 +129,7 @@ internal fun MessageComposerView( @PreviewsDayNight @Composable internal fun MessageComposerViewPreview( - @PreviewParameter(MessageComposerStateProvider::class) state: MessageComposerState, + @PreviewParameter(MessageComposerStatePreviewParam::class) state: MessageComposerState, ) = ElementPreview { Column { MessageComposerView( @@ -149,7 +149,7 @@ internal fun MessageComposerViewPreview( @PreviewsDayNight @Composable internal fun MessageComposerViewVoicePreview( - @PreviewParameter(VoiceMessageComposerStateProvider::class) state: VoiceMessageComposerState, + @PreviewParameter(VoiceMessageComposerStatePreviewParam::class) state: VoiceMessageComposerState, ) = ElementPreview { Column { MessageComposerView( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerStatePreviewParam.kt similarity index 96% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerStatePreviewParam.kt index 2a2c30bf6b..37660d0000 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerStatePreviewParam.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.matrix.api.core.EventId import kotlin.random.Random -internal class PinnedMessagesBannerStateProvider : PreviewParameterProvider { +internal class PinnedMessagesBannerStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aHiddenPinnedMessagesBannerState(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerView.kt index bc80d6b014..70a98d4b4e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerView.kt @@ -294,7 +294,9 @@ private class ExitOnScrollBehavior : PinnedMessagesBannerViewScrollBehavior { @PreviewsDayNight @Composable -internal fun PinnedMessagesBannerViewPreview(@PreviewParameter(PinnedMessagesBannerStateProvider::class) state: PinnedMessagesBannerState) = ElementPreview { +internal fun PinnedMessagesBannerViewPreview(@PreviewParameter( + PinnedMessagesBannerStatePreviewParam::class +) state: PinnedMessagesBannerState) = ElementPreview { PinnedMessagesBannerView( state = state, onClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStatePreviewParam.kt similarity index 98% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStatePreviewParam.kt index bfa9820a44..3391f6847f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListStatePreviewParam.kt @@ -30,7 +30,7 @@ import io.element.android.features.messages.impl.timeline.protection.aTimelinePr import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -open class PinnedMessagesListStateProvider : PreviewParameterProvider { +open class PinnedMessagesListStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aFailedPinnedMessagesListState(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt index 4f69bacd28..e13fb1a45e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt @@ -312,7 +312,7 @@ private fun TimelineItemEventContentViewWrapper( @PreviewsDayNight @Composable -internal fun PinnedMessagesListViewPreview(@PreviewParameter(PinnedMessagesListStateProvider::class) state: PinnedMessagesListState) = +internal fun PinnedMessagesListViewPreview(@PreviewParameter(PinnedMessagesListStatePreviewParam::class) state: PinnedMessagesListState) = ElementPreview { PinnedMessagesListView( state = state, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStatePreviewParam.kt similarity index 94% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStatePreviewParam.kt index 0e2b4b0c50..1f24b3f480 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.messages.impl.report import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction -open class ReportMessageStateProvider : PreviewParameterProvider { +open class ReportMessageStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aReportMessageState(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt index dde3f13558..a3cf4f09da 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt @@ -137,7 +137,7 @@ fun ReportMessageView( @PreviewsDayNight @Composable -internal fun ReportMessageViewPreview(@PreviewParameter(ReportMessageStateProvider::class) state: ReportMessageState) = ElementPreview { +internal fun ReportMessageViewPreview(@PreviewParameter(ReportMessageStatePreviewParam::class) state: ReportMessageState) = ElementPreview { ReportMessageView( onBackClick = {}, state = state, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 78c067eed1..5e7fd34723 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -90,7 +90,7 @@ import io.element.android.features.messages.impl.timeline.focus.FocusRequestStat import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentPreviewParam import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState import io.element.android.libraries.androidutils.system.copyToClipboard @@ -582,7 +582,7 @@ private fun JumpToPositionButton( @PreviewsDayNight @Composable internal fun TimelineViewPreview( - @PreviewParameter(TimelineItemEventContentProvider::class) content: TimelineItemEventContent + @PreviewParameter(TimelineItemEventContentPreviewParam::class) content: TimelineItemEventContent ) = ElementPreview { val timelineItems = aTimelineItemList(content) val timelineEvents = timelineItems.filterIsInstance() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt index 28f5011652..d4b9885ea5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.roomcall.api.RoomCallState -import io.element.android.features.roomcall.api.RoomCallStateProvider +import io.element.android.features.roomcall.api.RoomCallStatePreviewParam import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon @@ -133,7 +133,7 @@ private fun OnGoingCallMenuItem( @PreviewsDayNight @Composable internal fun CallMenuItemPreview( - @PreviewParameter(RoomCallStateProvider::class) roomCallState: RoomCallState + @PreviewParameter(RoomCallStatePreviewParam::class) roomCallState: RoomCallState ) = ElementPreview { CallMenuItem( roomCallState = roomCallState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt index 1ebdc34c6c..0dcf489fe3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt @@ -38,7 +38,7 @@ import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState -import io.element.android.features.messages.impl.timeline.model.bubble.BubbleStateProvider +import io.element.android.features.messages.impl.timeline.model.bubble.BubbleStatePreviewParam import io.element.android.libraries.core.extensions.to01 import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.modifiers.onKeyboardContextMenuAction @@ -192,7 +192,7 @@ object MessageEventBubbleDefaults { @PreviewsDayNight @Composable -internal fun MessageEventBubblePreview(@PreviewParameter(BubbleStateProvider::class) state: BubbleState) = ElementPreview { +internal fun MessageEventBubblePreview(@PreviewParameter(BubbleStatePreviewParam::class) state: BubbleState) = ElementPreview { // Due to position offset, surround with a Box Box( modifier = Modifier diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt index dc8e171298..931e0de801 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt @@ -41,7 +41,7 @@ import io.element.android.features.messages.impl.R import io.element.android.features.messages.impl.timeline.a11y.a11yReactionAction import io.element.android.features.messages.impl.timeline.a11y.a11yReactionDetails import io.element.android.features.messages.impl.timeline.model.AggregatedReaction -import io.element.android.features.messages.impl.timeline.model.AggregatedReactionProvider +import io.element.android.features.messages.impl.timeline.model.AggregatedReactionPreviewParam import io.element.android.features.messages.impl.timeline.model.aTimelineItemReactions import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.modifiers.onKeyboardContextMenuAction @@ -199,7 +199,7 @@ private fun ReactionContent( @PreviewsDayNight @Composable -internal fun MessagesReactionButtonPreview(@PreviewParameter(AggregatedReactionProvider::class) reaction: AggregatedReaction) = ElementPreview { +internal fun MessagesReactionButtonPreview(@PreviewParameter(AggregatedReactionPreviewParam::class) reaction: AggregatedReaction) = ElementPreview { MessagesReactionButton( content = MessagesReactionsButtonContent.Reaction(reaction), onClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt index b6af359269..1314e8cefa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt @@ -134,7 +134,9 @@ fun TimelineEventTimestampView( @PreviewsDayNight @Composable -internal fun TimelineEventTimestampViewPreview(@PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event) = ElementPreview { +internal fun TimelineEventTimestampViewPreview(@PreviewParameter( + TimelineItemEventForTimestampViewPreviewParam::class +) event: TimelineItem.Event) = ElementPreview { TimelineEventTimestampView( event = event, eventSink = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventContentForGalleryViewProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventContentForGalleryViewPreviewParam.kt similarity index 98% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventContentForGalleryViewProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventContentForGalleryViewPreviewParam.kt index ed790da8a2..0f7da4209f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventContentForGalleryViewProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventContentForGalleryViewPreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.model.event.anAttachmentItem import kotlin.time.Duration -class TimelineItemEventContentForGalleryViewProvider : +class TimelineItemEventContentForGalleryViewPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewPreviewParam.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewPreviewParam.kt index 3689c84875..118edae373 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewPreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield -class TimelineItemEventForTimestampViewProvider : PreviewParameterProvider { +class TimelineItemEventForTimestampViewPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemEvent(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowDisambiguatedPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowDisambiguatedPreview.kt index 4b6223fd97..94d29d2191 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowDisambiguatedPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowDisambiguatedPreview.kt @@ -13,12 +13,12 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails -import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsDisambiguatedProvider +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsDisambiguatedPreviewParam @PreviewsDayNight @Composable internal fun TimelineItemEventRowDisambiguatedPreview( - @PreviewParameter(InReplyToDetailsDisambiguatedProvider::class) inReplyToDetails: InReplyToDetails, + @PreviewParameter(InReplyToDetailsDisambiguatedPreviewParam::class) inReplyToDetails: InReplyToDetails, ) = ElementPreview { TimelineItemEventRowWithReplyContentToPreview( inReplyToDetails = inReplyToDetails, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowGalleryPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowGalleryPreview.kt index 22c3f354f2..b942695470 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowGalleryPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowGalleryPreview.kt @@ -19,7 +19,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight @PreviewsDayNight @Composable internal fun TimelineItemEventRowWithGalleryPreview( - @PreviewParameter(TimelineItemEventContentForGalleryViewProvider::class) content: TimelineItemEventContent, + @PreviewParameter(TimelineItemEventContentForGalleryViewPreviewParam::class) content: TimelineItemEventContent, ) = ElementPreview { Column { sequenceOf(false, true).forEach { isMine -> diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt index c588b1d071..ae0077786b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt @@ -20,7 +20,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight @PreviewsDayNight @Composable internal fun TimelineItemEventRowTimestampPreview( - @PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event + @PreviewParameter(TimelineItemEventForTimestampViewPreviewParam::class) event: TimelineItem.Event ) = ElementPreview { Column { when (event.content) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt index 6ea1161605..379a5b3547 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt @@ -14,7 +14,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.aTimelineItemReactions import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState -import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewStateForTimelineItemEventRowProvider +import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewStateForTimelineItemEventRowPreviewParam import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.libraries.designsystem.preview.ElementPreview @@ -25,7 +25,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight @PreviewsDayNight @Composable internal fun TimelineItemEventRowWithRRPreview( - @PreviewParameter(ReadReceiptViewStateForTimelineItemEventRowProvider::class) state: ReadReceiptViewState, + @PreviewParameter(ReadReceiptViewStateForTimelineItemEventRowPreviewParam::class) state: ReadReceiptViewState, ) = ElementPreview { Column { // A message from someone else diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyInformativePreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyInformativePreview.kt index cdc60a9e7e..374561e553 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyInformativePreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyInformativePreview.kt @@ -13,12 +13,12 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails -import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsInformativeProvider +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsInformativePreviewParam @PreviewsDayNight @Composable internal fun TimelineItemEventRowWithReplyInformativePreview( - @PreviewParameter(InReplyToDetailsInformativeProvider::class) inReplyToDetails: InReplyToDetails, + @PreviewParameter(InReplyToDetailsInformativePreviewParam::class) inReplyToDetails: InReplyToDetails, ) = ElementPreview { TimelineItemEventRowWithReplyContentToPreview(inReplyToDetails) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyOtherPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyOtherPreview.kt index 3071fe114a..f7d2de2ced 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyOtherPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyOtherPreview.kt @@ -13,12 +13,12 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails -import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsOtherProvider +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsOtherPreviewParam @PreviewsDayNight @Composable internal fun TimelineItemEventRowWithReplyOtherPreview( - @PreviewParameter(InReplyToDetailsOtherProvider::class) inReplyToDetails: InReplyToDetails, + @PreviewParameter(InReplyToDetailsOtherPreviewParam::class) inReplyToDetails: InReplyToDetails, ) = ElementPreview { TimelineItemEventRowWithReplyContentToPreview(inReplyToDetails) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt index 7ddcadfe3c..aba6971f1d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt @@ -21,12 +21,12 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails -import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsProvider +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsPreviewParam @PreviewsDayNight @Composable internal fun TimelineItemEventRowWithReplyPreview( - @PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails, + @PreviewParameter(InReplyToDetailsPreviewParam::class) inReplyToDetails: InReplyToDetails, ) = ElementPreview { TimelineItemEventRowWithReplyContentToPreview(inReplyToDetails) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsContentPreviewParam.kt similarity index 96% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsContentPreviewParam.kt index c63d68e062..a42eec8504 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsContentPreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.model.event.anAttachmentItem import io.element.android.libraries.matrix.api.media.MediaSource -class TimelineItemAttachmentsContentProvider : PreviewParameterProvider { +class TimelineItemAttachmentsContentPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemAttachmentsContent( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsListView.kt index f3745b2c5a..232c107698 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsListView.kt @@ -265,7 +265,7 @@ private fun AttachmentListItem( @PreviewsDayNight @Composable internal fun TimelineItemAttachmentsListViewPreview( - @PreviewParameter(TimelineItemAttachmentsContentProvider::class) content: TimelineItemAttachmentsContent + @PreviewParameter(TimelineItemAttachmentsContentPreviewParam::class) content: TimelineItemAttachmentsContent ) = ElementPreview { TimelineItemAttachmentsListView( eventId = EventId("\$eventId"), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt index 221ba2ca6f..d47ca552b8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt @@ -14,7 +14,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContentProvider +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContentPreviewParam import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.ui.media.contentvalidation.ContentValidationValue @@ -38,7 +38,7 @@ fun TimelineItemAudioView( @PreviewsDayNight @Composable -internal fun TimelineItemAudioViewPreview(@PreviewParameter(TimelineItemAudioContentProvider::class) content: TimelineItemAudioContent) = +internal fun TimelineItemAudioViewPreview(@PreviewParameter(TimelineItemAudioContentPreviewParam::class) content: TimelineItemAudioContent) = ElementTimelineItemPreview { TimelineItemAudioView( content, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt index 48b3acfbb8..6b9022d3cf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt @@ -14,7 +14,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContentProvider +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContentPreviewParam import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -75,7 +75,7 @@ fun TimelineItemEncryptedView( @PreviewsDayNight @Composable internal fun TimelineItemEncryptedViewPreview( - @PreviewParameter(TimelineItemEncryptedContentProvider::class) content: TimelineItemEncryptedContent + @PreviewParameter(TimelineItemEncryptedContentPreviewParam::class) content: TimelineItemEncryptedContent ) = ElementPreview { TimelineItemEncryptedView( content = content, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt index cd0632282e..77df9ba923 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContentProvider +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContentPreviewParam import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.ui.media.contentvalidation.ContentValidationValue import io.element.android.libraries.ui.strings.CommonStrings @@ -43,7 +43,7 @@ fun TimelineItemFileView( @PreviewsDayNight @Composable -internal fun TimelineItemFileViewPreview(@PreviewParameter(TimelineItemFileContentProvider::class) content: TimelineItemFileContent) { +internal fun TimelineItemFileViewPreview(@PreviewParameter(TimelineItemFileContentPreviewParam::class) content: TimelineItemFileContent) { ElementTimelineItemPreview { TimelineItemFileView( content, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemGalleryContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemGalleryContentPreviewParam.kt similarity index 97% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemGalleryContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemGalleryContentPreviewParam.kt index 9d88d8c0fb..9fb34afaea 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemGalleryContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemGalleryContentPreviewParam.kt @@ -15,7 +15,7 @@ import kotlinx.collections.immutable.toImmutableList import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds -class TimelineItemGalleryContentProvider : PreviewParameterProvider { +class TimelineItemGalleryContentPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemGalleryContent( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemGalleryView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemGalleryView.kt index 3212d789f9..e433f6ef8e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemGalleryView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemGalleryView.kt @@ -390,7 +390,7 @@ private fun RemainingCountOverlay(count: Int) { @PreviewsDayNight @Composable internal fun TimelineItemGalleryViewPreview( - @PreviewParameter(TimelineItemGalleryContentProvider::class) content: TimelineItemGalleryContent, + @PreviewParameter(TimelineItemGalleryContentPreviewParam::class) content: TimelineItemGalleryContent, ) = ElementPreview { TimelineItemGalleryView( eventId = null, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt index 3e79fd6fd9..ce12690534 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt @@ -33,7 +33,7 @@ import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.components.ATimelineItemEventRow import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContentProvider +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContentPreviewParam import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent import io.element.android.features.messages.impl.timeline.protection.ProtectedView import io.element.android.features.messages.impl.timeline.protection.coerceRatioWhenHidingContent @@ -125,7 +125,7 @@ fun TimelineItemImageView( @PreviewsDayNight @Composable -internal fun TimelineItemImageViewPreview(@PreviewParameter(TimelineItemImageContentProvider::class) content: TimelineItemImageContent) = ElementPreview { +internal fun TimelineItemImageViewPreview(@PreviewParameter(TimelineItemImageContentPreviewParam::class) content: TimelineItemImageContent) = ElementPreview { TimelineItemImageView( content = content, hideMediaContent = false, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt index a7f7b33989..3ff8a62455 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt @@ -31,7 +31,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.location.api.LocalMapTilerConfig import io.element.android.features.location.api.StaticMapView import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContentProvider +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContentPreviewParam import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator @@ -162,7 +162,7 @@ private fun LiveLocationOverlay( @PreviewsDayNight @Composable -internal fun TimelineItemLocationViewPreview(@PreviewParameter(TimelineItemLocationContentProvider::class) content: TimelineItemLocationContent) = +internal fun TimelineItemLocationViewPreview(@PreviewParameter(TimelineItemLocationContentPreviewParam::class) content: TimelineItemLocationContent) = ElementPreview { TimelineItemLocationView( content = content, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt index e956a99b45..b6e4d12517 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.messages.impl.timeline.TimelineEvent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContentProvider +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContentPreviewParam import io.element.android.features.poll.api.pollcontent.PollContentView import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -55,7 +55,7 @@ fun TimelineItemPollView( @PreviewsDayNight @Composable -internal fun TimelineItemPollViewPreview(@PreviewParameter(TimelineItemPollContentProvider::class) content: TimelineItemPollContent) = +internal fun TimelineItemPollViewPreview(@PreviewParameter(TimelineItemPollContentPreviewParam::class) content: TimelineItemPollContent) = ElementPreview { TimelineItemPollView( content = content, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt index 7e240f8b51..c7878c00f5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt @@ -28,7 +28,7 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.PreviewParameter import coil3.compose.AsyncImage import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContentProvider +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContentPreviewParam import io.element.android.features.messages.impl.timeline.protection.ProtectedView import io.element.android.features.messages.impl.timeline.protection.coerceRatioWhenHidingContent import io.element.android.features.messages.impl.timeline.util.handleAsyncImageStateChange @@ -123,7 +123,9 @@ fun TimelineItemStickerView( @PreviewsDayNight @Composable -internal fun TimelineItemStickerViewPreview(@PreviewParameter(TimelineItemStickerContentProvider::class) content: TimelineItemStickerContent) = ElementPreview { +internal fun TimelineItemStickerViewPreview(@PreviewParameter( + TimelineItemStickerContentPreviewParam::class +) content: TimelineItemStickerContent) = ElementPreview { TimelineItemStickerView( content = content, hideMediaContent = false, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt index 1a4be5d2e5..a7d54e819b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt @@ -24,7 +24,7 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayout import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContentProvider +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContentPreviewParam import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.features.messages.impl.utils.containsOnlyEmojis import io.element.android.libraries.androidutils.text.LinkifyHelper @@ -82,7 +82,7 @@ internal fun getTextWithResolvedMentions(content: TimelineItemTextBasedContent): @PreviewsDayNight @Composable internal fun TimelineItemTextViewPreview( - @PreviewParameter(TimelineItemTextBasedContentProvider::class) content: TimelineItemTextBasedContent + @PreviewParameter(TimelineItemTextBasedContentPreviewParam::class) content: TimelineItemTextBasedContent ) = ElementPreview { TimelineItemTextView( content = content, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index b47220fa5e..5b3803713e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -37,7 +37,7 @@ import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContentProvider +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContentPreviewParam import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVideoContent import io.element.android.features.messages.impl.timeline.protection.ProtectedView import io.element.android.features.messages.impl.timeline.protection.coerceRatioWhenHidingContent @@ -150,7 +150,7 @@ fun TimelineItemVideoView( @PreviewsDayNight @Composable -internal fun TimelineItemVideoViewPreview(@PreviewParameter(TimelineItemVideoContentProvider::class) content: TimelineItemVideoContent) = ElementPreview { +internal fun TimelineItemVideoViewPreview(@PreviewParameter(TimelineItemVideoContentPreviewParam::class) content: TimelineItemVideoContent) = ElementPreview { TimelineItemVideoView( content = content, hideMediaContent = false, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt index 15387f51c0..a05ac78f84 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt @@ -42,7 +42,7 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContentProvider +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContentPreviewParam import io.element.android.libraries.designsystem.atomic.atoms.PlaybackSpeedButton import io.element.android.libraries.designsystem.components.media.WaveformPlaybackView import io.element.android.libraries.designsystem.preview.ElementPreview @@ -56,7 +56,7 @@ import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.libraries.voiceplayer.api.VoiceMessageEvent import io.element.android.libraries.voiceplayer.api.VoiceMessageState -import io.element.android.libraries.voiceplayer.api.VoiceMessageStateProvider +import io.element.android.libraries.voiceplayer.api.VoiceMessageStatePreviewParam import kotlinx.coroutines.delay @Composable @@ -261,9 +261,9 @@ private fun CustomIconButton( ) } -open class TimelineItemVoiceViewParametersProvider : PreviewParameterProvider { - private val voiceMessageStateProvider = VoiceMessageStateProvider() - private val timelineItemVoiceContentProvider = TimelineItemVoiceContentProvider() +open class TimelineItemVoiceViewParametersPreviewParam : PreviewParameterProvider { + private val voiceMessageStateProvider = VoiceMessageStatePreviewParam() + private val timelineItemVoiceContentProvider = TimelineItemVoiceContentPreviewParam() override val values: Sequence get() = timelineItemVoiceContentProvider.values.flatMap { content -> voiceMessageStateProvider.values.map { state -> @@ -283,7 +283,7 @@ data class TimelineItemVoiceViewParameters( @PreviewsDayNight @Composable internal fun TimelineItemVoiceViewPreview( - @PreviewParameter(TimelineItemVoiceViewParametersProvider::class) timelineItemVoiceViewParameters: TimelineItemVoiceViewParameters, + @PreviewParameter(TimelineItemVoiceViewParametersPreviewParam::class) timelineItemVoiceViewParameters: TimelineItemVoiceViewParameters, ) = ElementPreview { TimelineItemVoiceView( state = timelineItemVoiceViewParameters.state, @@ -296,7 +296,7 @@ internal fun TimelineItemVoiceViewPreview( @PreviewsDayNight @Composable internal fun TimelineItemVoiceViewUnifiedPreview() = ElementPreview { - val timelineItemVoiceViewParametersProvider = TimelineItemVoiceViewParametersProvider() + val timelineItemVoiceViewParametersProvider = TimelineItemVoiceViewParametersPreviewParam() Column { timelineItemVoiceViewParametersProvider.values.forEach { TimelineItemVoiceView( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStatePreviewParam.kt similarity index 91% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStatePreviewParam.kt index 82974c8908..a617394723 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.messages.impl.timeline.aTimelineItemReactions import io.element.android.libraries.matrix.api.core.EventId -open class ReactionSummaryStateProvider : PreviewParameterProvider { +open class ReactionSummaryStatePreviewParam : PreviewParameterProvider { override val values = sequenceOf(aReactionSummaryState()) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt index 2a902fd692..5b6c7fda37 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt @@ -293,7 +293,7 @@ private fun SenderRow( @PreviewsDayNight @Composable internal fun ReactionSummaryViewContentPreview( - @PreviewParameter(ReactionSummaryStateProvider::class) state: ReactionSummaryState + @PreviewParameter(ReactionSummaryStatePreviewParam::class) state: ReactionSummaryState ) = ElementPreview { ReactionSummaryViewContent(summary = state.target as ReactionSummaryState.Summary) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowPreviewParam.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowPreviewParam.kt index c5140c4c03..a3556a9b03 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowPreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState -class ReadReceiptViewStateForTimelineItemEventRowProvider : +class ReadReceiptViewStateForTimelineItemEventRowPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStatePreviewParam.kt similarity index 96% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStatePreviewParam.kt index 64a7cf34ff..751a88cb39 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStatePreviewParam.kt @@ -17,7 +17,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import kotlinx.collections.immutable.toImmutableList -class ReadReceiptViewStateProvider : PreviewParameterProvider { +class ReadReceiptViewStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aReadReceiptViewState(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt index c9b712eba7..17a9b0c5eb 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt @@ -202,7 +202,7 @@ private fun computeReceiptDescription(receipts: ImmutableList): @PreviewsDayNight @Composable internal fun TimelineItemReadReceiptViewPreview( - @PreviewParameter(ReadReceiptViewStateProvider::class) state: ReadReceiptViewState, + @PreviewParameter(ReadReceiptViewStatePreviewParam::class) state: ReadReceiptViewState, ) = ElementPreview { TimelineItemReadReceiptView( state = state, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt index 407cd29026..ad30a3765e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt @@ -117,7 +117,7 @@ private fun ReadReceiptBottomSheetContent( @PreviewsDayNight @Composable -internal fun ReadReceiptBottomSheetPreview(@PreviewParameter(ReadReceiptBottomSheetStateProvider::class) state: ReadReceiptBottomSheetState) { +internal fun ReadReceiptBottomSheetPreview(@PreviewParameter(ReadReceiptBottomSheetStatePreviewParam::class) state: ReadReceiptBottomSheetState) { ElementPreview(fillMaxSize = true) { ReadReceiptBottomSheet( state = state, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStatePreviewParam.kt similarity index 87% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStatePreviewParam.kt index ff297a4f8d..86c2beb707 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStatePreviewParam.kt @@ -10,14 +10,14 @@ package io.element.android.features.messages.impl.timeline.components.receipt.bo import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.messages.impl.timeline.aTimelineItemEvent -import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewStateProvider +import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewStatePreviewParam import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import kotlinx.collections.immutable.toImmutableList -class ReadReceiptBottomSheetStateProvider : PreviewParameterProvider { - // Reuse the provider ReadReceiptViewStateProvider - private val readReceiptViewStateProvider = ReadReceiptViewStateProvider() +class ReadReceiptBottomSheetStatePreviewParam : PreviewParameterProvider { + // Reuse the provider ReadReceiptViewStatePreviewParam + private val readReceiptViewStateProvider = ReadReceiptViewStatePreviewParam() override val values: Sequence = readReceiptViewStateProvider.values .filter { it.sendState is LocalEventSendState.Sent } .map { readReceiptViewState -> diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt index 30befea07d..f2a62aa4a4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModel -import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModelProvider +import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModelPreviewParam import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Text @@ -53,7 +53,7 @@ internal fun TimelineItemDaySeparatorView( @PreviewsDayNight @Composable internal fun TimelineItemDaySeparatorViewPreview( - @PreviewParameter(TimelineItemDaySeparatorModelProvider::class) model: TimelineItemDaySeparatorModel + @PreviewParameter(TimelineItemDaySeparatorModelPreviewParam::class) model: TimelineItemDaySeparatorModel ) = ElementPreview { TimelineItemDaySeparatorView( model = model, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStatePreviewParam.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStatePreviewParam.kt index 0a6fb70cc5..c28e9c4d92 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStatePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.features.messages.impl.timeline.FocusRequestState import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.room.errors.FocusEventException -open class FocusRequestStateProvider : PreviewParameterProvider { +open class FocusRequestStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( FocusRequestState.Loading( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt index 3dff7ac4f7..2bda2fe4bf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt @@ -55,7 +55,7 @@ fun FocusRequestStateView( @PreviewsDayNight @Composable internal fun FocusRequestStateViewPreview( - @PreviewParameter(FocusRequestStateProvider::class) state: FocusRequestState, + @PreviewParameter(FocusRequestStatePreviewParam::class) state: FocusRequestState, ) = ElementPreview { FocusRequestStateView( focusRequestState = state, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionPreviewParam.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionPreviewParam.kt index e5eb52c91e..f7ede5ebd7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionPreviewParam.kt @@ -15,7 +15,7 @@ import java.text.DateFormat import java.util.Date import java.util.TimeZone -open class AggregatedReactionProvider : PreviewParameterProvider { +open class AggregatedReactionPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf(false, true).flatMap { sequenceOf( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactionsProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactionsProvider.kt index 3190e61a75..35d4f21dda 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactionsProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactionsProvider.kt @@ -11,6 +11,6 @@ package io.element.android.features.messages.impl.timeline.model import kotlinx.collections.immutable.toImmutableList fun aTimelineItemReactions() = TimelineItemReactions( - // Use values from AggregatedReactionProvider - reactions = AggregatedReactionProvider().values.toImmutableList() + // Use values from AggregatedReactionPreviewParam + reactions = AggregatedReactionPreviewParam().values.toImmutableList() ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStatePreviewParam.kt similarity index 94% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStatePreviewParam.kt index cdd24ed264..4a7a9f8187 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStatePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition -open class BubbleStateProvider : PreviewParameterProvider { +open class BubbleStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( TimelineItemGroupPosition.First, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentPreviewParam.kt similarity index 92% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentPreviewParam.kt index 3b60a48c78..600288012b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentPreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MediaSource import kotlin.time.Duration.Companion.milliseconds -open class TimelineItemAudioContentProvider : PreviewParameterProvider { +open class TimelineItemAudioContentPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemAudioContent("A sound.mp3"), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEncryptedContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEncryptedContentPreviewParam.kt similarity index 96% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEncryptedContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEncryptedContentPreviewParam.kt index bb53da3b7b..a85553360a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEncryptedContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEncryptedContentPreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent import io.element.android.libraries.matrix.api.timeline.item.event.UtdCause -open class TimelineItemEncryptedContentProvider : PreviewParameterProvider { +open class TimelineItemEncryptedContentPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemEncryptedContent(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentPreviewParam.kt similarity index 96% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentPreviewParam.kt index 1d4c7c04ec..0dd91fccfe 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentPreviewParam.kt @@ -18,7 +18,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecry import kotlinx.collections.immutable.toImmutableList import org.jsoup.nodes.Document -class TimelineItemEventContentProvider : PreviewParameterProvider { +class TimelineItemEventContentPreviewParam : PreviewParameterProvider { override val values = sequenceOf( aTimelineItemEmoteContent(), aTimelineItemEncryptedContent(), @@ -45,7 +45,7 @@ class TimelineItemEventContentProvider : PreviewParameterProvider { +class TimelineItemTextBasedContentPreviewParam : PreviewParameterProvider { private fun buildSpanned(text: String) = buildSpannedString { inSpans(StyleSpan(Typeface.BOLD)) { append("Rich Text") diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContentPreviewParam.kt similarity index 92% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContentPreviewParam.kt index e2021e9646..277f461edc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContentPreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MediaSource -open class TimelineItemFileContentProvider : PreviewParameterProvider { +open class TimelineItemFileContentPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemFileContent(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentPreviewParam.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentPreviewParam.kt index 6d3b114428..cd93c7c006 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentPreviewParam.kt @@ -13,7 +13,7 @@ import androidx.media3.common.MimeTypes import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH -open class TimelineItemImageContentProvider : PreviewParameterProvider { +open class TimelineItemImageContentPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemImageContent(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContentPreviewParam.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContentPreviewParam.kt index 6c095c828e..e4f2691f27 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContentPreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails import io.element.android.libraries.matrix.ui.messages.reply.aProfileDetailsReady -open class TimelineItemLocationContentProvider : PreviewParameterProvider { +open class TimelineItemLocationContentPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemLocationContent( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentPreviewParam.kt similarity index 94% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentPreviewParam.kt index 26577f5230..2cabada8b9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentPreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.features.poll.api.pollcontent.aPollQuestion import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.poll.PollKind -open class TimelineItemPollContentProvider : PreviewParameterProvider { +open class TimelineItemPollContentPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemPollContent(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStickerContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStickerContentPreviewParam.kt similarity index 92% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStickerContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStickerContentPreviewParam.kt index eb564cd803..def748d97b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStickerContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStickerContentPreviewParam.kt @@ -13,7 +13,7 @@ import androidx.media3.common.MimeTypes import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH -open class TimelineItemStickerContentProvider : PreviewParameterProvider { +open class TimelineItemStickerContentPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemStickerContent(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentPreviewParam.kt similarity index 94% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentPreviewParam.kt index 627dbf3dd7..46de9ea89a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentPreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH import kotlin.time.Duration.Companion.milliseconds -open class TimelineItemVideoContentProvider : PreviewParameterProvider { +open class TimelineItemVideoContentPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemVideoContent(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentPreviewParam.kt similarity index 95% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentPreviewParam.kt index d596963ebc..9b9a044791 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentPreviewParam.kt @@ -17,7 +17,7 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.minutes -open class TimelineItemVoiceContentProvider : PreviewParameterProvider { +open class TimelineItemVoiceContentPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTimelineItemVoiceContent( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemDaySeparatorModelProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemDaySeparatorModelPreviewParam.kt similarity index 85% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemDaySeparatorModelProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemDaySeparatorModelPreviewParam.kt index d31d35d62f..2fb4e7ac63 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemDaySeparatorModelProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemDaySeparatorModelPreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.messages.impl.timeline.model.virtual import androidx.compose.ui.tooling.preview.PreviewParameterProvider -class TimelineItemDaySeparatorModelProvider : PreviewParameterProvider { +class TimelineItemDaySeparatorModelPreviewParam : PreviewParameterProvider { override val values = sequenceOf( aTimelineItemDaySeparatorModel("Today"), aTimelineItemDaySeparatorModel("March 6, 2023") diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/AspectRatioProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/AspectRatioPreviewParam.kt similarity index 88% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/AspectRatioProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/AspectRatioPreviewParam.kt index 0ebd63439a..43118d96fd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/AspectRatioProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/AspectRatioPreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.messages.impl.timeline.protection import androidx.compose.ui.tooling.preview.PreviewParameterProvider -class AspectRatioProvider : PreviewParameterProvider { +class AspectRatioPreviewParam : PreviewParameterProvider { override val values: Sequence = sequenceOf( null, 0.05f, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/ProtectedView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/ProtectedView.kt index 2dbe47e9e4..f8a0b7ac89 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/ProtectedView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/ProtectedView.kt @@ -83,7 +83,7 @@ fun ProtectedView( @PreviewsDayNight @Composable internal fun ProtectedViewPreview( - @PreviewParameter(AspectRatioProvider::class) aspectRatio: Float?, + @PreviewParameter(AspectRatioPreviewParam::class) aspectRatio: Float?, ) = ElementPreview { TimelineItemAspectRatioBox( modifier = Modifier.blurHashBackground(A_BLUR_HASH, alpha = 0.9f), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStatePreviewParam.kt similarity index 97% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStatePreviewParam.kt index e298b3af26..b98262a2a8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStatePreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.designsystem.preview.USER_NAME_DAVID import io.element.android.libraries.designsystem.preview.USER_NAME_EVE import kotlinx.collections.immutable.toImmutableList -class TypingNotificationStateProvider : PreviewParameterProvider { +class TypingNotificationStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTypingNotificationState(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt index c8e5722b19..3d2227c6d6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt @@ -141,7 +141,7 @@ private fun computeTypingNotificationText(typingMembers: ImmutableList { +internal class MigrationStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMigrationState(), diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationView.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationView.kt index a294c32534..c7f4dd678a 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationView.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationView.kt @@ -49,7 +49,7 @@ fun MigrationView( @PreviewsDayNight @Composable internal fun MigrationViewPreview( - @PreviewParameter(MigrationStateProvider::class) state: MigrationState, + @PreviewParameter(MigrationStatePreviewParam::class) state: MigrationState, ) = ElementPreview { MigrationView( migrationState = state, diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStatePreviewParam.kt similarity index 98% rename from features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt rename to features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStatePreviewParam.kt index 20d9f0d557..9c7a505421 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.matrix.api.poll.PollKind import kotlinx.collections.immutable.toImmutableList -class CreatePollStateProvider : PreviewParameterProvider { +class CreatePollStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aCreatePollState( diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt index 6163fe19ad..a20c1f5af1 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt @@ -232,7 +232,7 @@ private fun CreatePollTopAppBar( @PreviewsDayNight @Composable internal fun CreatePollViewPreview( - @PreviewParameter(CreatePollStateProvider::class) state: CreatePollState + @PreviewParameter(CreatePollStatePreviewParam::class) state: CreatePollState ) = ElementPreview { CreatePollView( state = state, diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStateProvider.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStatePreviewParam.kt similarity index 96% rename from features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStateProvider.kt rename to features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStatePreviewParam.kt index a08550f2a1..ff07eaee37 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStateProvider.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStatePreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.features.poll.impl.history.model.PollHistoryItem import io.element.android.features.poll.impl.history.model.PollHistoryItems import kotlinx.collections.immutable.toImmutableList -class PollHistoryStateProvider : PreviewParameterProvider { +class PollHistoryStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aPollHistoryState(), diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryView.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryView.kt index 9a654c2abb..01b475593d 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryView.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryView.kt @@ -255,7 +255,7 @@ private fun PollHistoryItemRow( @PreviewsDayNight @Composable internal fun PollHistoryViewPreview( - @PreviewParameter(PollHistoryStateProvider::class) state: PollHistoryState + @PreviewParameter(PollHistoryStatePreviewParam::class) state: PollHistoryState ) = ElementPreview { PollHistoryView( state = state, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutStatePreviewParam.kt similarity index 90% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutStatePreviewParam.kt index 561fe53a49..a15a457302 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.preferences.impl.about import androidx.compose.ui.tooling.preview.PreviewParameterProvider import kotlinx.collections.immutable.toImmutableList -open class AboutStateProvider : PreviewParameterProvider { +open class AboutStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAboutState(), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt index 3a6671f692..79a17b3e5a 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt @@ -51,7 +51,7 @@ fun AboutView( @PreviewsDayNight @Composable -internal fun AboutViewPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) = ElementPreview { +internal fun AboutViewPreview(@PreviewParameter(AboutStatePreviewParam::class) state: AboutState) = ElementPreview { AboutView( state = state, onElementLegalClick = {}, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStatePreviewParam.kt similarity index 96% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStatePreviewParam.kt index 2df59e3ced..c41df427cc 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.preferences.api.store.VideoCompressionPreset import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList -open class AdvancedSettingsStateProvider : PreviewParameterProvider { +open class AdvancedSettingsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aAdvancedSettingsState(), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt index 438b20049a..de6ce5c9f6 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt @@ -410,17 +410,17 @@ private fun LiveLocationUpdatesSection( @PreviewWithLargeHeight @Composable -internal fun AdvancedSettingsViewLightPreview(@PreviewParameter(AdvancedSettingsStateProvider::class) state: AdvancedSettingsState) = +internal fun AdvancedSettingsViewLightPreview(@PreviewParameter(AdvancedSettingsStatePreviewParam::class) state: AdvancedSettingsState) = ElementPreviewLight { ContentToPreview(state) } @PreviewWithLargeHeight @Composable -internal fun AdvancedSettingsViewDarkPreview(@PreviewParameter(AdvancedSettingsStateProvider::class) state: AdvancedSettingsState) = +internal fun AdvancedSettingsViewDarkPreview(@PreviewParameter(AdvancedSettingsStatePreviewParam::class) state: AdvancedSettingsState) = ElementPreviewDark { ContentToPreview(state) } @PreviewWithLargeHeight @Composable -internal fun AdvancedSettingsViewBlackPreview(@PreviewParameter(AdvancedSettingsStateProvider::class) state: AdvancedSettingsState) = +internal fun AdvancedSettingsViewBlackPreview(@PreviewParameter(AdvancedSettingsStatePreviewParam::class) state: AdvancedSettingsState) = ElementPreviewBlack { ContentToPreview(state) } @ExcludeFromCoverage diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsStatePreviewParam.kt similarity index 87% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsStatePreviewParam.kt index 9e15ea2f35..ab7a8aae3d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.preferences.impl.analytics import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.analytics.api.preferences.aAnalyticsPreferencesState -open class AnalyticsSettingsStateProvider : PreviewParameterProvider { +open class AnalyticsSettingsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aAnalyticsSettingsState(), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt index ef84600e2e..61fc3216f2 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt @@ -37,7 +37,7 @@ fun AnalyticsSettingsView( @PreviewsDayNight @Composable -internal fun AnalyticsSettingsViewPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) = ElementPreview { +internal fun AnalyticsSettingsViewPreview(@PreviewParameter(AnalyticsSettingsStatePreviewParam::class) state: AnalyticsSettingsState) = ElementPreview { AnalyticsSettingsView( state = state, onBackClick = {}, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewParam.kt similarity index 95% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewParam.kt index 0e0e9e5ca5..b1a9a2224d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList import kotlinx.collections.immutable.toImmutableList -class BlockedUsersStateProvider : PreviewParameterProvider { +class BlockedUsersStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aBlockedUsersState(), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt index c4d8387295..be0c9d4937 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt @@ -126,7 +126,7 @@ private fun BlockedUserItem( @PreviewsDayNight @Composable -internal fun BlockedUsersViewPreview(@PreviewParameter(BlockedUsersStateProvider::class) state: BlockedUsersState) { +internal fun BlockedUsersViewPreview(@PreviewParameter(BlockedUsersStatePreviewParam::class) state: BlockedUsersState) { ElementPreview { BlockedUsersView( state = state, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStatePreviewParam.kt similarity index 96% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStatePreviewParam.kt index b14446d11c..8cc6ad7dce 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStatePreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.DeviceId import kotlinx.collections.immutable.persistentMapOf -open class DeveloperSettingsStateProvider : PreviewParameterProvider { +open class DeveloperSettingsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aDeveloperSettingsState(), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index 2fd2137451..66a478a4e5 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -231,7 +231,7 @@ private fun NotificationCategory(onPushHistoryClick: () -> Unit) { @PreviewsDayNight @Composable internal fun DeveloperSettingsViewPreview( - @PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState + @PreviewParameter(DeveloperSettingsStatePreviewParam::class) state: DeveloperSettingsState ) = ElementPreview { DeveloperSettingsView( state = state, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsStatePreviewParam.kt similarity index 94% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsStatePreviewParam.kt index e4536f4dba..480b8426d7 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.featureflag.ui.model.aFeatureUiModelList import io.element.android.libraries.matrix.api.tracing.TraceLogPack import kotlinx.collections.immutable.toImmutableList -open class AppDeveloperSettingsStateProvider : PreviewParameterProvider { +open class AppDeveloperSettingsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAppDeveloperSettingsState(), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt index a4f41edb4a..7b1401b5e5 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt @@ -182,7 +182,7 @@ private fun FeatureListContent( @PreviewsDayNight @Composable internal fun AppDeveloperSettingsViewPreview( - @PreviewParameter(AppDeveloperSettingsStateProvider::class) state: AppDeveloperSettingsState + @PreviewParameter(AppDeveloperSettingsStatePreviewParam::class) state: AppDeveloperSettingsState ) = ElementPreview { AppDeveloperSettingsView( state = state, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsStatePreviewParam.kt similarity index 95% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsStatePreviewParam.kt index 08e251dcfa..f0b5705376 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsStatePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.featureflag.ui.model.FeatureUiModel import kotlinx.collections.immutable.toImmutableList -internal class LabsStateProvider : PreviewParameterProvider { +internal class LabsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLabsState(features = aFeatureList()), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsView.kt index 7a6703e3bc..7c7f2482e3 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsView.kt @@ -101,7 +101,7 @@ fun LabsView( @PreviewsDayNight @Composable -internal fun LabsViewPreview(@PreviewParameter(LabsStateProvider::class) state: LabsState) { +internal fun LabsViewPreview(@PreviewParameter(LabsStatePreviewParam::class) state: LabsState) { ElementPreview { LabsView(state = state, onBack = {}) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStatePreviewParam.kt similarity index 98% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStatePreviewParam.kt index 905f869dbe..38c83153ea 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStatePreviewParam.kt @@ -19,7 +19,7 @@ import io.element.android.libraries.pushproviders.api.Distributor import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -open class NotificationSettingsStateProvider : PreviewParameterProvider { +open class NotificationSettingsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aValidNotificationSettingsState(systemNotificationsEnabled = false), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt index 9ede5138cf..a15f991f95 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt @@ -500,7 +500,9 @@ private fun InvalidNotificationSettingsView( @PreviewsDayNight @Composable -internal fun NotificationSettingsViewPreview(@PreviewParameter(NotificationSettingsStateProvider::class) state: NotificationSettingsState) = ElementPreview { +internal fun NotificationSettingsViewPreview(@PreviewParameter( + NotificationSettingsStatePreviewParam::class +) state: NotificationSettingsState) = ElementPreview { NotificationSettingsView( state = state, onBackClick = {}, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStatePreviewParam.kt similarity index 95% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStatePreviewParam.kt index 389554d2e0..819cd0d1f3 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStatePreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomNotificationMode import kotlinx.collections.immutable.persistentListOf -open class EditDefaultNotificationSettingStateProvider : PreviewParameterProvider { +open class EditDefaultNotificationSettingStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anEditDefaultNotificationSettingsState(), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt index 24da141a48..3d4256f65d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt @@ -125,7 +125,7 @@ fun EditDefaultNotificationSettingView( @PreviewsDayNight @Composable internal fun EditDefaultNotificationSettingViewPreview( - @PreviewParameter(EditDefaultNotificationSettingStateProvider::class) state: EditDefaultNotificationSettingState + @PreviewParameter(EditDefaultNotificationSettingStatePreviewParam::class) state: EditDefaultNotificationSettingState ) = ElementPreview { EditDefaultNotificationSettingView( state = state, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStatePreviewParam.kt similarity index 98% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStatePreviewParam.kt index 86f984f254..80c47a33b2 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStatePreviewParam.kt @@ -21,7 +21,7 @@ import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.toImmutableList -open class PreferencesRootStateProvider : PreviewParameterProvider { +open class PreferencesRootStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( // Nominal state, that a regular user will see if multi account is enabled diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 2a37309e26..82c88b16fa 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -373,14 +373,14 @@ private fun DeveloperPreferencesView(onOpenDeveloperSettings: () -> Unit) { @PreviewWithLargeHeight @Composable -internal fun PreferencesRootViewLightPreview(@PreviewParameter(PreferencesRootStateProvider::class) state: PreferencesRootState) = +internal fun PreferencesRootViewLightPreview(@PreviewParameter(PreferencesRootStatePreviewParam::class) state: PreferencesRootState) = ElementPreviewLight( drawableFallbackForImages = CommonDrawables.sample_avatar, ) { ContentToPreview(state) } @PreviewWithLargeHeight @Composable -internal fun PreferencesRootViewDarkPreview(@PreviewParameter(PreferencesRootStateProvider::class) state: PreferencesRootState) = +internal fun PreferencesRootViewDarkPreview(@PreviewParameter(PreferencesRootStatePreviewParam::class) state: PreferencesRootState) = ElementPreviewDark( drawableFallbackForImages = CommonDrawables.sample_avatar, ) { ContentToPreview(state) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/UserPreferences.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/UserPreferences.kt index 43c7e8dacf..066b9ac73e 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/UserPreferences.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/UserPreferences.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.MatrixUserHeader -import io.element.android.libraries.matrix.ui.components.MatrixUserProvider +import io.element.android.libraries.matrix.ui.components.MatrixUserPreviewParam @Composable fun UserPreferences( @@ -30,6 +30,6 @@ fun UserPreferences( @PreviewsDayNight @Composable -internal fun UserPreferencesPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreview { +internal fun UserPreferencesPreview(@PreviewParameter(MatrixUserPreviewParam::class) matrixUser: MatrixUser) = ElementPreview { UserPreferences(matrixUser) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStatePreviewParam.kt similarity index 95% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStatePreviewParam.kt index 2f238f9935..8b711cba51 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStatePreviewParam.kt @@ -17,7 +17,7 @@ import io.element.android.libraries.permissions.api.PermissionsState import io.element.android.libraries.permissions.api.aPermissionsState import kotlinx.collections.immutable.toImmutableList -open class EditUserProfileStateProvider : PreviewParameterProvider { +open class EditUserProfileStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aEditUserProfileState(), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt index d4571d7be5..89e739fe56 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt @@ -178,7 +178,7 @@ fun EditUserProfileView( @PreviewsDayNight @Composable -internal fun EditUserProfileViewPreview(@PreviewParameter(EditUserProfileStateProvider::class) state: EditUserProfileState) = +internal fun EditUserProfileViewPreview(@PreviewParameter(EditUserProfileStatePreviewParam::class) state: EditUserProfileState) = ElementPreview { EditUserProfileView( onEditProfileSuccess = {}, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusStatePreviewParam.kt similarity index 96% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusStateProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusStatePreviewParam.kt index 6b198183a7..34a12c604e 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusStatePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.api.user.UserStatus -internal class UserStatusStateProvider : PreviewParameterProvider { +internal class UserStatusStatePreviewParam : PreviewParameterProvider { override val values get() = sequenceOf( aUserStatusState(), aUserStatusState(displayedStatus = DisplayedStatus.UserSet(UserStatus("🌴", "Away"))), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusView.kt index 19cc43d3d7..c25c159f33 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusView.kt @@ -354,7 +354,7 @@ private fun EmojiPickerBottomSheet( @PreviewsDayNight @Composable -internal fun UserStatusViewPreview(@PreviewParameter(UserStatusStateProvider::class) state: UserStatusState) = ElementPreview { +internal fun UserStatusViewPreview(@PreviewParameter(UserStatusStatePreviewParam::class) state: UserStatusState) = ElementPreview { UserStatusView( state = state, emojiPickerRenderer = NoOpEmojiPickerRenderer, diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesStateProvider.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesStatePreviewParam.kt similarity index 91% rename from features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesStateProvider.kt rename to features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesStatePreviewParam.kt index 1de65c1a69..28ecc9f187 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesStateProvider.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.rageshake.api.preferences import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class RageshakePreferencesStateProvider : PreviewParameterProvider { +open class RageshakePreferencesStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRageshakePreferencesState(isEnabled = true, isSupported = true, sensitivity = 0.5f), diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt index a227d0ffcf..7b01f62b82 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt @@ -68,6 +68,8 @@ fun RageshakePreferencesView( @PreviewsDayNight @Composable -internal fun RageshakePreferencesViewPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) = ElementPreview { +internal fun RageshakePreferencesViewPreview(@PreviewParameter( + RageshakePreferencesStatePreviewParam::class +) state: RageshakePreferencesState) = ElementPreview { RageshakePreferencesView(state) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportStateProvider.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportStatePreviewParam.kt similarity index 94% rename from features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportStateProvider.kt rename to features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportStatePreviewParam.kt index 0afd06f22a..1e08c055b4 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportStateProvider.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.rageshake.impl.bugreport import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction -open class BugReportStateProvider : PreviewParameterProvider { +open class BugReportStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aBugReportState(), diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index fede373627..2c8dba4af3 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -232,7 +232,7 @@ fun BugReportView( @Preview(heightDp = 1000) @Composable -internal fun BugReportViewDayPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreview { +internal fun BugReportViewDayPreview(@PreviewParameter(BugReportStatePreviewParam::class) state: BugReportState) = ElementPreview { BugReportView( state = state, onSuccess = {}, @@ -243,7 +243,7 @@ internal fun BugReportViewDayPreview(@PreviewParameter(BugReportStateProvider::c @Preview(heightDp = 1000, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -internal fun BugReportViewNightPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreview { +internal fun BugReportViewNightPreview(@PreviewParameter(BugReportStatePreviewParam::class) state: BugReportState) = ElementPreview { BugReportView( state = state, onSuccess = {}, diff --git a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomStateProvider.kt b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomStatePreviewParam.kt similarity index 93% rename from features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomStateProvider.kt rename to features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomStatePreviewParam.kt index 8baae080f5..11d2777446 100644 --- a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomStateProvider.kt +++ b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.reportroom.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction -open class ReportRoomStateProvider : PreviewParameterProvider { +open class ReportRoomStatePreviewParam : PreviewParameterProvider { companion object { private const val A_REPORT_ROOM_REASON = "Inappropriate content" } diff --git a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomView.kt b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomView.kt index a754825dcc..7b614b13cb 100644 --- a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomView.kt +++ b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomView.kt @@ -139,7 +139,7 @@ fun ReportRoomView( @PreviewsDayNight @Composable internal fun ReportRoomViewPreview( - @PreviewParameter(ReportRoomStateProvider::class) state: ReportRoomState + @PreviewParameter(ReportRoomStatePreviewParam::class) state: ReportRoomState ) = ElementPreview { ReportRoomView( state = state, diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStatePreviewParam.kt similarity index 96% rename from features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStatePreviewParam.kt index 700a82795e..95e3d48c2a 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsV import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableMap -class ChangeRoomPermissionsStateProvider : PreviewParameterProvider { +class ChangeRoomPermissionsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aChangeRoomPermissionsState(), diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt index bb702ed90a..aba7a671a8 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt @@ -132,7 +132,7 @@ private fun titleForType(type: RoomPermissionType): String = when (type) { @PreviewsDayNight @Composable -internal fun ChangeRoomPermissionsViewPreview(@PreviewParameter(ChangeRoomPermissionsStateProvider::class) state: ChangeRoomPermissionsState) { +internal fun ChangeRoomPermissionsViewPreview(@PreviewParameter(ChangeRoomPermissionsStatePreviewParam::class) state: ChangeRoomPermissionsState) { ElementPreview { ChangeRoomPermissionsView( state = state, diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStatePreviewParam.kt similarity index 98% rename from features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStatePreviewParam.kt index e6b71ef752..2d6f8e02d0 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStatePreviewParam.kt @@ -29,7 +29,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -class ChangeRolesStateProvider : PreviewParameterProvider { +class ChangeRolesStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aChangeRolesState(), diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt index b2c88efede..05bb77dfd4 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt @@ -393,7 +393,7 @@ private fun MemberRow( @PreviewsDayNight @Composable -internal fun ChangeRolesViewPreview(@PreviewParameter(ChangeRolesStateProvider::class) state: ChangeRolesState) { +internal fun ChangeRolesViewPreview(@PreviewParameter(ChangeRolesStatePreviewParam::class) state: ChangeRolesState) { ElementPreview { ChangeRolesView( state = state diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStatePreviewParam.kt similarity index 96% rename from features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStateProvider.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStatePreviewParam.kt index 45bd72db19..2fc39212d8 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import kotlinx.collections.immutable.toImmutableList -class RolesAndPermissionsStateProvider : PreviewParameterProvider { +class RolesAndPermissionsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRolesAndPermissionsState(roomSupportsOwners = false), diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt index 3cf29c18cf..d9c09bbc64 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt @@ -191,7 +191,7 @@ private fun ChangeOwnRoleBottomSheet( @PreviewsDayNight @Composable -internal fun RolesAndPermissionsViewPreview(@PreviewParameter(RolesAndPermissionsStateProvider::class) state: RolesAndPermissionsState) { +internal fun RolesAndPermissionsViewPreview(@PreviewParameter(RolesAndPermissionsStatePreviewParam::class) state: RolesAndPermissionsState) { ElementPreview { RolesAndPermissionsView( state = state, diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStateProvider.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStatePreviewParam.kt similarity index 93% rename from features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStateProvider.kt rename to features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStatePreviewParam.kt index c3152bc303..6f93930b99 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStateProvider.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStatePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias -open class RoomAliasResolverStateProvider : PreviewParameterProvider { +open class RoomAliasResolverStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomAliasResolverState(), diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt index ad0d2e9ab9..37960a50e2 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt @@ -147,7 +147,7 @@ private fun RoomAliasResolverTopBar( @PreviewsDayNight @Composable -internal fun RoomAliasResolverViewPreview(@PreviewParameter(RoomAliasResolverStateProvider::class) state: RoomAliasResolverState) = ElementPreview { +internal fun RoomAliasResolverViewPreview(@PreviewParameter(RoomAliasResolverStatePreviewParam::class) state: RoomAliasResolverState) = ElementPreview { RoomAliasResolverView( state = state, onSuccess = { }, diff --git a/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStatePreviewParam.kt similarity index 94% rename from features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt rename to features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStatePreviewParam.kt index 3356fa0366..84b0bda6be 100644 --- a/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt +++ b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.roomcall.api import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class RoomCallStateProvider : PreviewParameterProvider { +open class RoomCallStatePreviewParam : PreviewParameterProvider { override val values: Sequence = sequenceOf( aStandByCallState(), aStandByCallState(canStartCall = false), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStatePreviewParam.kt similarity index 99% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt rename to features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStatePreviewParam.kt index 6aa4f11a8e..d1652b30dd 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStatePreviewParam.kt @@ -31,7 +31,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList import kotlinx.collections.immutable.toImmutableList -open class RoomDetailsStateProvider : PreviewParameterProvider { +open class RoomDetailsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomDetailsState(displayAdminSettings = true), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 96dc3e25ba..b2874d01f9 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -905,12 +905,12 @@ private fun DebugInfoSection( @PreviewWithExtraLargeHeight @Composable -internal fun RoomDetailsPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = +internal fun RoomDetailsPreview(@PreviewParameter(RoomDetailsStatePreviewParam::class) state: RoomDetailsState) = ElementPreviewLight { ContentToPreview(state) } @PreviewWithExtraLargeHeight @Composable -internal fun RoomDetailsDarkPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = +internal fun RoomDetailsDarkPreview(@PreviewParameter(RoomDetailsStatePreviewParam::class) state: RoomDetailsState) = ElementPreviewDark { ContentToPreview(state) } @PreviewWithExtraLargeHeight diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt index cb305426bc..d00a4e159a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt @@ -24,7 +24,7 @@ import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.features.invitepeople.api.InvitePeopleEvents import io.element.android.features.invitepeople.api.InvitePeopleState -import io.element.android.features.invitepeople.api.InvitePeopleStateProvider +import io.element.android.features.invitepeople.api.InvitePeopleStatePreviewParam import io.element.android.features.roomdetails.impl.R import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.button.BackButton @@ -116,7 +116,7 @@ private fun InviteProgressDialog() { @PreviewsDayNight @Composable -internal fun RoomInviteMembersViewPreview(@PreviewParameter(InvitePeopleStateProvider::class) state: InvitePeopleState) = ElementPreview { +internal fun RoomInviteMembersViewPreview(@PreviewParameter(InvitePeopleStatePreviewParam::class) state: InvitePeopleState) = ElementPreview { RoomInviteMembersView( state = state, invitePeopleView = {}, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStatePreviewParam.kt similarity index 98% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt rename to features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStatePreviewParam.kt index 6b1971fdae..59a26e6207 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStatePreviewParam.kt @@ -31,7 +31,7 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.user.DisplayedStatus import kotlinx.collections.immutable.persistentListOf -internal class RoomMemberListStateProvider : PreviewParameterProvider { +internal class RoomMemberListStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomMemberListState( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt index 33ca61b570..f19ee95106 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt @@ -375,7 +375,7 @@ private fun RoomMemberListTopBar( @PreviewsDayNight @Composable -internal fun RoomMemberListViewPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) = ElementPreview { +internal fun RoomMemberListViewPreview(@PreviewParameter(RoomMemberListStatePreviewParam::class) state: RoomMemberListState) = ElementPreview { RoomMemberListView( state = state, navigator = object : RoomMemberListNavigator { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStatePreviewParam.kt similarity index 95% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt rename to features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStatePreviewParam.kt index 603d22b502..f7ac7597d6 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStatePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.room.RoomNotificationMode -internal class RoomNotificationSettingsStateProvider : PreviewParameterProvider { +internal class RoomNotificationSettingsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomNotificationSettingsState(), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt index a8be2944a2..f0676f133e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt @@ -174,7 +174,7 @@ private fun RoomNotificationSettingsTopBar( @PreviewsDayNight @Composable internal fun RoomNotificationSettingsViewPreview( - @PreviewParameter(RoomNotificationSettingsStateProvider::class) state: RoomNotificationSettingsState + @PreviewParameter(RoomNotificationSettingsStatePreviewParam::class) state: RoomNotificationSettingsState ) = ElementPreview { RoomNotificationSettingsView( state = state, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStatePreviewParam.kt similarity index 92% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStateProvider.kt rename to features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStatePreviewParam.kt index 039f489bbf..6a3ecacfcb 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStatePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.room.RoomNotificationMode -internal class UserDefinedRoomNotificationSettingsStateProvider : PreviewParameterProvider { +internal class UserDefinedRoomNotificationSettingsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( RoomNotificationSettingsState( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt index 1721c9925c..befbc9382f 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt @@ -105,7 +105,7 @@ private fun UserDefinedRoomNotificationSettingsTopBar( @PreviewsDayNight @Composable internal fun UserDefinedRoomNotificationSettingsViewPreview( - @PreviewParameter(UserDefinedRoomNotificationSettingsStateProvider::class) state: RoomNotificationSettingsState + @PreviewParameter(UserDefinedRoomNotificationSettingsStatePreviewParam::class) state: RoomNotificationSettingsState ) = ElementPreview { UserDefinedRoomNotificationSettingsView( state = state, diff --git a/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditStateProvider.kt b/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditStatePreviewParam.kt similarity index 96% rename from features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditStateProvider.kt rename to features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditStatePreviewParam.kt index a6e2e3798b..b58cf5db49 100644 --- a/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditStateProvider.kt +++ b/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditStatePreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.permissions.api.PermissionsState import io.element.android.libraries.permissions.api.aPermissionsState import kotlinx.collections.immutable.toImmutableList -open class RoomDetailsEditStateProvider : PreviewParameterProvider { +open class RoomDetailsEditStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomDetailsEditState(), diff --git a/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditView.kt b/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditView.kt index 9cdc800267..04c8796285 100644 --- a/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditView.kt +++ b/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditView.kt @@ -185,7 +185,7 @@ fun RoomDetailsEditView( @PreviewsDayNight @Composable -internal fun RoomDetailsEditViewPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = ElementPreview { +internal fun RoomDetailsEditViewPreview(@PreviewParameter(RoomDetailsEditStatePreviewParam::class) state: RoomDetailsEditState) = ElementPreview { RoomDetailsEditView( state = state, onDone = {}, diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStatePreviewParam.kt similarity index 96% rename from features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt rename to features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStatePreviewParam.kt index 4ee90fdf16..d7869f3d06 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -open class RoomDirectoryStateProvider : PreviewParameterProvider { +open class RoomDirectoryStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomDirectoryState(), diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt index dd9098b7ad..b880a4d28e 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt @@ -278,7 +278,7 @@ private fun RoomDirectoryRoomRow( @PreviewsDayNight @Composable -internal fun RoomDirectoryViewPreview(@PreviewParameter(RoomDirectoryStateProvider::class) state: RoomDirectoryState) = ElementPreview { +internal fun RoomDirectoryViewPreview(@PreviewParameter(RoomDirectoryStatePreviewParam::class) state: RoomDirectoryState) = ElementPreview { RoomDirectoryView( state = state, onResultClick = {}, diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStateProvider.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStatePreviewParam.kt similarity index 97% rename from features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStateProvider.kt rename to features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStatePreviewParam.kt index 2c32f2c47a..78358da213 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStateProvider.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStatePreviewParam.kt @@ -20,7 +20,7 @@ import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.toImmutableList -class InternalRoomMemberModerationStateProvider : PreviewParameterProvider { +class InternalRoomMemberModerationStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomMembersModerationState( diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt index a5e0ac274f..f1c6cc8ed3 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt @@ -362,7 +362,7 @@ private fun RoomMemberActionsBottomSheet( @PreviewsDayNight @Composable -internal fun RoomMemberModerationViewPreview(@PreviewParameter(InternalRoomMemberModerationStateProvider::class) state: InternalRoomMemberModerationState) { +internal fun RoomMemberModerationViewPreview(@PreviewParameter(InternalRoomMemberModerationStatePreviewParam::class) state: InternalRoomMemberModerationState) { val isDoingAction = listOf(state.kickUserAsyncAction, state.banUserAsyncAction, state.unbanUserAsyncAction).any { it is AsyncAction.Loading } val modifier = if (isDoingAction) { Modifier.fillMaxWidth().heightIn(min = 64.dp) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableStatePreviewParam.kt similarity index 92% rename from features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableStateProvider.kt rename to features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableStatePreviewParam.kt index 6aa10d9a0b..6eea9c366a 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableStateProvider.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.encryption.BackupState -open class SecureBackupDisableStateProvider : PreviewParameterProvider { +open class SecureBackupDisableStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSecureBackupDisableState(), diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt index 24678148b4..423e2ed5da 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt @@ -112,7 +112,7 @@ private fun SecureBackupDisableItem(text: String) { @PreviewsDayNight @Composable internal fun SecureBackupDisableViewPreview( - @PreviewParameter(SecureBackupDisableStateProvider::class) state: SecureBackupDisableState + @PreviewParameter(SecureBackupDisableStatePreviewParam::class) state: SecureBackupDisableState ) = ElementPreview { SecureBackupDisableView( state = state, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyStatePreviewParam.kt similarity index 94% rename from features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyStateProvider.kt rename to features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyStatePreviewParam.kt index 5e95b5713e..5041f4ab92 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyStateProvider.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyStatePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyView import io.element.android.features.securebackup.impl.setup.views.aFormattedRecoveryKey import io.element.android.libraries.architecture.AsyncAction -open class SecureBackupEnterRecoveryKeyStateProvider : PreviewParameterProvider { +open class SecureBackupEnterRecoveryKeyStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSecureBackupEnterRecoveryKeyState(recoveryKey = ""), 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 34fca33e96..823bbf5c80 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 @@ -97,7 +97,7 @@ private fun ColumnScope.Buttons( @PreviewsDayNight @Composable internal fun SecureBackupEnterRecoveryKeyViewPreview( - @PreviewParameter(SecureBackupEnterRecoveryKeyStateProvider::class) state: SecureBackupEnterRecoveryKeyState + @PreviewParameter(SecureBackupEnterRecoveryKeyStatePreviewParam::class) state: SecureBackupEnterRecoveryKeyState ) = ElementPreview { SecureBackupEnterRecoveryKeyView( state = state, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordStatePreviewParam.kt similarity index 91% rename from features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordStateProvider.kt rename to features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordStatePreviewParam.kt index fc4ce2430e..b6e786a9bc 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordStateProvider.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.securebackup.impl.reset.password import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction -class ResetIdentityPasswordStateProvider : PreviewParameterProvider { +class ResetIdentityPasswordStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aResetIdentityPasswordState(), diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt index 3bb77ac206..27612a7b4d 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt @@ -106,7 +106,7 @@ private fun Content(text: String, onTextChange: (String) -> Unit, hasError: Bool @PreviewsDayNight @Composable -internal fun ResetIdentityPasswordViewPreview(@PreviewParameter(ResetIdentityPasswordStateProvider::class) state: ResetIdentityPasswordState) { +internal fun ResetIdentityPasswordViewPreview(@PreviewParameter(ResetIdentityPasswordStatePreviewParam::class) state: ResetIdentityPasswordState) { ElementPreview { ResetIdentityPasswordView( state = state, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootStatePreviewParam.kt similarity index 88% rename from features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootStateProvider.kt rename to features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootStatePreviewParam.kt index 697401367f..e4bb24355e 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootStateProvider.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.securebackup.impl.reset.root import androidx.compose.ui.tooling.preview.PreviewParameterProvider -class ResetIdentityRootStateProvider : PreviewParameterProvider { +class ResetIdentityRootStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( ResetIdentityRootState( 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 7ac8dbe4cf..0109bd4faf 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 @@ -130,7 +130,7 @@ private fun Content() { @PreviewsDayNight @Composable -internal fun ResetIdentityRootViewPreview(@PreviewParameter(ResetIdentityRootStateProvider::class) state: ResetIdentityRootState) { +internal fun ResetIdentityRootViewPreview(@PreviewParameter(ResetIdentityRootStatePreviewParam::class) state: ResetIdentityRootState) { ElementPreview { ResetIdentityRootView( state = state, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStatePreviewParam.kt similarity index 97% rename from features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt rename to features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStatePreviewParam.kt index 5dc3bea7e8..3247db78eb 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.RecoveryState -open class SecureBackupRootStateProvider : PreviewParameterProvider { +open class SecureBackupRootStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSecureBackupRootState(backupState = BackupState.UNKNOWN, doesBackupExistOnServer = AsyncData.Uninitialized), diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt index 626a162152..8059329cd0 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt @@ -236,7 +236,7 @@ private fun LoadingView() { @PreviewsDayNight @Composable internal fun SecureBackupRootViewPreview( - @PreviewParameter(SecureBackupRootStateProvider::class) state: SecureBackupRootState + @PreviewParameter(SecureBackupRootStatePreviewParam::class) state: SecureBackupRootState ) = ElementPreview { SecureBackupRootView( state = state, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupStatePreviewParam.kt similarity index 95% rename from features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupStateProvider.kt rename to features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupStatePreviewParam.kt index a780fa0c36..f88fd56d79 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupStateProvider.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupStatePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyUser import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyViewState import io.element.android.features.securebackup.impl.setup.views.aFormattedRecoveryKey -open class SecureBackupSetupStateProvider : PreviewParameterProvider { +open class SecureBackupSetupStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSecureBackupSetupState(setupState = SetupState.Init), diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt index 497c64f072..cab1e0995c 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt @@ -195,7 +195,7 @@ private fun ColumnScope.Buttons( @PreviewsDayNight @Composable internal fun SecureBackupSetupViewPreview( - @PreviewParameter(SecureBackupSetupStateProvider::class) state: SecureBackupSetupState + @PreviewParameter(SecureBackupSetupStatePreviewParam::class) state: SecureBackupSetupState ) = ElementPreview { SecureBackupSetupView( state = state, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupViewChangePreview.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupViewChangePreview.kt index 39fa7debe9..0fd7901f55 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupViewChangePreview.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupViewChangePreview.kt @@ -17,7 +17,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight @PreviewsDayNight @Composable internal fun SecureBackupSetupViewChangePreview( - @PreviewParameter(SecureBackupSetupStateProvider::class) state: SecureBackupSetupState + @PreviewParameter(SecureBackupSetupStatePreviewParam::class) state: SecureBackupSetupState ) = ElementPreview { SecureBackupSetupView( state = state.copy( diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt index 8055709f20..61b36d3adf 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt @@ -269,7 +269,7 @@ private fun RecoveryKeyFooter(state: RecoveryKeyViewState) { @PreviewsDayNight @Composable internal fun RecoveryKeyViewPreview( - @PreviewParameter(RecoveryKeyViewStateProvider::class) state: RecoveryKeyViewState + @PreviewParameter(RecoveryKeyViewStatePreviewParam::class) state: RecoveryKeyViewState ) = ElementPreview { RecoveryKeyView( state = state, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyViewStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyViewStatePreviewParam.kt similarity index 95% rename from features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyViewStateProvider.kt rename to features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyViewStatePreviewParam.kt index c1a841aa7b..69a5757e1f 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyViewStateProvider.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyViewStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.securebackup.impl.setup.views import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class RecoveryKeyViewStateProvider : PreviewParameterProvider { +open class RecoveryKeyViewStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf(RecoveryKeyUserStory.Setup, RecoveryKeyUserStory.Change, RecoveryKeyUserStory.Enter) .flatMap { diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressStateProvider.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressStatePreviewParam.kt similarity index 94% rename from features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressStateProvider.kt rename to features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressStatePreviewParam.kt index 7b82175bb7..096e9c50c5 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressStateProvider.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity -open class EditRoomAddressStateProvider : PreviewParameterProvider { +open class EditRoomAddressStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anEditRoomAddressState(), diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressView.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressView.kt index da18b963fa..c1156df209 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressView.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressView.kt @@ -112,7 +112,7 @@ private fun EditRoomAddressTopBar( @PreviewsDayNight @Composable internal fun EditRoomAddressViewPreview( - @PreviewParameter(EditRoomAddressStateProvider::class) state: EditRoomAddressState + @PreviewParameter(EditRoomAddressStatePreviewParam::class) state: EditRoomAddressState ) = ElementPreview { EditRoomAddressView( state = state, diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesStateProvider.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesStatePreviewParam.kt similarity index 94% rename from features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesStateProvider.kt rename to features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesStatePreviewParam.kt index d2fec941ff..d5d1986b83 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesStateProvider.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesStatePreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.previewutils.room.aSpaceRoom import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableSet -open class ManageAuthorizedSpacesStateProvider : PreviewParameterProvider { +open class ManageAuthorizedSpacesStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aManageAuthorizedSpacesState(), diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesView.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesView.kt index 514c38a3fd..0bb6bc49e6 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesView.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesView.kt @@ -191,7 +191,7 @@ private fun ManageAuthorizedSpacesTopBar( @PreviewsDayNight @Composable internal fun ManageAuthorizedSpacesViewPreview( - @PreviewParameter(ManageAuthorizedSpacesStateProvider::class) state: ManageAuthorizedSpacesState + @PreviewParameter(ManageAuthorizedSpacesStatePreviewParam::class) state: ManageAuthorizedSpacesState ) = ElementPreview { ManageAuthorizedSpacesView(state = state) } diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyStateProvider.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyStatePreviewParam.kt similarity index 98% rename from features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyStateProvider.kt rename to features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyStatePreviewParam.kt index 998d61b564..102adb4015 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyStateProvider.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyStatePreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.matrix.api.spaces.SpaceRoom import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableSet -open class SecurityAndPrivacyStateProvider : PreviewParameterProvider { +open class SecurityAndPrivacyStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = commonSecurityAndPrivacyStates(isSpace = false) + commonSecurityAndPrivacyStates(isSpace = true) + diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyView.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyView.kt index 43feb99611..e2cc5522a6 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyView.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyView.kt @@ -469,12 +469,12 @@ private fun HistoryVisibilityItem( @PreviewWithLargeHeight @Composable -internal fun SecurityAndPrivacyViewLightPreview(@PreviewParameter(SecurityAndPrivacyStateProvider::class) state: SecurityAndPrivacyState) = +internal fun SecurityAndPrivacyViewLightPreview(@PreviewParameter(SecurityAndPrivacyStatePreviewParam::class) state: SecurityAndPrivacyState) = ElementPreviewLight { ContentToPreview(state) } @PreviewWithLargeHeight @Composable -internal fun SecurityAndPrivacyViewDarkPreview(@PreviewParameter(SecurityAndPrivacyStateProvider::class) state: SecurityAndPrivacyState) = +internal fun SecurityAndPrivacyViewDarkPreview(@PreviewParameter(SecurityAndPrivacyStatePreviewParam::class) state: SecurityAndPrivacyState) = ElementPreviewDark { ContentToPreview(state) } @ExcludeFromCoverage diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareStateProvider.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareStatePreviewParam.kt similarity index 93% rename from features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareStateProvider.kt rename to features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareStatePreviewParam.kt index 88f9d42f40..97135acb71 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareStateProvider.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId -open class ShareStateProvider : PreviewParameterProvider { +open class ShareStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aShareState(), diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareView.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareView.kt index ec5897bcad..5e5130965f 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareView.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareView.kt @@ -33,7 +33,7 @@ fun ShareView( @PreviewsDayNight @Composable -internal fun ShareViewPreview(@PreviewParameter(ShareStateProvider::class) state: ShareState) = ElementPreview { +internal fun ShareViewPreview(@PreviewParameter(ShareStatePreviewParam::class) state: ShareState) = ElementPreview { ShareView( state = state, onShareSuccess = {} 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/SignedOutStatePreviewParam.kt similarity index 94% rename from features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt rename to features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStatePreviewParam.kt index 95a57db93e..ae60b7aacb 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/SignedOutStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.sessionstorage.api.LoginType import io.element.android.libraries.sessionstorage.api.SessionData -open class SignedOutStateProvider : PreviewParameterProvider { +open class SignedOutStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSignedOutState(), diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt index 41a99713e7..9dcb4585e5 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt @@ -109,7 +109,7 @@ private fun SignedOutFooter( @PreviewsDayNight @Composable internal fun SignedOutViewPreview( - @PreviewParameter(SignedOutStateProvider::class) state: SignedOutState, + @PreviewParameter(SignedOutStatePreviewParam::class) state: SignedOutState, ) = ElementPreview { SignedOutView( state = state, diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceStatePreviewParam.kt similarity index 97% rename from features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceStateProvider.kt rename to features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceStatePreviewParam.kt index add57bd4c4..9f9652c65c 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceStateProvider.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceStatePreviewParam.kt @@ -17,7 +17,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -internal class AddRoomToSpaceStateProvider : PreviewParameterProvider { +internal class AddRoomToSpaceStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( // Initial state with suggestions diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt index 2f75072d0a..4db17634d6 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt @@ -273,7 +273,7 @@ private fun RoomListItem( @PreviewsDayNight @Composable internal fun AddRoomToSpaceViewPreview( - @PreviewParameter(AddRoomToSpaceStateProvider::class) state: AddRoomToSpaceState + @PreviewParameter(AddRoomToSpaceStatePreviewParam::class) state: AddRoomToSpaceState ) = ElementPreview { AddRoomToSpaceView( state = state, diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStatePreviewParam.kt similarity index 98% rename from features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStateProvider.kt rename to features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStatePreviewParam.kt index ddf34cb076..3243a4fa53 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStateProvider.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStatePreviewParam.kt @@ -19,7 +19,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -class LeaveSpaceStateProvider : PreviewParameterProvider { +class LeaveSpaceStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLeaveSpaceState(), diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt index b4722fad0d..992240cac2 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt @@ -366,7 +366,7 @@ private fun SpaceItem( @PreviewsDayNight @Composable internal fun LeaveSpaceViewPreview( - @PreviewParameter(LeaveSpaceStateProvider::class) state: LeaveSpaceState, + @PreviewParameter(LeaveSpaceStatePreviewParam::class) state: LeaveSpaceState, ) = ElementPreview { LeaveSpaceView( state = state, diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStatePreviewParam.kt similarity index 98% rename from features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt rename to features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStatePreviewParam.kt index 6824cc6c10..34d6e4534a 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStatePreviewParam.kt @@ -26,7 +26,7 @@ import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableMap import kotlinx.collections.immutable.toImmutableSet -open class SpaceStateProvider : PreviewParameterProvider { +open class SpaceStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSpaceState(), diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt index fe971091f5..f1cddf5e0f 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt @@ -680,7 +680,7 @@ private fun RemoveRoomsActionView( @PreviewsDayNight @Composable internal fun SpaceViewPreview( - @PreviewParameter(SpaceStateProvider::class) state: SpaceState + @PreviewParameter(SpaceStatePreviewParam::class) state: SpaceState ) = ElementPreview { SpaceView( state = state, diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStatePreviewParam.kt similarity index 94% rename from features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt rename to features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStatePreviewParam.kt index 36f6a2a1d0..a68a3ce850 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStatePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.designsystem.preview.SPACE_NAME import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId -open class SpaceSettingsStateProvider : PreviewParameterProvider { +open class SpaceSettingsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSpaceSettingsState(), diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt index 6237e443b0..b5cfc7702a 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt @@ -223,7 +223,7 @@ private fun LeaveSpaceItem( @PreviewsDayNight @Composable internal fun SpaceSettingsViewPreview( - @PreviewParameter(SpaceSettingsStateProvider::class) state: SpaceSettingsState + @PreviewParameter(SpaceSettingsStatePreviewParam::class) state: SpaceSettingsState ) = ElementPreview { SpaceSettingsView( state = state, diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/components/UserListView.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/components/UserListView.kt index c8587d9784..4b62820ef4 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/components/UserListView.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/components/UserListView.kt @@ -26,7 +26,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.startchat.impl.userlist.UserListEvents import io.element.android.features.startchat.impl.userlist.UserListState -import io.element.android.features.startchat.impl.userlist.UserListStateProvider +import io.element.android.features.startchat.impl.userlist.UserListStatePreviewParam import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -129,7 +129,7 @@ fun UserListView( @PreviewsDayNight @Composable -internal fun UserListViewPreview(@PreviewParameter(UserListStateProvider::class) state: UserListState) = ElementPreview { +internal fun UserListViewPreview(@PreviewParameter(UserListStatePreviewParam::class) state: UserListState) = ElementPreview { UserListView( state = state, onSelectUser = {}, diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressStateProvider.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressStatePreviewParam.kt similarity index 93% rename from features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressStateProvider.kt rename to features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressStatePreviewParam.kt index 5528a84196..3404a737a3 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressStateProvider.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias -open class JoinRoomByAddressStateProvider : PreviewParameterProvider { +open class JoinRoomByAddressStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aJoinRoomByAddressState(), diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressView.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressView.kt index ffe6a47f0d..4d2babe70b 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressView.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressView.kt @@ -137,7 +137,7 @@ private fun RoomAddressField( @PreviewsDayNight @Composable internal fun JoinRoomByAddressViewPreview( - @PreviewParameter(JoinRoomByAddressStateProvider::class) state: JoinRoomByAddressState + @PreviewParameter(JoinRoomByAddressStatePreviewParam::class) state: JoinRoomByAddressState ) = ElementPreview { JoinRoomByAddressView(state = state) } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStateProvider.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStatePreviewParam.kt similarity index 97% rename from features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStateProvider.kt rename to features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStatePreviewParam.kt index 41a3551d1d..e8ac1d55a7 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStateProvider.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStatePreviewParam.kt @@ -21,7 +21,7 @@ import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.usersearch.api.UserSearchResult import kotlinx.collections.immutable.persistentListOf -open class StartChatStateProvider : PreviewParameterProvider { +open class StartChatStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aCreateRoomRootState(), diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt index 104a8cecf7..350cf15757 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt @@ -255,7 +255,7 @@ private fun CreateRoomActionButton( @PreviewsDayNight @Composable -internal fun StartChatViewPreview(@PreviewParameter(StartChatStateProvider::class) state: StartChatState) = +internal fun StartChatViewPreview(@PreviewParameter(StartChatStatePreviewParam::class) state: StartChatState) = ElementPreview { StartChatView( state = state, diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListStateProvider.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListStatePreviewParam.kt similarity index 97% rename from features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListStateProvider.kt rename to features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListStatePreviewParam.kt index 91d047d238..190ff23ad2 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListStateProvider.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListStatePreviewParam.kt @@ -19,7 +19,7 @@ import io.element.android.libraries.usersearch.api.UserSearchResult import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList -open class UserListStateProvider : PreviewParameterProvider { +open class UserListStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aUserListState(), diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStateProvider.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStatePreviewParam.kt similarity index 97% rename from features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStateProvider.kt rename to features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStatePreviewParam.kt index 4e25284753..84a95e0b14 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStateProvider.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStatePreviewParam.kt @@ -21,7 +21,7 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.ui.components.aMatrixUser -open class UserProfileStateProvider : PreviewParameterProvider { +open class UserProfileStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aUserProfileState(), diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt index c5a661dbac..b9f43db36d 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt @@ -147,7 +147,7 @@ private fun VerifyUserSection( @PreviewsDayNight @Composable internal fun UserProfileViewPreview( - @PreviewParameter(UserProfileStateProvider::class) state: UserProfileState + @PreviewParameter(UserProfileStatePreviewParam::class) state: UserProfileState ) = ElementPreview { UserProfileView( state = state, diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStatePreviewParam.kt similarity index 97% rename from features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateProvider.kt rename to features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStatePreviewParam.kt index 895f78cbdc..c8d70e03c0 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateProvider.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStatePreviewParam.kt @@ -20,7 +20,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails import io.element.android.libraries.matrix.api.verification.VerificationRequest -open class IncomingVerificationStateProvider : PreviewParameterProvider { +open class IncomingVerificationStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anIncomingVerificationState(), diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt index e86c83825b..64adc78aa0 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt @@ -264,7 +264,9 @@ private fun IncomingVerificationBottomMenu( @PreviewsDayNight @Composable -internal fun IncomingVerificationViewPreview(@PreviewParameter(IncomingVerificationStateProvider::class) state: IncomingVerificationState) = ElementPreview { +internal fun IncomingVerificationViewPreview(@PreviewParameter( + IncomingVerificationStatePreviewParam::class +) state: IncomingVerificationState) = ElementPreview { IncomingVerificationView( state = state, ) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStatePreviewParam.kt similarity index 97% rename from features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateProvider.kt rename to features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStatePreviewParam.kt index 2da099df9f..ee3d5dd7a3 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateProvider.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStatePreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.verification.VerificationRequest -open class OutgoingVerificationStateProvider : PreviewParameterProvider { +open class OutgoingVerificationStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anOutgoingVerificationState( diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt index 9131ad63fb..978ced6587 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt @@ -328,7 +328,9 @@ private fun OutgoingVerificationBottomMenu( @PreviewsDayNight @Composable -internal fun OutgoingVerificationViewPreview(@PreviewParameter(OutgoingVerificationStateProvider::class) state: OutgoingVerificationState) = ElementPreview { +internal fun OutgoingVerificationViewPreview(@PreviewParameter( + OutgoingVerificationStatePreviewParam::class +) state: OutgoingVerificationState) = ElementPreview { OutgoingVerificationView( state = state, onLearnMoreClick = {}, diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileStateProvider.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileStatePreviewParam.kt similarity index 97% rename from features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileStateProvider.kt rename to features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileStatePreviewParam.kt index 1b20b5c947..8420cf8fd6 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileStateProvider.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.viewfolder.impl.file import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncData -open class ViewFileStateProvider : PreviewParameterProvider { +open class ViewFileStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aViewFileState(), diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt index 23f92fc9ba..8b44f12ba8 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt @@ -92,7 +92,7 @@ fun ViewFileView( @PreviewsDayNight @Composable -internal fun ViewFileViewPreview(@PreviewParameter(ViewFileStateProvider::class) state: ViewFileState) = ElementPreview { +internal fun ViewFileViewPreview(@PreviewParameter(ViewFileStatePreviewParam::class) state: ViewFileState) = ElementPreview { ViewFileView( state = state, onBackClick = {}, diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderStateProvider.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderStatePreviewParam.kt similarity index 92% rename from features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderStateProvider.kt rename to features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderStatePreviewParam.kt index 2debe4cbf8..0fad56fe40 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderStateProvider.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.viewfolder.impl.model.Item import kotlinx.collections.immutable.toImmutableList -open class ViewFolderStateProvider : PreviewParameterProvider { +open class ViewFolderStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aViewFolderState(), diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderView.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderView.kt index 86cd5302c8..89fb3da7f4 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderView.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderView.kt @@ -142,7 +142,7 @@ private fun ItemRow( @PreviewsDayNight @Composable -internal fun ViewFolderViewPreview(@PreviewParameter(ViewFolderStateProvider::class) state: ViewFolderState) = ElementPreview { +internal fun ViewFolderViewPreview(@PreviewParameter(ViewFolderStatePreviewParam::class) state: ViewFolderState) = ElementPreview { ViewFolderView( state = state, onNavigateTo = {}, diff --git a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectStateProvider.kt b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectStatePreviewParam.kt similarity index 90% rename from libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectStateProvider.kt rename to libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectStatePreviewParam.kt index 0c12d34352..78c2f895dd 100644 --- a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectStateProvider.kt +++ b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectStatePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList import kotlinx.collections.immutable.toImmutableList -open class AccountSelectStateProvider : PreviewParameterProvider { +open class AccountSelectStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAccountSelectState(), diff --git a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectView.kt b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectView.kt index bfa1bf45b6..74f15f8cfa 100644 --- a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectView.kt +++ b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectView.kt @@ -80,7 +80,7 @@ fun AccountSelectView( @PreviewsDayNight @Composable -internal fun AccountSelectViewPreview(@PreviewParameter(AccountSelectStateProvider::class) state: AccountSelectState) = ElementPreview { +internal fun AccountSelectViewPreview(@PreviewParameter(AccountSelectStatePreviewParam::class) state: AccountSelectState) = ElementPreview { AccountSelectView( state = state, onSelectAccount = {}, diff --git a/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/previews/DateFormatterModeProvider.kt b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/previews/DateFormatterModePreviewParam.kt similarity index 86% rename from libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/previews/DateFormatterModeProvider.kt rename to libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/previews/DateFormatterModePreviewParam.kt index 48856cad7b..f00911294e 100644 --- a/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/previews/DateFormatterModeProvider.kt +++ b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/previews/DateFormatterModePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.libraries.dateformatter.impl.previews import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.dateformatter.api.DateFormatterMode -class DateFormatterModeProvider : PreviewParameterProvider { +class DateFormatterModePreviewParam : PreviewParameterProvider { override val values: Sequence get() = DateFormatterMode.entries.asSequence() } diff --git a/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/previews/DateFormatterModeViewPreview.kt b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/previews/DateFormatterModeViewPreview.kt index e3cd9563a8..5e1d35a568 100644 --- a/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/previews/DateFormatterModeViewPreview.kt +++ b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/previews/DateFormatterModeViewPreview.kt @@ -34,7 +34,7 @@ import kotlin.time.Instant @Preview @Composable internal fun DateFormatterModeViewPreview( - @PreviewParameter(DateFormatterModeProvider::class) dateFormatterMode: DateFormatterMode, + @PreviewParameter(DateFormatterModePreviewParam::class) dateFormatterMode: DateFormatterMode, ) = ElementPreview { DateFormatterModeView(dateFormatterMode) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt index 8b0f17177b..08d2f05fbc 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt @@ -134,7 +134,7 @@ enum class ComposerAlertLevel { @PreviewsDayNight @Composable internal fun ComposerAlertMoleculePreview( - @PreviewParameter(ComposerAlertMoleculeParamsProvider::class) params: ComposerAlertMoleculeParams, + @PreviewParameter(ComposerAlertMoleculeParamsPreviewParam::class) params: ComposerAlertMoleculeParams, ) = ElementPreview { ComposerAlertMolecule( avatar = params.avatar, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMoleculeParamsProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMoleculeParamsPreviewParam.kt similarity index 91% rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMoleculeParamsProvider.kt rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMoleculeParamsPreviewParam.kt index cd1bc8a02b..487cd06dfc 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMoleculeParamsProvider.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMoleculeParamsPreviewParam.kt @@ -19,7 +19,7 @@ internal data class ComposerAlertMoleculeParams( val showIcon: Boolean = false, ) -internal class ComposerAlertMoleculeParamsProvider : PreviewParameterProvider { +internal class ComposerAlertMoleculeParamsPreviewParam : PreviewParameterProvider { private val allLevels = sequenceOf( ComposerAlertLevel.Info, ComposerAlertLevel.Critical diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BigIcon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BigIcon.kt index 639d365625..cf6329ee66 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BigIcon.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BigIcon.kt @@ -160,7 +160,7 @@ internal fun BigIconPreview() = ElementPreview { horizontalArrangement = Arrangement.spacedBy(10.dp), verticalArrangement = Arrangement.spacedBy(10.dp), ) { - items(BigIconStyleProvider().values.toList()) { style -> + items(BigIconStylePreviewParam().values.toList()) { style -> Box( contentAlignment = Alignment.Center ) { @@ -170,7 +170,7 @@ internal fun BigIconPreview() = ElementPreview { } } -internal class BigIconStyleProvider : PreviewParameterProvider { +internal class BigIconStylePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( BigIcon.Style.Default(Icons.Filled.CatchingPokemon), diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionPreviewParam.kt similarity index 90% rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionProvider.kt rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionPreviewParam.kt index 2f7439e1c8..9fe699b828 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionProvider.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionPreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.libraries.designsystem.components.async import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction -open class AsyncActionProvider : PreviewParameterProvider> { +open class AsyncActionPreviewParam : PreviewParameterProvider> { override val values: Sequence> get() = sequenceOf( AsyncAction.Uninitialized, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt index da1b0fc796..65dc628eb3 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt @@ -82,7 +82,7 @@ object AsyncActionViewDefaults { @PreviewsDayNight @Composable internal fun AsyncActionViewPreview( - @PreviewParameter(AsyncActionProvider::class) async: AsyncAction, + @PreviewParameter(AsyncActionPreviewParam::class) async: AsyncAction, ) = ElementPreview { AsyncActionView( async = async, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarRow.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarRow.kt index 25784aa501..b80105350d 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarRow.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarRow.kt @@ -24,7 +24,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.components.avatar.internal.OverlapRatioProvider +import io.element.android.libraries.designsystem.components.avatar.internal.OverlapRatioPreviewParam import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toPx @@ -110,7 +110,7 @@ fun AvatarRow( @Composable @PreviewsDayNight -internal fun AvatarRowPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) { +internal fun AvatarRowPreview(@PreviewParameter(OverlapRatioPreviewParam::class) overlapRatio: Float) { ElementPreview { ContentToPreview(overlapRatio) } @@ -118,7 +118,7 @@ internal fun AvatarRowPreview(@PreviewParameter(OverlapRatioProvider::class) ove @Composable @PreviewsDayNight -internal fun AvatarRowLastOnTopPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) { +internal fun AvatarRowLastOnTopPreview(@PreviewParameter(OverlapRatioPreviewParam::class) overlapRatio: Float) { ElementPreview { ContentToPreview( overlapRatio = overlapRatio, @@ -129,7 +129,7 @@ internal fun AvatarRowLastOnTopPreview(@PreviewParameter(OverlapRatioProvider::c @Composable @PreviewsDayNight -internal fun AvatarRowRtlPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) { +internal fun AvatarRowRtlPreview(@PreviewParameter(OverlapRatioPreviewParam::class) overlapRatio: Float) { CompositionLocalProvider( LocalLayoutDirection provides LayoutDirection.Rtl, ) { @@ -141,7 +141,7 @@ internal fun AvatarRowRtlPreview(@PreviewParameter(OverlapRatioProvider::class) @Composable @PreviewsDayNight -internal fun AvatarRowLastOnTopRtlPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) { +internal fun AvatarRowLastOnTopRtlPreview(@PreviewParameter(OverlapRatioPreviewParam::class) overlapRatio: Float) { CompositionLocalProvider( LocalLayoutDirection provides LayoutDirection.Rtl, ) { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/OverlapRatioProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/OverlapRatioPreviewParam.kt similarity index 88% rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/OverlapRatioProvider.kt rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/OverlapRatioPreviewParam.kt index 26a76c2fac..90f9c7c1c2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/OverlapRatioProvider.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/OverlapRatioPreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.designsystem.components.avatar.internal import androidx.compose.ui.tooling.preview.PreviewParameterProvider -class OverlapRatioProvider : PreviewParameterProvider { +class OverlapRatioPreviewParam : PreviewParameterProvider { override val values: Sequence = sequenceOf( 0f, 0.25f, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/ImageVectorProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/ImageVectorPreviewParam.kt similarity index 89% rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/ImageVectorProvider.kt rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/ImageVectorPreviewParam.kt index c1bf2b9112..530452cd60 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/ImageVectorProvider.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/ImageVectorPreviewParam.kt @@ -13,7 +13,7 @@ import androidx.compose.material.icons.filled.BugReport import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class ImageVectorProvider : PreviewParameterProvider { +open class ImageVectorPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( Icons.Default.BugReport, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt index 59e818c18a..5a2e3518f2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt @@ -86,7 +86,7 @@ private fun PreferenceIcon( @Preview(group = PreviewGroup.Preferences) @Composable -internal fun PreferenceIconPreview(@PreviewParameter(ImageVectorProvider::class) content: ImageVector?) = +internal fun PreferenceIconPreview(@PreviewParameter(ImageVectorPreviewParam::class) content: ImageVector?) = ElementThemedPreview { PreferenceIcon( icon = content, @@ -96,7 +96,7 @@ internal fun PreferenceIconPreview(@PreviewParameter(ImageVectorProvider::class) @Preview(group = PreviewGroup.Preferences) @Composable -internal fun PreferenceIconWithBadgePreview(@PreviewParameter(ImageVectorProvider::class) content: ImageVector?) = +internal fun PreferenceIconWithBadgePreview(@PreviewParameter(ImageVectorPreviewParam::class) content: ImageVector?) = ElementThemedPreview { PreferenceIcon( icon = content, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/BooleanProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/BooleanPreviewParam.kt similarity index 86% rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/BooleanProvider.kt rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/BooleanPreviewParam.kt index c50e20dd66..05715464b5 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/BooleanProvider.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/BooleanPreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.designsystem.utils import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class BooleanProvider : PreviewParameterProvider { +open class BooleanPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf(true, false) } diff --git a/libraries/emoji/impl/src/main/kotlin/io/element/android/libraries/emoji/impl/picker/DefaultEmojiPickerStateProvider.kt b/libraries/emoji/impl/src/main/kotlin/io/element/android/libraries/emoji/impl/picker/DefaultEmojiPickerStatePreviewParam.kt similarity index 96% rename from libraries/emoji/impl/src/main/kotlin/io/element/android/libraries/emoji/impl/picker/DefaultEmojiPickerStateProvider.kt rename to libraries/emoji/impl/src/main/kotlin/io/element/android/libraries/emoji/impl/picker/DefaultEmojiPickerStatePreviewParam.kt index 964e0f3b78..e649670da4 100644 --- a/libraries/emoji/impl/src/main/kotlin/io/element/android/libraries/emoji/impl/picker/DefaultEmojiPickerStateProvider.kt +++ b/libraries/emoji/impl/src/main/kotlin/io/element/android/libraries/emoji/impl/picker/DefaultEmojiPickerStatePreviewParam.kt @@ -19,7 +19,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -internal class DefaultEmojiPickerStateProvider : PreviewParameterProvider { +internal class DefaultEmojiPickerStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aDefaultEmojiPickerState(), diff --git a/libraries/emoji/impl/src/main/kotlin/io/element/android/libraries/emoji/impl/picker/EmojiPickerView.kt b/libraries/emoji/impl/src/main/kotlin/io/element/android/libraries/emoji/impl/picker/EmojiPickerView.kt index 88faa3a19a..c41cbb3b57 100644 --- a/libraries/emoji/impl/src/main/kotlin/io/element/android/libraries/emoji/impl/picker/EmojiPickerView.kt +++ b/libraries/emoji/impl/src/main/kotlin/io/element/android/libraries/emoji/impl/picker/EmojiPickerView.kt @@ -166,7 +166,7 @@ private fun EmojiResults( @PreviewsDayNight @Composable -internal fun EmojiPickerViewPreview(@PreviewParameter(DefaultEmojiPickerStateProvider::class) state: DefaultEmojiPickerState) = ElementPreview { +internal fun EmojiPickerViewPreview(@PreviewParameter(DefaultEmojiPickerStatePreviewParam::class) state: DefaultEmojiPickerState) = ElementPreview { EmojiPickerView( state = state, onSelectEmoji = {}, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt index ebc8790a8a..38c5732875 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt @@ -132,7 +132,7 @@ data class AttachmentThumbnailInfo( @PreviewsDayNight @Composable -internal fun AttachmentThumbnailPreview(@PreviewParameter(AttachmentThumbnailInfoProvider::class) data: AttachmentThumbnailInfo) = ElementPreview { +internal fun AttachmentThumbnailPreview(@PreviewParameter(AttachmentThumbnailInfoPreviewParam::class) data: AttachmentThumbnailInfo) = ElementPreview { AttachmentThumbnail( info = data, modifier = Modifier diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnailInfoProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnailInfoPreviewParam.kt similarity index 94% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnailInfoProvider.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnailInfoPreviewParam.kt index ea38a979ae..1509ac2144 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnailInfoProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnailInfoPreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.libraries.matrix.ui.components import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.matrix.api.media.MediaSource -open class AttachmentThumbnailInfoProvider : PreviewParameterProvider { +open class AttachmentThumbnailInfoPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAttachmentThumbnailInfo(type = AttachmentThumbnailType.Image), diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CreateDmConfirmationBottomSheet.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CreateDmConfirmationBottomSheet.kt index 598d7f2ca7..45835ed479 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CreateDmConfirmationBottomSheet.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CreateDmConfirmationBottomSheet.kt @@ -158,7 +158,7 @@ fun CreateDmConfirmationBottomSheet( @Composable internal fun CreateDmConfirmationBottomSheetPreview( @PreviewParameter( - CreateDmConfirmationBottomSheetStateProvider::class + CreateDmConfirmationBottomSheetStatePreviewParam::class ) state: CreateDmConfirmationBottomSheetState ) = ElementPreview(fillMaxSize = true) { CreateDmConfirmationBottomSheet( @@ -174,7 +174,7 @@ data class CreateDmConfirmationBottomSheetState( val isUserIdentityUnknown: Boolean, ) -class CreateDmConfirmationBottomSheetStateProvider : PreviewParameterProvider { +class CreateDmConfirmationBottomSheetStatePreviewParam : PreviewParameterProvider { override val values = sequenceOf( CreateDmConfirmationBottomSheetState(matrixUser = aMatrixUser(), isUserIdentityUnknown = false), CreateDmConfirmationBottomSheetState(matrixUser = aMatrixUser(), isUserIdentityUnknown = true), diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt index 89b0532580..188a8bc1a4 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt @@ -79,6 +79,6 @@ fun MatrixUserHeader( @PreviewsDayNight @Composable -internal fun MatrixUserHeaderPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreview { +internal fun MatrixUserHeaderPreview(@PreviewParameter(MatrixUserPreviewParam::class) matrixUser: MatrixUser) = ElementPreview { MatrixUserHeader(matrixUser) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserPreviewParam.kt similarity index 94% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserPreviewParam.kt index 7bc7d00f09..034570310e 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserPreviewParam.kt @@ -24,7 +24,7 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.api.user.MatrixUser -open class MatrixUserProvider : PreviewParameterProvider { +open class MatrixUserPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMatrixUser(), @@ -33,7 +33,7 @@ open class MatrixUserProvider : PreviewParameterProvider { ) } -open class MatrixUserWithAvatarProvider : PreviewParameterProvider { +open class MatrixUserWithAvatarPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMatrixUser(displayName = USER_NAME_JOHN_DOE), diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt index 9fc7ea7b4a..2a202486a3 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt @@ -46,6 +46,6 @@ fun MatrixUserRow( @PreviewsDayNight @Composable -internal fun MatrixUserRowPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreview { +internal fun MatrixUserRowPreview(@PreviewParameter(MatrixUserPreviewParam::class) matrixUser: MatrixUser) = ElementPreview { MatrixUserRow(matrixUser) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectRoomInfoProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectRoomInfoPreviewParam.kt similarity index 95% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectRoomInfoProvider.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectRoomInfoPreviewParam.kt index c6a4b9d474..e205bac1dd 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectRoomInfoProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectRoomInfoPreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.matrix.ui.model.SelectRoomInfo import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -class SelectRoomInfoProvider : PreviewParameterProvider { +class SelectRoomInfoPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSelectRoomInfo(roomId = RoomId("!room1:domain")), diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt index b7484903da..9ebaa5b22c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt @@ -51,7 +51,7 @@ fun SelectedRoom( @PreviewsDayNight @Composable internal fun SelectedRoomPreview( - @PreviewParameter(SelectRoomInfoProvider::class) roomInfo: SelectRoomInfo + @PreviewParameter(SelectRoomInfoPreviewParam::class) roomInfo: SelectRoomInfo ) = ElementPreview { SelectedRoom( roomInfo = roomInfo, @@ -62,7 +62,7 @@ internal fun SelectedRoomPreview( @PreviewsDayNight @Composable internal fun SelectedRoomRtlPreview( - @PreviewParameter(SelectRoomInfoProvider::class) roomInfo: SelectRoomInfo + @PreviewParameter(SelectRoomInfoPreviewParam::class) roomInfo: SelectRoomInfo ) = CompositionLocalProvider( LocalLayoutDirection provides LayoutDirection.Rtl, ) { diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt index 17ec7c6ec7..ce8c047cef 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt @@ -44,7 +44,7 @@ fun SelectedUser( @PreviewsDayNight @Composable -internal fun SelectedUserPreview(@PreviewParameter(MatrixUserWithAvatarProvider::class) user: MatrixUser) = ElementPreview { +internal fun SelectedUserPreview(@PreviewParameter(MatrixUserWithAvatarPreviewParam::class) user: MatrixUser) = ElementPreview { SelectedUser( matrixUser = user, canRemove = true, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomItemView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomItemView.kt index d3824f9eec..d10698662f 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomItemView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomItemView.kt @@ -210,7 +210,7 @@ private fun SpaceRoomItemScaffold( @Composable @PreviewsDayNight -internal fun SpaceRoomItemViewPreview(@PreviewParameter(SpaceRoomProvider::class) spaceRoom: SpaceRoom) = ElementPreview { +internal fun SpaceRoomItemViewPreview(@PreviewParameter(SpaceRoomPreviewParam::class) spaceRoom: SpaceRoom) = ElementPreview { SpaceRoomItemView( spaceRoom = spaceRoom, showUnreadIndicator = spaceRoom.state == CurrentUserMembership.INVITED, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomPreviewParam.kt similarity index 97% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomProvider.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomPreviewParam.kt index d6a582d83c..0eddafd9da 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomPreviewParam.kt @@ -17,7 +17,7 @@ import io.element.android.libraries.matrix.api.room.RoomType import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.previewutils.room.aSpaceRoom -class SpaceRoomProvider : PreviewParameterProvider { +class SpaceRoomPreviewParam : PreviewParameterProvider { override val values: Sequence = sequenceOf( aSpaceRoom( roomType = RoomType.Room, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsPreviewParam.kt similarity index 95% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsPreviewParam.kt index b335ef9609..4791c5397e 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsPreviewParam.kt @@ -35,7 +35,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageT import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf -open class InReplyToDetailsProvider : PreviewParameterProvider { +open class InReplyToDetailsPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMessageContent( @@ -99,7 +99,7 @@ open class InReplyToDetailsProvider : PreviewParameterProvider } } -class InReplyToDetailsDisambiguatedProvider : InReplyToDetailsProvider() { +class InReplyToDetailsDisambiguatedPreviewParam : InReplyToDetailsPreviewParam() { override val values: Sequence get() = sequenceOf( aMessageContent( @@ -114,7 +114,7 @@ class InReplyToDetailsDisambiguatedProvider : InReplyToDetailsProvider() { } } -class InReplyToDetailsInformativeProvider : InReplyToDetailsProvider() { +class InReplyToDetailsInformativePreviewParam : InReplyToDetailsPreviewParam() { override val values: Sequence get() = sequenceOf( RedactedContent, @@ -126,7 +126,7 @@ class InReplyToDetailsInformativeProvider : InReplyToDetailsProvider() { } } -class InReplyToDetailsOtherProvider : InReplyToDetailsProvider() { +class InReplyToDetailsOtherPreviewParam : InReplyToDetailsPreviewParam() { override val values: Sequence get() = sequenceOf( InReplyToDetails.Loading(eventId = EventId("\$anEventId")), diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToView.kt index 45a9ef2427..8cdc3ae24c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToView.kt @@ -236,7 +236,7 @@ private fun ReplyToNotFoundContent( @PreviewsDayNight @Composable -internal fun InReplyToViewPreview(@PreviewParameter(provider = InReplyToDetailsProvider::class) inReplyTo: InReplyToDetails) = ElementPreview { +internal fun InReplyToViewPreview(@PreviewParameter(provider = InReplyToDetailsPreviewParam::class) inReplyTo: InReplyToDetails) = ElementPreview { InReplyToView( inReplyTo = inReplyTo, hideImage = false, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderName.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderName.kt index 42432c6950..69db0ad8c2 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderName.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderName.kt @@ -134,7 +134,7 @@ private fun RowScope.SecondaryText( @PreviewsDayNight @Composable internal fun SenderNamePreview( - @PreviewParameter(SenderNameDataProvider::class) senderNameData: SenderNameData, + @PreviewParameter(SenderNameDataPreviewParam::class) senderNameData: SenderNameData, ) = ElementPreview { SenderName( senderId = senderNameData.userId, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameDataProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameDataPreviewParam.kt similarity index 96% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameDataProvider.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameDataPreviewParam.kt index e4ca73e69c..3b20643340 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameDataProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameDataPreviewParam.kt @@ -20,7 +20,7 @@ data class SenderNameData( val senderNameMode: SenderNameMode, ) -open class SenderNameDataProvider : PreviewParameterProvider { +open class SenderNameDataPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( SenderNameMode.Timeline(mainColor = Color.Red), diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/LoadingRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/LoadingRoomState.kt index cbbe79b480..cdd1475641 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/LoadingRoomState.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/LoadingRoomState.kt @@ -30,7 +30,7 @@ sealed interface LoadingRoomState { data class Loaded(val room: JoinedRoom) : LoadingRoomState } -open class LoadingRoomStateProvider : PreviewParameterProvider { +open class LoadingRoomStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( LoadingRoomState.Loading, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDeleteConfirmationProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDeleteConfirmationPreviewParam.kt similarity index 90% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDeleteConfirmationProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDeleteConfirmationPreviewParam.kt index 72b16c4e1d..449ab1c5c3 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDeleteConfirmationProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDeleteConfirmationPreviewParam.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.mediaviewer.api.MediaInfo import io.element.android.libraries.mediaviewer.api.anImageMediaInfo -open class MediaBottomSheetStateDeleteConfirmationProvider : PreviewParameterProvider { +open class MediaBottomSheetStateDeleteConfirmationPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMediaBottomSheetStateDeleteConfirmation(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsPreviewParam.kt similarity index 93% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsPreviewParam.kt index 7540d49b8a..a38c5c2ac9 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsPreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.mediaviewer.api.MediaInfo import io.element.android.libraries.mediaviewer.api.anApkMediaInfo import io.element.android.libraries.mediaviewer.api.anImageMediaInfo -open class MediaBottomSheetStateDetailsProvider : PreviewParameterProvider { +open class MediaBottomSheetStateDetailsPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMediaBottomSheetStateDetails(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheet.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheet.kt index 38bbb4dcf0..6aff6cf136 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheet.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheet.kt @@ -168,7 +168,7 @@ private fun MediaRow( @PreviewsDayNight @Composable internal fun MediaDeleteConfirmationBottomSheetPreview( - @PreviewParameter(provider = MediaBottomSheetStateDeleteConfirmationProvider::class) state: MediaBottomSheetState.DeleteConfirmation, + @PreviewParameter(provider = MediaBottomSheetStateDeleteConfirmationPreviewParam::class) state: MediaBottomSheetState.DeleteConfirmation, ) = ElementPreview(fillMaxSize = true) { MediaDeleteConfirmationBottomSheet( state = state, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt index 35d056dc39..f4b35a78ff 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt @@ -266,7 +266,7 @@ private fun SectionText( @PreviewsDayNight @Composable internal fun MediaDetailsBottomSheetPreview( - @PreviewParameter(MediaBottomSheetStateDetailsProvider::class) state: MediaBottomSheetState.Details, + @PreviewParameter(MediaBottomSheetStateDetailsPreviewParam::class) state: MediaBottomSheetState.Details, ) = ElementPreview(fillMaxSize = true) { MediaDetailsBottomSheet( state = state, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryStatePreviewParam.kt similarity index 98% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryStateProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryStatePreviewParam.kt index 43a361ac29..6d90fff26a 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryStatePreviewParam.kt @@ -28,7 +28,7 @@ import io.element.android.libraries.mediaviewer.impl.model.aMediaItemVideo import io.element.android.libraries.mediaviewer.impl.model.aMediaItemVoice import kotlinx.collections.immutable.toImmutableList -open class MediaGalleryStateProvider : PreviewParameterProvider { +open class MediaGalleryStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMediaGalleryState( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryView.kt index 441b6082dd..132fb4af91 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryView.kt @@ -597,7 +597,7 @@ private fun LoadingContent( @PreviewsDayNight @Composable internal fun MediaGalleryViewPreview( - @PreviewParameter(MediaGalleryStateProvider::class) state: MediaGalleryState + @PreviewParameter(MediaGalleryStatePreviewParam::class) state: MediaGalleryState ) = ElementPreview { CompositionLocalProvider( LocalMediaItemPresenterFactories provides aFakeMediaItemPresenterFactories(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt index cda8422e33..36d1c2de35 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt @@ -46,7 +46,7 @@ fun AudioItemView( @PreviewsDayNight @Composable internal fun AudioItemViewPreview( - @PreviewParameter(MediaItemAudioProvider::class) audio: MediaItem.Audio, + @PreviewParameter(MediaItemAudioPreviewParam::class) audio: MediaItem.Audio, ) = ElementPreview { val states = remember { listOf( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/DateItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/DateItemView.kt index b04b25a53e..8f86c86523 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/DateItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/DateItemView.kt @@ -45,7 +45,7 @@ fun DateItemView( @PreviewsDayNight @Composable internal fun DateItemViewPreview( - @PreviewParameter(MediaItemDateSeparatorProvider::class) date: MediaItem.DateSeparator, + @PreviewParameter(MediaItemDateSeparatorPreviewParam::class) date: MediaItem.DateSeparator, ) = ElementPreview { DateItemView(date) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt index b552c374b9..1919eea459 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt @@ -46,7 +46,7 @@ fun FileItemView( @PreviewsDayNight @Composable internal fun FileItemViewPreview( - @PreviewParameter(MediaItemFileProvider::class) file: MediaItem.File, + @PreviewParameter(MediaItemFilePreviewParam::class) file: MediaItem.File, ) = ElementPreview { val states = remember { listOf( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemAudioProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemAudioPreviewParam.kt similarity index 92% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemAudioProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemAudioPreviewParam.kt index 4dfd1c18c8..7d5d39c9bd 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemAudioProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemAudioPreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.core.preview.loremIpsum import io.element.android.libraries.mediaviewer.impl.model.MediaItem import io.element.android.libraries.mediaviewer.impl.model.aMediaItemAudio -class MediaItemAudioProvider : PreviewParameterProvider { +class MediaItemAudioPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMediaItemAudio(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemDateSeparatorProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemDateSeparatorPreviewParam.kt similarity index 88% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemDateSeparatorProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemDateSeparatorPreviewParam.kt index 05705a9251..914df563cf 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemDateSeparatorProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemDateSeparatorPreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.mediaviewer.impl.model.MediaItem import io.element.android.libraries.mediaviewer.impl.model.aMediaItemDateSeparator -class MediaItemDateSeparatorProvider : PreviewParameterProvider { +class MediaItemDateSeparatorPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMediaItemDateSeparator(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemFileProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemFilePreviewParam.kt similarity index 92% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemFileProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemFilePreviewParam.kt index e64b10adb4..f0bd4c4383 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemFileProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemFilePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.core.preview.loremIpsum import io.element.android.libraries.mediaviewer.impl.model.MediaItem import io.element.android.libraries.mediaviewer.impl.model.aMediaItemFile -class MediaItemFileProvider : PreviewParameterProvider { +class MediaItemFilePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMediaItemFile(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVideoProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVideoPreviewParam.kt similarity index 90% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVideoProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVideoPreviewParam.kt index c9c4666904..e3e4017e8d 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVideoProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVideoPreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.mediaviewer.impl.model.MediaItem import io.element.android.libraries.mediaviewer.impl.model.aMediaItemVideo -class MediaItemVideoProvider : PreviewParameterProvider { +class MediaItemVideoPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMediaItemVideo(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVoiceProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVoicePreviewParam.kt similarity index 92% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVoiceProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVoicePreviewParam.kt index 56c1c4bb32..4f0ec2f3fa 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVoiceProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/MediaItemVoicePreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.core.preview.loremIpsum import io.element.android.libraries.mediaviewer.impl.model.MediaItem import io.element.android.libraries.mediaviewer.impl.model.aMediaItemVoice -class MediaItemVoiceProvider : PreviewParameterProvider { +class MediaItemVoicePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMediaItemVoice(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt index 3aad3f1061..01d1e29f08 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VideoItemView.kt @@ -117,7 +117,7 @@ private fun VideoInfoRow( @PreviewsDayNight @Composable internal fun VideoItemViewPreview( - @PreviewParameter(MediaItemVideoProvider::class) video: MediaItem.Video, + @PreviewParameter(MediaItemVideoPreviewParam::class) video: MediaItem.Video, ) = ElementPreview { VideoItemView( video = video, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt index 6c9df5fc48..2ffaed7af0 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt @@ -54,7 +54,7 @@ import io.element.android.libraries.mediaviewer.impl.model.aMediaItemVoice import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.voiceplayer.api.VoiceMessageEvent import io.element.android.libraries.voiceplayer.api.VoiceMessageState -import io.element.android.libraries.voiceplayer.api.VoiceMessageStateProvider +import io.element.android.libraries.voiceplayer.api.VoiceMessageStatePreviewParam import io.element.android.libraries.voiceplayer.api.aVoiceMessageState import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.delay @@ -279,7 +279,7 @@ private fun CustomIconButton( @PreviewsDayNight @Composable internal fun VoiceItemViewPreview( - @PreviewParameter(MediaItemVoiceProvider::class) voice: MediaItem.Voice, + @PreviewParameter(MediaItemVoicePreviewParam::class) voice: MediaItem.Voice, ) = ElementPreview { val states = remember { listOf( @@ -304,7 +304,7 @@ internal fun VoiceItemViewPreview( @PreviewsDayNight @Composable internal fun VoiceItemViewPlayPreview( - @PreviewParameter(VoiceMessageStateProvider::class) state: VoiceMessageState, + @PreviewParameter(VoiceMessageStatePreviewParam::class) state: VoiceMessageState, ) = ElementPreview { val validationState = remember { listOf( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt index 3ac578196d..4ef746cb89 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt @@ -393,7 +393,7 @@ private fun AudioInfoView( @PreviewsDayNight @Composable internal fun MediaAudioViewPreview( - @PreviewParameter(MediaInfoAudioProvider::class) info: MediaInfo + @PreviewParameter(MediaInfoAudioPreviewParam::class) info: MediaInfo ) = ElementPreview { MediaAudioView( modifier = Modifier.fillMaxSize(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaInfoAudioProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaInfoAudioPreviewParam.kt similarity index 91% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaInfoAudioProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaInfoAudioPreviewParam.kt index 02607e3e0e..900321a909 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaInfoAudioProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaInfoAudioPreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.designsystem.components.media.WaveFormSample import io.element.android.libraries.mediaviewer.api.MediaInfo import io.element.android.libraries.mediaviewer.api.anAudioMediaInfo -open class MediaInfoAudioProvider : PreviewParameterProvider { +open class MediaInfoAudioPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAudioMediaInfo(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaFileView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaFileView.kt index a1c57ef272..89e02b616a 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaFileView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaFileView.kt @@ -114,7 +114,7 @@ fun MediaFileView( @PreviewsDayNight @Composable internal fun MediaFileViewPreview( - @PreviewParameter(MediaInfoFileProvider::class) info: MediaInfo + @PreviewParameter(MediaInfoFilePreviewParam::class) info: MediaInfo ) = ElementPreview { MediaFileView( modifier = Modifier.fillMaxSize(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaInfoFileProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaInfoFilePreviewParam.kt similarity index 91% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaInfoFileProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaInfoFilePreviewParam.kt index 3214236253..b647eb8fd2 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaInfoFileProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaInfoFilePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.mediaviewer.api.aZipMediaInfo import io.element.android.libraries.mediaviewer.api.anApkMediaInfo import io.element.android.libraries.mediaviewer.api.anAudioMediaInfo -open class MediaInfoFileProvider : PreviewParameterProvider { +open class MediaInfoFilePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aZipMediaInfo(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerStatePreviewParam.kt similarity index 92% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerStateProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerStatePreviewParam.kt index 55432fb817..b229d11283 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.mediaviewer.impl.local.player import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class MediaPlayerControllerStateProvider : PreviewParameterProvider { +open class MediaPlayerControllerStatePreviewParam : PreviewParameterProvider { override val values: Sequence = sequenceOf( aMediaPlayerControllerState(), aMediaPlayerControllerState( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt index 1f2b0b93d2..0a68a2d75d 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt @@ -213,7 +213,7 @@ fun MediaPlayerControllerView( @PreviewsDayNight @Composable internal fun MediaPlayerControllerViewPreview( - @PreviewParameter(MediaPlayerControllerStateProvider::class) state: MediaPlayerControllerState + @PreviewParameter(MediaPlayerControllerStatePreviewParam::class) state: MediaPlayerControllerState ) = ElementPreview { MediaPlayerControllerView( state = state, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileContentProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileContentPreviewParam.kt similarity index 89% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileContentProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileContentPreviewParam.kt index d92e400495..108f8dd066 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileContentProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileContentPreviewParam.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.architecture.AsyncData import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -open class TextFileContentProvider : PreviewParameterProvider>> { +open class TextFileContentPreviewParam : PreviewParameterProvider>> { override val values: Sequence>> get() = sequenceOf( AsyncData.Uninitialized, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileView.kt index f4984470dd..2f397103e8 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileView.kt @@ -98,7 +98,7 @@ private fun TextFileContentView( @PreviewsDayNight @Composable internal fun TextFileContentViewPreview( - @PreviewParameter(TextFileContentProvider::class) text: AsyncData>, + @PreviewParameter(TextFileContentPreviewParam::class) text: AsyncData>, ) = ElementPreview { TextFileContentView( data = text, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStatePreviewParam.kt similarity index 99% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStatePreviewParam.kt index 440d366c30..ea422e41f1 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStatePreviewParam.kt @@ -41,7 +41,7 @@ private const val LONG_CAPTION = "This is a very long caption that should be scr "Line 4: Duis aute irure dolor in reprehenderit in voluptate velit esse cillum. " + "Line 5: Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia." -open class MediaViewerStateProvider : PreviewParameterProvider { +open class MediaViewerStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMediaViewerState(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index e1c1a0c250..343bf99ec9 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -749,7 +749,7 @@ private fun ErrorView( // Only preview in dark, dark theme is forced on the Node. @Preview @Composable -internal fun MediaViewerViewPreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) = ElementPreviewDark { +internal fun MediaViewerViewPreview(@PreviewParameter(MediaViewerStatePreviewParam::class) state: MediaViewerState) = ElementPreviewDark { MediaViewerView( state = state, audioFocus = null, @@ -760,7 +760,7 @@ internal fun MediaViewerViewPreview(@PreviewParameter(MediaViewerStateProvider:: @Preview(device = "${Devices.PHONE}, orientation=landscape") @Composable -internal fun MediaViewerViewLandscapePreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) = ElementPreviewDark { +internal fun MediaViewerViewLandscapePreview(@PreviewParameter(MediaViewerStatePreviewParam::class) state: MediaViewerState) = ElementPreviewDark { MediaViewerView( state = state, audioFocus = null, diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStateProvider.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStatePreviewParam.kt similarity index 93% rename from libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStateProvider.kt rename to libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStatePreviewParam.kt index a330333d17..9052667578 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStateProvider.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.libraries.permissions.api import android.Manifest import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class PermissionsStateProvider : PreviewParameterProvider { +open class PermissionsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aPermissionsState(showDialog = true, permission = Manifest.permission.POST_NOTIFICATIONS), diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt index af0f3dd5e8..7a641b1cfd 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt @@ -55,7 +55,7 @@ private fun String.toDialogContent(): String { @PreviewsDayNight @Composable -internal fun PermissionsViewPreview(@PreviewParameter(PermissionsStateProvider::class) state: PermissionsState) = ElementPreview { +internal fun PermissionsViewPreview(@PreviewParameter(PermissionsStatePreviewParam::class) state: PermissionsState) = ElementPreview { PermissionsView( state = state, ) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStatePreviewParam.kt similarity index 97% rename from libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt rename to libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStatePreviewParam.kt index c7fa907be2..41fbf93532 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStatePreviewParam.kt @@ -20,7 +20,7 @@ import io.element.android.libraries.roomselect.api.RoomSelectMode import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -open class RoomSelectStateProvider : PreviewParameterProvider { +open class RoomSelectStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomSelectState(), diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index 357d86dd5a..43b91068f9 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -296,7 +296,7 @@ private fun RoomSummaryView( @PreviewsDayNight @Composable -internal fun RoomSelectViewPreview(@PreviewParameter(RoomSelectStateProvider::class) state: RoomSelectState) = ElementPreview { +internal fun RoomSelectViewPreview(@PreviewParameter(RoomSelectStatePreviewParam::class) state: RoomSelectState) = ElementPreview { RoomSelectView( state = state, onDismiss = {}, diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ComposerModeView.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ComposerModeView.kt index 9b2260d441..e4a1dbf5a1 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ComposerModeView.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ComposerModeView.kt @@ -192,7 +192,7 @@ private fun ReplyToModeView( @PreviewsDayNight @Composable internal fun ComposerModeViewPreview( - @PreviewParameter(MessageComposerModeSpecialProvider::class) mode: MessageComposerMode.Special + @PreviewParameter(MessageComposerModeSpecialPreviewParam::class) mode: MessageComposerMode.Special ) = ElementPreview { ComposerModeView( composerMode = mode, diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerModeSpecialProvider.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerModeSpecialPreviewParam.kt similarity index 74% rename from libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerModeSpecialProvider.kt rename to libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerModeSpecialPreviewParam.kt index 60850d26c2..2fd367190e 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerModeSpecialProvider.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerModeSpecialPreviewParam.kt @@ -9,15 +9,15 @@ package io.element.android.libraries.textcomposer import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsProvider +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsPreviewParam import io.element.android.libraries.textcomposer.model.MessageComposerMode -class MessageComposerModeSpecialProvider : PreviewParameterProvider { +class MessageComposerModeSpecialPreviewParam : PreviewParameterProvider { override val values: Sequence = sequenceOf( aMessageComposerModeEdit() ) + - // Keep only 3 values from InReplyToDetailsProvider - InReplyToDetailsProvider().values.take(3).map { + // Keep only 3 values from InReplyToDetailsPreviewParam + InReplyToDetailsPreviewParam().values.take(3).map { aMessageComposerModeReply( replyToDetails = it ) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 66c9dcba03..6a10ece336 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -76,7 +76,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageConten import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails -import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsProvider +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsPreviewParam import io.element.android.libraries.matrix.ui.messages.reply.aProfileDetailsReady import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag @@ -859,7 +859,7 @@ internal fun MarkdownTextComposerEditPreview() = ElementPreview { @PreviewsDayNight @Composable -internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails) = ElementPreview { +internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsPreviewParam::class) inReplyToDetails: InReplyToDetails) = ElementPreview { PreviewColumn( items = aTextEditorStateRichList() ) { textEditorState -> @@ -883,7 +883,7 @@ internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider heightDp = 800, ) @Composable -internal fun TextComposerReplyNotEncryptedPreview(@PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails) = ElementPreview { +internal fun TextComposerReplyNotEncryptedPreview(@PreviewParameter(InReplyToDetailsPreviewParam::class) inReplyToDetails: InReplyToDetails) = ElementPreview { PreviewColumn( items = aTextEditorStateRichList(isRoomEncrypted = false) ) { textEditorState -> diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsStateProvider.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsStatePreviewParam.kt similarity index 97% rename from libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsStateProvider.kt rename to libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsStatePreviewParam.kt index 5cc565559e..54dd40617f 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsStateProvider.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsStatePreviewParam.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState import kotlinx.collections.immutable.toImmutableList -open class TroubleshootNotificationsStateProvider : PreviewParameterProvider { +open class TroubleshootNotificationsStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aTroubleshootNotificationsState( diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsView.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsView.kt index 4e0f89155d..d168cfd764 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsView.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsView.kt @@ -207,7 +207,7 @@ private fun ColumnScope.TestSuiteView( @PreviewsDayNight @Composable internal fun TroubleshootNotificationsViewPreview( - @PreviewParameter(TroubleshootNotificationsStateProvider::class) state: TroubleshootNotificationsState, + @PreviewParameter(TroubleshootNotificationsStatePreviewParam::class) state: TroubleshootNotificationsState, ) = ElementPreview { TroubleshootNotificationsView( state = state, diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStateProvider.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStatePreviewParam.kt similarity index 97% rename from libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStateProvider.kt rename to libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStatePreviewParam.kt index e7384048f4..968de5534c 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStateProvider.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStatePreviewParam.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.push.api.history.PushHistoryItem import kotlinx.collections.immutable.toImmutableList -open class PushHistoryStateProvider : PreviewParameterProvider { +open class PushHistoryStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aPushHistoryState(), diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryView.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryView.kt index 21e9c64139..7315cb4e5a 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryView.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryView.kt @@ -268,7 +268,7 @@ private fun PushHistoryItem( @PreviewsDayNight @Composable internal fun PushHistoryViewPreview( - @PreviewParameter(PushHistoryStateProvider::class) state: PushHistoryState, + @PreviewParameter(PushHistoryStatePreviewParam::class) state: PushHistoryState, ) = ElementPreview { PushHistoryView( state = state, diff --git a/libraries/voiceplayer/api/src/main/kotlin/io/element/android/libraries/voiceplayer/api/VoiceMessageStateProvider.kt b/libraries/voiceplayer/api/src/main/kotlin/io/element/android/libraries/voiceplayer/api/VoiceMessageStatePreviewParam.kt similarity index 95% rename from libraries/voiceplayer/api/src/main/kotlin/io/element/android/libraries/voiceplayer/api/VoiceMessageStateProvider.kt rename to libraries/voiceplayer/api/src/main/kotlin/io/element/android/libraries/voiceplayer/api/VoiceMessageStatePreviewParam.kt index dd8d222e69..f417947cb6 100644 --- a/libraries/voiceplayer/api/src/main/kotlin/io/element/android/libraries/voiceplayer/api/VoiceMessageStateProvider.kt +++ b/libraries/voiceplayer/api/src/main/kotlin/io/element/android/libraries/voiceplayer/api/VoiceMessageStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.voiceplayer.api import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class VoiceMessageStateProvider : PreviewParameterProvider { +open class VoiceMessageStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aVoiceMessageState( diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt index 93b3200acc..202432c6d8 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt @@ -57,16 +57,16 @@ class KonsistClassNameTest { } @Test - fun `Classes extending 'PreviewParameterProvider' name MUST end with 'Provider' and MUST contain provided class name`() { + fun `Classes extending 'PreviewParameterProvider' name MUST end with 'PreviewParam' and MUST contain provided class name`() { Konsist.scopeFromProduction() .classes() .withAllParentsOf(PreviewParameterProvider::class) .withoutName( - "AspectRatioProvider", - "EditableAvatarViewUriProvider", - "LoginModeViewErrorProvider", - "OverlapRatioProvider", - "TextFileContentProvider", + "AspectRatioPreviewParam", + "EditableAvatarViewUriPreviewParam", + "LoginModeViewErrorPreviewParam", + "OverlapRatioPreviewParam", + "TextFileContentPreviewParam", ) .also { // Check that classes are actually found @@ -82,8 +82,7 @@ class KonsistClassNameTest { .removeSuffix("?") .replace(".", "") val name = klass.name - name.endsWith("Provider") && - name.endsWith("PreviewProvider").not() && + name.endsWith("PreviewParam") && name.contains(providedType) } } diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.0.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.0.kt index 26372fc970..9230360bd9 100644 --- a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.0.kt +++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.0.kt @@ -2,7 +2,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider -open class ${NAME}StateProvider : PreviewParameterProvider<${NAME}State> { +open class ${NAME}StatePreviewParam : PreviewParameterProvider<${NAME}State> { override val values: Sequence<${NAME}State> get() = sequenceOf( a${NAME}State(), diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.2.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.2.kt index c4f3b1b5b2..83e1b5c01f 100644 --- a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.2.kt +++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.2.kt @@ -27,7 +27,7 @@ fun ${NAME}View( @PreviewsDayNight @Composable internal fun ${NAME}ViewPreview( - @PreviewParameter(${NAME}StateProvider::class) state: ${NAME}State + @PreviewParameter(${NAME}StatePreviewParam::class) state: ${NAME}State ) = ElementPreview { ${NAME}View( state = state, diff --git a/tools/templates/files/options/file.template.settings.xml b/tools/templates/files/options/file.template.settings.xml index a284c63140..b45a92f4c2 100644 --- a/tools/templates/files/options/file.template.settings.xml +++ b/tools/templates/files/options/file.template.settings.xml @@ -2,13 +2,13 @@