From f7a3f707fb3ac130af69ebca4f0c64db29ea1d27 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 1 Oct 2024 14:40:53 +0200 Subject: [PATCH 01/67] Improve mapping regarding filename, caption and formattedCaption --- .../messages/impl/MessagesFlowNode.kt | 16 +-- .../impl/actionlist/ActionListView.kt | 10 +- .../components/TimelineItemEventRow.kt | 2 +- ...imelineItemEventRowForDirectRoomPreview.kt | 2 +- .../TimelineItemEventRowShieldPreview.kt | 4 +- .../TimelineItemEventRowWithReplyPreview.kt | 2 +- .../components/event/TimelineItemAudioView.kt | 2 +- .../components/event/TimelineItemFileView.kt | 2 +- .../components/event/TimelineItemImageView.kt | 14 +-- .../event/TimelineItemStickerView.kt | 4 +- .../components/event/TimelineItemVideoView.kt | 18 ++-- .../TimelineItemContentMessageFactory.kt | 40 ++++--- .../TimelineItemContentStickerFactory.kt | 6 +- .../model/event/TimelineItemAudioContent.kt | 7 +- .../event/TimelineItemAudioContentProvider.kt | 6 +- .../model/event/TimelineItemEventContent.kt | 11 ++ .../model/event/TimelineItemFileContent.kt | 7 +- .../event/TimelineItemFileContentProvider.kt | 8 +- .../model/event/TimelineItemImageContent.kt | 11 +- .../event/TimelineItemImageContentProvider.kt | 10 +- .../model/event/TimelineItemStickerContent.kt | 7 +- .../TimelineItemStickerContentProvider.kt | 4 +- .../model/event/TimelineItemVideoContent.kt | 11 +- .../event/TimelineItemVideoContentProvider.kt | 6 +- .../model/event/TimelineItemVoiceContent.kt | 7 +- .../event/TimelineItemVoiceContentProvider.kt | 14 ++- .../timeline/VoiceMessagePresenter.kt | 2 +- .../messages/impl/MessagesPresenterTest.kt | 16 +-- .../pinned/list/PinnedMessagesListViewTest.kt | 4 +- .../TimelineItemContentMessageFactoryTest.kt | 101 +++++++++++------- .../roomdetails/impl/RoomDetailsFlowNode.kt | 3 +- .../userprofile/impl/UserProfileFlowNode.kt | 5 +- .../DefaultPinnedMessagesBannerFormatter.kt | 15 +-- .../impl/DefaultRoomLastMessageFormatter.kt | 2 +- ...efaultPinnedMessagesBannerFormatterTest.kt | 12 +-- .../DefaultRoomLastMessageFormatterTest.kt | 12 +-- .../api/timeline/item/event/EventContent.kt | 8 +- .../api/timeline/item/event/MessageType.kt | 51 ++++++--- .../timeline/item/event/EventMessageMapper.kt | 32 +++++- .../item/event/TimelineEventContentMapper.kt | 3 +- .../matrix/test/timeline/TimelineFixture.kt | 15 +++ .../matrix/ui/media/CoilMediaFetcher.kt | 2 +- .../matrix/ui/media/MediaRequestData.kt | 7 +- .../reply/InReplyToDetailsProvider.kt | 8 +- .../messages/reply/InReplyToMetadataKtTest.kt | 42 +++++--- .../mediaviewer/api/local/LocalMediaView.kt | 2 +- .../mediaviewer/api/local/MediaInfo.kt | 48 +++++---- .../api/viewer/MediaViewerPresenter.kt | 2 +- .../mediaviewer/api/viewer/MediaViewerView.kt | 2 +- .../impl/local/AndroidLocalMediaActions.kt | 4 +- .../impl/local/AndroidLocalMediaFactory.kt | 36 +++++-- .../local/AndroidLocalMediaFactoryTest.kt | 3 +- .../mediaviewer/test/FakeLocalMediaFactory.kt | 3 +- .../DefaultNotifiableEventResolver.kt | 12 +-- .../notifications/NotificationMediaRepo.kt | 2 +- .../DefaultNotifiableEventResolverTest.kt | 10 +- 56 files changed, 440 insertions(+), 255 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 08b485b1e7..fc3fba9c59 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -50,7 +50,6 @@ import io.element.android.features.poll.api.create.CreatePollMode import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode -import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.overlay.Overlay import io.element.android.libraries.architecture.overlay.operation.show import io.element.android.libraries.di.RoomScope @@ -324,7 +323,8 @@ class MessagesFlowNode @AssistedInject constructor( is TimelineItemImageContent -> { val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( - name = event.content.filename ?: event.content.body, + filename = event.content.filename, + caption = event.content.caption, mimeType = event.content.mimeType, formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension @@ -341,7 +341,8 @@ class MessagesFlowNode @AssistedInject constructor( if (event.content.preferredMediaSource != null) { val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( - name = event.content.body, + filename = event.content.filename, + caption = event.content.caption, mimeType = event.content.mimeType, formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension @@ -358,7 +359,8 @@ class MessagesFlowNode @AssistedInject constructor( is TimelineItemVideoContent -> { val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( - name = event.content.filename ?: event.content.body, + filename = event.content.filename, + caption = event.content.caption, mimeType = event.content.mimeType, formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension @@ -372,7 +374,8 @@ class MessagesFlowNode @AssistedInject constructor( is TimelineItemFileContent -> { val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( - name = event.content.body, + filename = event.content.filename, + caption = event.content.caption, mimeType = event.content.mimeType, formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension @@ -386,7 +389,8 @@ class MessagesFlowNode @AssistedInject constructor( is TimelineItemAudioContent -> { val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( - name = event.content.body, + filename = event.content.filename, + caption = event.content.caption, mimeType = event.content.mimeType, formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension 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 cccfe89409..0b9377ae90 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 @@ -269,19 +269,19 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif content = { ContentForBody(stringResource(CommonStrings.common_shared_location)) } } is TimelineItemImageContent -> { - content = { ContentForBody(event.content.body) } + content = { ContentForBody(event.content.bestDescription) } } is TimelineItemStickerContent -> { - content = { ContentForBody(event.content.body) } + content = { ContentForBody(event.content.bestDescription) } } is TimelineItemVideoContent -> { - content = { ContentForBody(event.content.body) } + content = { ContentForBody(event.content.bestDescription) } } is TimelineItemFileContent -> { - content = { ContentForBody(event.content.body) } + content = { ContentForBody(event.content.bestDescription) } } is TimelineItemAudioContent -> { - content = { ContentForBody(event.content.body) } + content = { ContentForBody(event.content.bestDescription) } } is TimelineItemVoiceContent -> { content = { ContentForBody(textContent) } 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 67342203bd..bc42824d68 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 @@ -629,7 +629,7 @@ internal fun TimelineItemEventRowPreview() = ElementPreview { ATimelineItemEventRow( event = aTimelineItemEvent( isMine = isMine, - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( aspectRatio = 2.5f ), groupPosition = TimelineItemGroupPosition.Last, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowForDirectRoomPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowForDirectRoomPreview.kt index 5bda7bb8d4..cdec04eb05 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowForDirectRoomPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowForDirectRoomPreview.kt @@ -38,7 +38,7 @@ internal fun TimelineItemEventRowForDirectRoomPreview() = ElementPreview { ATimelineItemEventRow( event = aTimelineItemEvent( isMine = it, - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( aspectRatio = 5f ), groupPosition = TimelineItemGroupPosition.Last, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowShieldPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowShieldPreview.kt index a3f943bed3..328741c807 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowShieldPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowShieldPreview.kt @@ -45,7 +45,7 @@ internal fun TimelineItemEventRowShieldPreview() = ElementPreview { ATimelineItemEventRow( event = aTimelineItemEvent( isMine = true, - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( aspectRatio = 2.5f ), groupPosition = TimelineItemGroupPosition.Last, @@ -54,7 +54,7 @@ internal fun TimelineItemEventRowShieldPreview() = ElementPreview { ) ATimelineItemEventRow( event = aTimelineItemEvent( - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( aspectRatio = 2.5f ), groupPosition = TimelineItemGroupPosition.Last, 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 ee13c8ec03..c429e9cc83 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 @@ -49,7 +49,7 @@ internal fun TimelineItemEventRowWithReplyContentToPreview( event = aTimelineItemEvent( isMine = it, timelineItemReactions = aTimelineItemReactions(count = 0), - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( aspectRatio = 2.5f ), inReplyTo = inReplyToDetails, 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 3ea3f33135..23069a1fac 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 @@ -63,7 +63,7 @@ fun TimelineItemAudioView( Spacer(Modifier.width(spacing)) Column { Text( - text = content.body, + text = content.bestDescription, color = ElementTheme.materialColors.primary, maxLines = 2, style = ElementTheme.typography.fontBodyLgRegular, 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 354bfaf2c7..dadfadc299 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 @@ -64,7 +64,7 @@ fun TimelineItemFileView( Spacer(Modifier.width(spacing)) Column { Text( - text = content.body, + text = content.bestDescription, color = ElementTheme.materialColors.primary, maxLines = 2, style = ElementTheme.typography.fontBodyLgRegular, 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 85b2b7f678..fcc24b791e 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 @@ -91,7 +91,7 @@ fun TimelineItemImageView( model = MediaRequestData( source = content.preferredMediaSource, kind = MediaRequestData.Kind.File( - body = content.filename ?: content.body, + fileName = content.filename, mimeType = content.mimeType, ), ), @@ -108,7 +108,9 @@ fun TimelineItemImageView( val caption = if (LocalInspectionMode.current) { SpannedString(content.caption) } else { - content.formatted?.body?.takeIf { content.formatted.format == MessageFormat.HTML } ?: SpannedString(content.caption) + content.formattedCaption?.body + ?.takeIf { content.formattedCaption.format == MessageFormat.HTML } + ?: SpannedString(content.caption) } CompositionLocalProvider( LocalContentColor provides ElementTheme.colors.textPrimary, @@ -158,9 +160,9 @@ internal fun TimelineImageWithCaptionRowPreview() = ElementPreview { ATimelineItemEventRow( event = aTimelineItemEvent( isMine = isMine, - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( filename = "image.jpg", - body = "A long caption that may wrap into several lines", + caption = "A long caption that may wrap into several lines", aspectRatio = 2.5f, ), groupPosition = TimelineItemGroupPosition.Last, @@ -170,9 +172,9 @@ internal fun TimelineImageWithCaptionRowPreview() = ElementPreview { ATimelineItemEventRow( event = aTimelineItemEvent( isMine = false, - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( filename = "image.jpg", - body = "Image with null aspectRatio", + caption = "Image with null aspectRatio", aspectRatio = null, ), groupPosition = TimelineItemGroupPosition.Last, 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 cef5acd1dd..0574843dba 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 @@ -43,7 +43,7 @@ fun TimelineItemStickerView( onShowClick: () -> Unit, modifier: Modifier = Modifier, ) { - val description = content.body.takeIf { it.isNotEmpty() } ?: stringResource(CommonStrings.common_image) + val description = content.bestDescription.takeIf { it.isNotEmpty() } ?: stringResource(CommonStrings.common_image) Column( modifier = modifier.semantics { contentDescription = description }, ) { @@ -65,7 +65,7 @@ fun TimelineItemStickerView( model = MediaRequestData( source = content.preferredMediaSource, kind = MediaRequestData.Kind.File( - body = content.body, + fileName = content.filename, mimeType = content.mimeType, ), ), 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 7815e02610..e743338ccf 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 @@ -76,8 +76,8 @@ fun TimelineItemVideoView( ) { val containerModifier = if (content.showCaption) { Modifier - .padding(top = 6.dp) - .clip(RoundedCornerShape(6.dp)) + .padding(top = 6.dp) + .clip(RoundedCornerShape(6.dp)) } else { Modifier } @@ -93,12 +93,12 @@ fun TimelineItemVideoView( var isLoaded by remember { mutableStateOf(false) } AsyncImage( modifier = Modifier - .fillMaxWidth() - .then(if (isLoaded) Modifier.background(Color.White) else Modifier), + .fillMaxWidth() + .then(if (isLoaded) Modifier.background(Color.White) else Modifier), model = MediaRequestData( source = content.thumbnailSource, kind = MediaRequestData.Kind.File( - body = content.filename ?: content.body, + fileName = content.filename, mimeType = content.mimeType ) ), @@ -126,7 +126,9 @@ fun TimelineItemVideoView( val caption = if (LocalInspectionMode.current) { SpannedString(content.caption) } else { - content.formatted?.body?.takeIf { content.formatted.format == MessageFormat.HTML } ?: SpannedString(content.caption) + content.formattedCaption?.body + ?.takeIf { content.formattedCaption.format == MessageFormat.HTML } + ?: SpannedString(content.caption) } CompositionLocalProvider( LocalContentColor provides ElementTheme.colors.textPrimary, @@ -178,7 +180,7 @@ internal fun TimelineVideoWithCaptionRowPreview() = ElementPreview { isMine = isMine, content = aTimelineItemVideoContent().copy( filename = "video.mp4", - body = "A long caption that may wrap into several lines", + caption = "A long caption that may wrap into several lines", aspectRatio = 2.5f, ), groupPosition = TimelineItemGroupPosition.Last, @@ -190,7 +192,7 @@ internal fun TimelineVideoWithCaptionRowPreview() = ElementPreview { isMine = false, content = aTimelineItemVideoContent().copy( filename = "video.mp4", - body = "Video with null aspect ratio", + caption = "Video with null aspect ratio", aspectRatio = null, ), groupPosition = TimelineItemGroupPosition.Last, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index d171c3dd0f..3eb0c66594 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -84,9 +84,9 @@ class TimelineItemContentMessageFactory @Inject constructor( is ImageMessageType -> { val aspectRatio = aspectRatioOf(messageType.info?.width, messageType.info?.height) TimelineItemImageContent( - body = messageType.body.trimEnd(), - formatted = messageType.formatted, filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, mediaSource = messageType.source, thumbnailSource = messageType.info?.thumbnailSource, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, @@ -95,13 +95,15 @@ class TimelineItemContentMessageFactory @Inject constructor( height = messageType.info?.height?.toInt(), aspectRatio = aspectRatio, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), - fileExtension = messageType.filename?.let { fileExtensionExtractor.extractFromName(it) }.orEmpty() + fileExtension = fileExtensionExtractor.extractFromName(messageType.filename) ) } is StickerMessageType -> { val aspectRatio = aspectRatioOf(messageType.info?.width, messageType.info?.height) TimelineItemStickerContent( - body = messageType.body.trimEnd(), + filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, mediaSource = messageType.source, thumbnailSource = messageType.info?.thumbnailSource, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, @@ -110,7 +112,7 @@ class TimelineItemContentMessageFactory @Inject constructor( height = messageType.info?.height?.toInt(), aspectRatio = aspectRatio, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), - fileExtension = fileExtensionExtractor.extractFromName(messageType.body) + fileExtension = fileExtensionExtractor.extractFromName(messageType.filename) ) } is LocationMessageType -> { @@ -136,9 +138,9 @@ class TimelineItemContentMessageFactory @Inject constructor( is VideoMessageType -> { val aspectRatio = aspectRatioOf(messageType.info?.width, messageType.info?.height) TimelineItemVideoContent( - body = messageType.body.trimEnd(), - formatted = messageType.formatted, filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, thumbnailSource = messageType.info?.thumbnailSource, videoSource = messageType.source, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, @@ -148,17 +150,19 @@ class TimelineItemContentMessageFactory @Inject constructor( blurHash = messageType.info?.blurhash, aspectRatio = aspectRatio, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), - fileExtension = messageType.filename?.let { fileExtensionExtractor.extractFromName(it) }.orEmpty(), + fileExtension = fileExtensionExtractor.extractFromName(messageType.filename), ) } is AudioMessageType -> { TimelineItemAudioContent( - body = messageType.body.trimEnd(), + filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, mediaSource = messageType.source, duration = messageType.info?.duration ?: Duration.ZERO, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), - fileExtension = fileExtensionExtractor.extractFromName(messageType.body), + fileExtension = fileExtensionExtractor.extractFromName(messageType.filename), ) } is VoiceMessageType -> { @@ -166,7 +170,9 @@ class TimelineItemContentMessageFactory @Inject constructor( true -> { TimelineItemVoiceContent( eventId = eventId, - body = messageType.body.trimEnd(), + filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, mediaSource = messageType.source, duration = messageType.info?.duration ?: Duration.ZERO, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, @@ -175,20 +181,24 @@ class TimelineItemContentMessageFactory @Inject constructor( } false -> { TimelineItemAudioContent( - body = messageType.body.trimEnd(), + filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, mediaSource = messageType.source, duration = messageType.info?.duration ?: Duration.ZERO, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), - fileExtension = fileExtensionExtractor.extractFromName(messageType.body), + fileExtension = fileExtensionExtractor.extractFromName(messageType.filename), ) } } } is FileMessageType -> { - val fileExtension = fileExtensionExtractor.extractFromName(messageType.body) + val fileExtension = fileExtensionExtractor.extractFromName(messageType.filename) TimelineItemFileContent( - body = messageType.body.trimEnd(), + filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, thumbnailSource = messageType.info?.thumbnailSource, fileSource = messageType.source, mimeType = messageType.info?.mimetype ?: MimeTypes.fromFileExtension(fileExtension), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt index 787e4d4be6..b76dfdf07b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt @@ -33,7 +33,9 @@ class TimelineItemContentStickerFactory @Inject constructor( val aspectRatio = aspectRatioOf(content.info.width, content.info.height) return TimelineItemStickerContent( - body = content.body, + filename = content.filename, + caption = content.body, + formattedCaption = null, mediaSource = content.source, thumbnailSource = content.info.thumbnailSource, mimeType = content.info.mimetype ?: MimeTypes.OctetStream, @@ -42,7 +44,7 @@ class TimelineItemContentStickerFactory @Inject constructor( height = content.info.height?.toInt(), aspectRatio = aspectRatio, formattedFileSize = fileSizeFormatter.format(content.info.size ?: 0), - fileExtension = fileExtensionExtractor.extractFromName(content.body) + fileExtension = fileExtensionExtractor.extractFromName(content.filename) ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt index aa33804d8e..fa2957b860 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt @@ -8,17 +8,20 @@ package io.element.android.features.messages.impl.timeline.model.event import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody import io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize import kotlin.time.Duration data class TimelineItemAudioContent( - val body: String, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val duration: Duration, val mediaSource: MediaSource, val mimeType: String, val formattedFileSize: String, val fileExtension: String, -) : TimelineItemEventContent { +) : TimelineItemEventContentWithAttachment { val fileExtensionAndSize = formatFileExtensionAndSize( fileExtension, 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/TimelineItemAudioContentProvider.kt index 25933e9ecd..7d4f6baf84 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/TimelineItemAudioContentProvider.kt @@ -22,8 +22,10 @@ open class TimelineItemAudioContentProvider : PreviewParameterProvider, -) : TimelineItemEventContent { +) : TimelineItemEventContentWithAttachment { override val type: String = "TimelineItemAudioContent" } 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/TimelineItemVoiceContentProvider.kt index 84354008f5..ddd731eceb 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/TimelineItemVoiceContentProvider.kt @@ -35,17 +35,21 @@ open class TimelineItemVoiceContentProvider : PreviewParameterProvider = listOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 8f, 7f, 6f, 5f, 4f, 3f, 2f, 1f, 0f), ) = TimelineItemVoiceContent( - eventId = eventId?.let { EventId(it) }, - body = body, + eventId = eventId, + filename = filename, + caption = caption, + formattedCaption = null, duration = duration, - mediaSource = MediaSource(contentUri), + mediaSource = mediaSource, mimeType = mimeType, waveform = waveform.toPersistentList(), ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt index 3ab00a71bc..155b47728a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt @@ -59,7 +59,7 @@ class VoiceMessagePresenter @AssistedInject constructor( eventId = content.eventId, mediaSource = content.mediaSource, mimeType = content.mimeType, - body = content.body, + body = content.caption, ) private val play = mutableStateOf>(AsyncData.Uninitialized) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 284f141d41..14ef106578 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -334,9 +334,9 @@ class MessagesPresenterTest { val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemImageContent( - body = "image.jpg", - formatted = null, - filename = null, + filename = "image.jpg", + caption = null, + formattedCaption = null, mediaSource = MediaSource(AN_AVATAR_URL), thumbnailSource = null, mimeType = MimeTypes.Jpeg, @@ -373,9 +373,9 @@ class MessagesPresenterTest { val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemVideoContent( - body = "video.mp4", - formatted = null, - filename = null, + filename = "video.mp4", + caption = null, + formattedCaption = null, duration = 10.milliseconds, videoSource = MediaSource(AN_AVATAR_URL), thumbnailSource = MediaSource(AN_AVATAR_URL), @@ -413,7 +413,9 @@ class MessagesPresenterTest { val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemFileContent( - body = "file.pdf", + filename = "file.pdf", + caption = null, + formattedCaption = null, fileSource = MediaSource(AN_AVATAR_URL), thumbnailSource = MediaSource(AN_AVATAR_URL), formattedFileSize = "10 MB", 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 529a280994..014e5e939e 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 @@ -69,7 +69,7 @@ class PinnedMessagesListViewTest { state = state, onEventClick = callback ) - rule.onAllNodesWithText(content.body).onFirst().performClick() + rule.onAllNodesWithText(content.filename).onFirst().performClick() } } @@ -85,7 +85,7 @@ class PinnedMessagesListViewTest { rule.setPinnedMessagesListView( state = state, ) - rule.onAllNodesWithText(content.body).onFirst() + rule.onAllNodesWithText(content.filename).onFirst() .performTouchInput { longClick() } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt index c627ff3fdc..337ca9ab7e 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt @@ -62,6 +62,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageT import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.media.aMediaSource import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser +import io.element.android.libraries.matrix.test.timeline.aStickerContent import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation import kotlinx.collections.immutable.persistentListOf @@ -228,14 +229,14 @@ class TimelineItemContentMessageFactoryTest { fun `test create VideoMessageType`() = runTest { val sut = createTimelineItemContentMessageFactory() val result = sut.create( - content = createMessageContent(type = VideoMessageType("body", null, null, MediaSource("url"), null)), + content = createMessageContent(type = VideoMessageType("filename", null, null, MediaSource("url"), null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemVideoContent( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = null, + formattedCaption = null, duration = Duration.ZERO, videoSource = MediaSource(url = "url", json = null), thumbnailSource = null, @@ -256,9 +257,9 @@ class TimelineItemContentMessageFactoryTest { val result = sut.create( content = createMessageContent( type = VideoMessageType( - body = "body.mp4 caption", - formatted = FormattedBody(MessageFormat.HTML, "formatted"), filename = "body.mp4", + caption = "body.mp4 caption", + formattedCaption = FormattedBody(MessageFormat.HTML, "formatted"), source = MediaSource("url"), info = VideoInfo( duration = 1.minutes, @@ -281,9 +282,9 @@ class TimelineItemContentMessageFactoryTest { eventId = AN_EVENT_ID, ) val expected = TimelineItemVideoContent( - body = "body.mp4 caption", - formatted = FormattedBody(MessageFormat.HTML, "formatted"), filename = "body.mp4", + caption = "body.mp4 caption", + formattedCaption = FormattedBody(MessageFormat.HTML, "formatted"), duration = 1.minutes, videoSource = MediaSource(url = "url", json = null), thumbnailSource = MediaSource("url_thumbnail"), @@ -302,12 +303,14 @@ class TimelineItemContentMessageFactoryTest { fun `test create AudioMessageType`() = runTest { val sut = createTimelineItemContentMessageFactory() val result = sut.create( - content = createMessageContent(type = AudioMessageType("body", MediaSource("url"), null)), + content = createMessageContent(type = AudioMessageType("filename", null, null, MediaSource("url"), null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemAudioContent( - body = "body", + filename = "filename", + caption = null, + formattedCaption = null, duration = Duration.ZERO, mediaSource = MediaSource(url = "url", json = null), mimeType = MimeTypes.OctetStream, @@ -323,7 +326,9 @@ class TimelineItemContentMessageFactoryTest { val result = sut.create( content = createMessageContent( type = AudioMessageType( - body = "body.mp3", + filename = "body.mp3", + caption = null, + formattedCaption = null, source = MediaSource("url"), info = AudioInfo( duration = 1.minutes, @@ -336,7 +341,9 @@ class TimelineItemContentMessageFactoryTest { eventId = AN_EVENT_ID, ) val expected = TimelineItemAudioContent( - body = "body.mp3", + filename = "body.mp3", + caption = null, + formattedCaption = null, duration = 1.minutes, mediaSource = MediaSource(url = "url", json = null), mimeType = MimeTypes.Mp3, @@ -350,13 +357,15 @@ class TimelineItemContentMessageFactoryTest { fun `test create VoiceMessageType`() = runTest { val sut = createTimelineItemContentMessageFactory() val result = sut.create( - content = createMessageContent(type = VoiceMessageType("body", MediaSource("url"), null, null)), + content = createMessageContent(type = VoiceMessageType("filename", null, null, MediaSource("url"), null, null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemVoiceContent( + filename = "filename", eventId = AN_EVENT_ID, - body = "body", + caption = null, + formattedCaption = null, duration = Duration.ZERO, mediaSource = MediaSource(url = "url", json = null), mimeType = MimeTypes.OctetStream, @@ -371,7 +380,9 @@ class TimelineItemContentMessageFactoryTest { val result = sut.create( content = createMessageContent( type = VoiceMessageType( - body = "body.ogg", + filename = "body.ogg", + caption = null, + formattedCaption = null, source = MediaSource("url"), info = AudioInfo( duration = 1.minutes, @@ -389,7 +400,9 @@ class TimelineItemContentMessageFactoryTest { ) val expected = TimelineItemVoiceContent( eventId = AN_EVENT_ID, - body = "body.ogg", + filename = "body.ogg", + caption = null, + formattedCaption = null, duration = 1.minutes, mediaSource = MediaSource(url = "url", json = null), mimeType = MimeTypes.Ogg, @@ -408,12 +421,14 @@ class TimelineItemContentMessageFactoryTest { ) ) val result = sut.create( - content = createMessageContent(type = VoiceMessageType("body", MediaSource("url"), null, null)), + content = createMessageContent(type = VoiceMessageType("filename", null, null, MediaSource("url"), null, null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemAudioContent( - body = "body", + filename = "filename", + caption = null, + formattedCaption = null, duration = Duration.ZERO, mediaSource = MediaSource(url = "url", json = null), mimeType = MimeTypes.OctetStream, @@ -427,14 +442,14 @@ class TimelineItemContentMessageFactoryTest { fun `test create ImageMessageType`() = runTest { val sut = createTimelineItemContentMessageFactory() val result = sut.create( - content = createMessageContent(type = ImageMessageType("body", null, null, MediaSource("url"), null)), + content = createMessageContent(type = ImageMessageType("filename", "body", null, MediaSource("url"), null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemImageContent( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = "body", + formattedCaption = null, mediaSource = MediaSource(url = "url", json = null), thumbnailSource = null, formattedFileSize = "0 Bytes", @@ -453,13 +468,15 @@ class TimelineItemContentMessageFactoryTest { val sut = createTimelineItemContentStickerFactory() val result = sut.create( content = createStickerContent( - "body", - ImageInfo(32, 32, "image/webp", 8192, null, MediaSource("thumbnail://url"), null), - "url" + filename = "filename", + inImageInfo = ImageInfo(32, 32, "image/webp", 8192, null, MediaSource("thumbnail://url"), null), + inUrl = "url" ) ) val expected = TimelineItemStickerContent( - body = "body", + filename = "filename", + caption = null, + formattedCaption = null, mediaSource = MediaSource(url = "url", json = null), thumbnailSource = MediaSource(url = "thumbnail://url", json = null), formattedFileSize = "8192 Bytes", @@ -479,9 +496,9 @@ class TimelineItemContentMessageFactoryTest { val result = sut.create( content = createMessageContent( type = ImageMessageType( - body = "body.jpg caption", - formatted = FormattedBody(MessageFormat.HTML, "formatted"), filename = "body.jpg", + caption = "body.jpg caption", + formattedCaption = FormattedBody(MessageFormat.HTML, "formatted"), source = MediaSource("url"), info = ImageInfo( height = 10L, @@ -503,9 +520,9 @@ class TimelineItemContentMessageFactoryTest { eventId = AN_EVENT_ID, ) val expected = TimelineItemImageContent( - body = "body.jpg caption", - formatted = FormattedBody(MessageFormat.HTML, "formatted"), filename = "body.jpg", + formattedCaption = FormattedBody(MessageFormat.HTML, "formatted"), + caption = "body.jpg caption", mediaSource = MediaSource(url = "url", json = null), thumbnailSource = MediaSource("url_thumbnail"), formattedFileSize = "888 Bytes", @@ -523,12 +540,14 @@ class TimelineItemContentMessageFactoryTest { fun `test create FileMessageType`() = runTest { val sut = createTimelineItemContentMessageFactory() val result = sut.create( - content = createMessageContent(type = FileMessageType("body", MediaSource("url"), null)), + content = createMessageContent(type = FileMessageType("filename", null, null, MediaSource("url"), null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemFileContent( - body = "body", + filename = "filename", + caption = null, + formattedCaption = null, fileSource = MediaSource(url = "url", json = null), thumbnailSource = null, formattedFileSize = "0 Bytes", @@ -544,7 +563,9 @@ class TimelineItemContentMessageFactoryTest { val result = sut.create( content = createMessageContent( type = FileMessageType( - body = "body.pdf", + filename = "body.pdf", + caption = null, + formattedCaption = null, source = MediaSource("url"), info = FileInfo( mimetype = MimeTypes.Pdf, @@ -563,7 +584,9 @@ class TimelineItemContentMessageFactoryTest { eventId = AN_EVENT_ID, ) val expected = TimelineItemFileContent( - body = "body.pdf", + filename = "body.pdf", + caption = null, + formattedCaption = null, fileSource = MediaSource(url = "url", json = null), thumbnailSource = MediaSource("url_thumbnail"), formattedFileSize = "123 Bytes", @@ -749,14 +772,16 @@ class TimelineItemContentMessageFactoryTest { ) private fun createStickerContent( - body: String = "Body", + filename: String = "filename", inImageInfo: ImageInfo, - inUrl: String + inUrl: String, + body: String? = null, ): StickerContent { - return StickerContent( + return aStickerContent( + filename = filename, body = body, info = inImageInfo, - source = aMediaSource(url = inUrl), + mediaSource = aMediaSource(url = inUrl), ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index c501c6cf38..4030e30272 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -206,7 +206,8 @@ class RoomDetailsFlowNode @AssistedInject constructor( val mimeType = MimeTypes.Images val input = MediaViewerNode.Inputs( mediaInfo = MediaInfo( - name = navTarget.name, + filename = navTarget.name, + caption = null, mimeType = mimeType, formattedFileSize = "", fileExtension = "" diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt index 26b578a39d..b544ad4750 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt @@ -84,10 +84,11 @@ class UserProfileFlowNode @AssistedInject constructor( val mimeType = MimeTypes.Images val input = MediaViewerNode.Inputs( mediaInfo = MediaInfo( - name = navTarget.name, + filename = navTarget.name, + caption = null, mimeType = mimeType, formattedFileSize = "", - fileExtension = "" + fileExtension = "", ), mediaSource = MediaSource(url = navTarget.avatarUrl), thumbnailSource = null, diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt index 8fb18e1c3e..ab7a19a9c7 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt @@ -46,7 +46,8 @@ class DefaultPinnedMessagesBannerFormatter @Inject constructor( return when (val content = event.content) { is MessageContent -> processMessageContents(event, content) is StickerContent -> { - content.body.prefixWith(CommonStrings.common_sticker) + val text = content.body ?: content.filename + text.prefixWith(CommonStrings.common_sticker) } is UnableToDecryptContent -> { sp.getString(CommonStrings.common_waiting_for_decryption_key) @@ -76,25 +77,25 @@ class DefaultPinnedMessagesBannerFormatter @Inject constructor( messageType.toPlainText(permalinkParser) } is VideoMessageType -> { - messageType.body.prefixWith(CommonStrings.common_video) + messageType.bestDescription.prefixWith(CommonStrings.common_video) } is ImageMessageType -> { - messageType.body.prefixWith(CommonStrings.common_image) + messageType.bestDescription.prefixWith(CommonStrings.common_image) } is StickerMessageType -> { - messageType.body.prefixWith(CommonStrings.common_sticker) + messageType.bestDescription.prefixWith(CommonStrings.common_sticker) } is LocationMessageType -> { messageType.body.prefixWith(CommonStrings.common_shared_location) } is FileMessageType -> { - messageType.body.prefixWith(CommonStrings.common_file) + messageType.bestDescription.prefixWith(CommonStrings.common_file) } is AudioMessageType -> { - messageType.body.prefixWith(CommonStrings.common_audio) + messageType.bestDescription.prefixWith(CommonStrings.common_audio) } is VoiceMessageType -> { - messageType.body.prefixWith(CommonStrings.common_voice_message) + messageType.bestDescription.prefixWith(CommonStrings.common_voice_message) } is OtherMessageType -> { messageType.body diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt index 200b93343d..6b43fc3607 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt @@ -67,7 +67,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor( message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } is StickerContent -> { - val message = sp.getString(CommonStrings.common_sticker) + " (" + content.body + ")" + val message = sp.getString(CommonStrings.common_sticker) + " (" + content.bestDescription + ")" message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } is UnableToDecryptContent -> { diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt index 6247065e37..af347135fb 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt @@ -32,7 +32,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.OtherState import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent import io.element.android.libraries.matrix.api.timeline.item.event.StateContent -import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent import io.element.android.libraries.matrix.api.timeline.item.event.StickerMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent @@ -46,6 +45,7 @@ import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.timeline.aPollContent import io.element.android.libraries.matrix.test.timeline.aProfileChangeMessageContent import io.element.android.libraries.matrix.test.timeline.aProfileTimelineDetails +import io.element.android.libraries.matrix.test.timeline.aStickerContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.toolbox.impl.strings.AndroidStringProvider @@ -91,7 +91,7 @@ class DefaultPinnedMessagesBannerFormatterTest { fun `Sticker content`() { val body = "a sticker body" val info = ImageInfo(null, null, null, null, null, null, null) - val message = createRoomEvent(false, null, StickerContent(body, info, aMediaSource(url = "url"))) + val message = createRoomEvent(false, null, aStickerContent(body, info, aMediaSource(url = "url"))) val result = formatter.format(message) val expectedBody = "Sticker: a sticker body" assertThat(result.toString()).isEqualTo(expectedBody) @@ -135,11 +135,11 @@ class DefaultPinnedMessagesBannerFormatterTest { val sharedContentMessagesTypes = arrayOf( TextMessageType(body, null), VideoMessageType(body, null, null, MediaSource("url"), null), - AudioMessageType(body, MediaSource("url"), null), - VoiceMessageType(body, MediaSource("url"), null, null), + AudioMessageType(body, null, null, MediaSource("url"), null), + VoiceMessageType(body, null, null, MediaSource("url"), null, null), ImageMessageType(body, null, null, MediaSource("url"), null), - StickerMessageType(body, MediaSource("url"), null), - FileMessageType(body, MediaSource("url"), null), + StickerMessageType(body, null, null, MediaSource("url"), null), + FileMessageType(body, null, null, MediaSource("url"), null), LocationMessageType(body, "geo:1,2", null), NoticeMessageType(body, null), EmoteMessageType(body, null), diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt index db3377c5e8..efc748d10f 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt @@ -32,7 +32,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.OtherState import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent import io.element.android.libraries.matrix.api.timeline.item.event.StateContent -import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent import io.element.android.libraries.matrix.api.timeline.item.event.StickerMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent @@ -46,6 +45,7 @@ import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.timeline.aPollContent import io.element.android.libraries.matrix.test.timeline.aProfileChangeMessageContent import io.element.android.libraries.matrix.test.timeline.aProfileTimelineDetails +import io.element.android.libraries.matrix.test.timeline.aStickerContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.services.toolbox.impl.strings.AndroidStringProvider import org.junit.Before @@ -98,7 +98,7 @@ class DefaultRoomLastMessageFormatterTest { fun `Sticker content`() { val body = "a sticker body" val info = ImageInfo(null, null, null, null, null, null, null) - val message = createRoomEvent(false, null, StickerContent(body, info, aMediaSource(url = "url"))) + val message = createRoomEvent(false, null, aStickerContent(body, info, aMediaSource(url = "url"))) val result = formatter.format(message, false) val expectedBody = someoneElseId.toString() + ": Sticker (a sticker body)" assertThat(result.toString()).isEqualTo(expectedBody) @@ -179,11 +179,11 @@ class DefaultRoomLastMessageFormatterTest { val sharedContentMessagesTypes = arrayOf( TextMessageType(body, null), VideoMessageType(body, null, null, MediaSource("url"), null), - AudioMessageType(body, MediaSource("url"), null), - VoiceMessageType(body, MediaSource("url"), null, null), + AudioMessageType(body, null, null, MediaSource("url"), null), + VoiceMessageType(body, null, null, MediaSource("url"), null, null), ImageMessageType(body, null, null, MediaSource("url"), null), - StickerMessageType(body, MediaSource("url"), null), - FileMessageType(body, MediaSource("url"), null), + StickerMessageType(body, null, null, MediaSource("url"), null), + FileMessageType(body, null, null, MediaSource("url"), null), LocationMessageType(body, "geo:1,2", null), NoticeMessageType(body, null), EmoteMessageType(body, null), diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt index fc65d30955..ebe8a10bdc 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt @@ -30,10 +30,14 @@ data class MessageContent( data object RedactedContent : EventContent data class StickerContent( - val body: String, + val filename: String, + val body: String?, val info: ImageInfo, val source: MediaSource, -) : EventContent +) : EventContent { + val bestDescription: String + get() = body ?: filename +} data class PollContent( val question: String, 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 b398e056bc..9237c5c8ea 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 @@ -18,24 +18,37 @@ import io.element.android.libraries.matrix.api.media.VideoInfo @Immutable sealed interface MessageType +@Immutable +sealed interface MessageTypeWithAttachment : MessageType { + val filename: String + val caption: String? + val formattedCaption: FormattedBody? + + val bestDescription: String + get() = caption ?: filename +} + data class EmoteMessageType( val body: String, val formatted: FormattedBody? ) : MessageType data class ImageMessageType( - val body: String, - val formatted: FormattedBody?, - val filename: String?, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: ImageInfo? -) : MessageType +) : MessageTypeWithAttachment +// FIXME This is never used in production code. data class StickerMessageType( - val body: String, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: ImageInfo? -) : MessageType +) : MessageTypeWithAttachment data class LocationMessageType( val body: String, @@ -44,31 +57,37 @@ data class LocationMessageType( ) : MessageType data class AudioMessageType( - val body: String, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: AudioInfo?, -) : MessageType +) : MessageTypeWithAttachment data class VoiceMessageType( - val body: String, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: AudioInfo?, val details: AudioDetails?, -) : MessageType +) : MessageTypeWithAttachment data class VideoMessageType( - val body: String, - val formatted: FormattedBody?, - val filename: String?, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: VideoInfo? -) : MessageType +) : MessageTypeWithAttachment data class FileMessageType( - val body: String, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: FileInfo? -) : MessageType +) : MessageTypeWithAttachment data class NoticeMessageType( val body: String, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt index 3e67bd8595..cc6bb87f0f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt @@ -50,14 +50,18 @@ class EventMessageMapper { when (type.content.voice) { null -> { AudioMessageType( - body = type.content.body, + filename = type.content.filename, + caption = type.content.caption, + formattedCaption = type.content.formattedCaption?.map(), source = type.content.source.map(), info = type.content.info?.map(), ) } else -> { VoiceMessageType( - body = type.content.body, + filename = type.content.filename, + caption = type.content.caption, + formattedCaption = type.content.formattedCaption?.map(), source = type.content.source.map(), info = type.content.info?.map(), details = type.content.audio?.map(), @@ -66,10 +70,22 @@ class EventMessageMapper { } } is RustMessageType.File -> { - FileMessageType(type.content.body, type.content.source.map(), type.content.info?.map()) + FileMessageType( + filename = type.content.filename, + caption = type.content.caption, + formattedCaption = type.content.formattedCaption?.map(), + source = type.content.source.map(), + info = type.content.info?.map(), + ) } is RustMessageType.Image -> { - ImageMessageType(type.content.body, type.content.formatted?.map(), type.content.filename, type.content.source.map(), type.content.info?.map()) + ImageMessageType( + filename = type.content.filename, + caption = type.content.caption, + formattedCaption = type.content.formattedCaption?.map(), + source = type.content.source.map(), + info = type.content.info?.map(), + ) } is RustMessageType.Notice -> { NoticeMessageType(type.content.body, type.content.formatted?.map()) @@ -81,7 +97,13 @@ class EventMessageMapper { EmoteMessageType(type.content.body, type.content.formatted?.map()) } is RustMessageType.Video -> { - VideoMessageType(type.content.body, type.content.formatted?.map(), type.content.filename, type.content.source.map(), type.content.info?.map()) + VideoMessageType( + filename = type.content.filename, + caption = type.content.caption, + formattedCaption = type.content.formattedCaption?.map(), + source = type.content.source.map(), + info = type.content.info?.map(), + ) } is RustMessageType.Location -> { LocationMessageType(type.content.body, type.content.geoUri, type.content.description) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt index ed075c508f..3c0ec16f65 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt @@ -84,7 +84,8 @@ class TimelineEventContentMapper( } is TimelineItemContent.Sticker -> { StickerContent( - body = it.body, + filename = it.body, + body = null, info = it.info.map(), source = it.source.map(), ) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt index b8bc798b28..85833f183a 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt @@ -10,6 +10,8 @@ package io.element.android.libraries.matrix.test.timeline import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.media.ImageInfo +import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.poll.PollAnswer import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo @@ -25,6 +27,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.PollContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.Receipt +import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_USER_ID @@ -110,6 +113,18 @@ fun aMessageContent( type = messageType ) +fun aStickerContent( + filename: String = "filename", + info: ImageInfo, + mediaSource: MediaSource, + body: String? = null, +) = StickerContent( + filename = filename, + body = body, + info = info, + source = mediaSource, +) + fun aTimelineItemDebugInfo( model: String = "Rust(Model())", originalJson: String? = null, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt index f6a3e5a3d2..a93ce21dea 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt @@ -46,7 +46,7 @@ internal class CoilMediaFetcher( * */ private suspend fun fetchFile(mediaSource: MediaSource, kind: MediaRequestData.Kind.File): FetchResult? { - return mediaLoader.downloadMediaFile(mediaSource, kind.mimeType, kind.body) + return mediaLoader.downloadMediaFile(mediaSource, kind.mimeType, kind.fileName) .map { mediaFile -> val file = mediaFile.toFile() SourceResult( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt index ac073d3840..38499d15fb 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt @@ -26,7 +26,12 @@ data class MediaRequestData( ) { sealed interface Kind { data object Content : Kind - data class File(val body: String?, val mimeType: String) : Kind + + data class File( + val fileName: String, + val mimeType: String, + ) : Kind + data class Thumbnail(val width: Long, val height: Long) : Kind { constructor(size: Long) : this(size, size) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt index 301966566a..58b3506d46 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt @@ -49,11 +49,11 @@ open class InReplyToDetailsProvider : PreviewParameterProvider ), aMessageContent( body = "Audio", - type = AudioMessageType("Audio", MediaSource("url"), null), + type = AudioMessageType("Audio", null, null, MediaSource("url"), null), ), aMessageContent( body = "Voice", - type = VoiceMessageType("Voice", MediaSource("url"), null, null), + type = VoiceMessageType("Voice", null, null, MediaSource("url"), null, null), ), aMessageContent( body = "Image", @@ -61,11 +61,11 @@ open class InReplyToDetailsProvider : PreviewParameterProvider ), aMessageContent( body = "Sticker", - type = StickerMessageType("Image", MediaSource("url"), null), + type = StickerMessageType("Image", null, null, MediaSource("url"), null), ), aMessageContent( body = "File", - type = FileMessageType("File", MediaSource("url"), null), + type = FileMessageType("File", null, null, MediaSource("url"), null), ), aMessageContent( body = "Location", diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt index 9e4318b3be..e224955164 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt @@ -75,9 +75,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = ImageMessageType( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = null, + formattedCaption = null, source = aMediaSource(), info = anImageInfo(), ) @@ -105,9 +105,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = ImageMessageType( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = anImageInfo(), ) @@ -134,6 +134,7 @@ class InReplyToMetadataKtTest { moleculeFlow(RecompositionMode.Immediate) { anInReplyToDetailsReady( eventContent = StickerContent( + filename = "filename", body = "body", info = anImageInfo(), source = aMediaSource(url = "url") @@ -160,6 +161,7 @@ class InReplyToMetadataKtTest { moleculeFlow(RecompositionMode.Immediate) { anInReplyToDetailsReady( eventContent = StickerContent( + filename = "filename", body = "body", info = anImageInfo(), source = aMediaSource(url = "url") @@ -187,9 +189,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = VideoMessageType( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = null, + formattedCaption = null, source = aMediaSource(), info = aVideoInfo(), ) @@ -217,9 +219,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = VideoMessageType( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = aVideoInfo(), ) @@ -247,7 +249,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = FileMessageType( - body = "body", + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = FileInfo( mimetype = null, @@ -280,7 +284,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = FileMessageType( - body = "body", + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = FileInfo( mimetype = null, @@ -313,7 +319,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = AudioMessageType( - body = "body", + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = AudioInfo( duration = null, @@ -375,7 +383,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = VoiceMessageType( - body = "body", + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = null, details = null, diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt index ef27ce68a7..7b63c22121 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt @@ -303,7 +303,7 @@ private fun MediaFileView( if (info != null) { Spacer(modifier = Modifier.height(20.dp)) Text( - text = info.name, + text = info.filename, maxLines = 2, style = ElementTheme.typography.fontBodyLgRegular, overflow = TextOverflow.Ellipsis, diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt index c836a25a85..5ded65b2b3 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt @@ -13,43 +13,49 @@ import kotlinx.parcelize.Parcelize @Parcelize data class MediaInfo( - val name: String, + val filename: String, + val caption: String?, val mimeType: String, val formattedFileSize: String, val fileExtension: String, ) : Parcelable fun anImageMediaInfo(): MediaInfo = MediaInfo( - "an image file.jpg", - MimeTypes.Jpeg, - "4MB", - "jpg" + filename = "an image file.jpg", + caption = null, + mimeType = MimeTypes.Jpeg, + formattedFileSize = "4MB", + fileExtension = "jpg", ) fun aVideoMediaInfo(): MediaInfo = MediaInfo( - "a video file.mp4", - MimeTypes.Mp4, - "14MB", - "mp4" + filename = "a video file.mp4", + caption = null, + mimeType = MimeTypes.Mp4, + formattedFileSize = "14MB", + fileExtension = "mp4", ) fun aPdfMediaInfo(): MediaInfo = MediaInfo( - "a pdf file.pdf", - MimeTypes.Pdf, - "23MB", - "pdf" + filename = "a pdf file.pdf", + caption = null, + mimeType = MimeTypes.Pdf, + formattedFileSize = "23MB", + fileExtension = "pdf", ) fun anApkMediaInfo(): MediaInfo = MediaInfo( - "an apk file.apk", - MimeTypes.Apk, - "50MB", - "apk" + filename = "an apk file.apk", + caption = null, + mimeType = MimeTypes.Apk, + formattedFileSize = "50MB", + fileExtension = "apk", ) fun anAudioMediaInfo(): MediaInfo = MediaInfo( - "an audio file.mp3", - MimeTypes.Mp3, - "7MB", - "mp3" + filename = "an audio file.mp3", + caption = null, + mimeType = MimeTypes.Mp3, + formattedFileSize = "7MB", + fileExtension = "mp3", ) diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt index c62094b364..3d51096dd3 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt @@ -92,7 +92,7 @@ class MediaViewerPresenter @AssistedInject constructor( mediaLoader.downloadMediaFile( source = inputs.mediaSource, mimeType = inputs.mediaInfo.mimeType, - body = inputs.mediaInfo.name + body = inputs.mediaInfo.filename ) .onSuccess { mediaFile.value = it diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt index f76bf51073..9f362c13fa 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt @@ -322,7 +322,7 @@ private fun ThumbnailView( if (isVisible) { val mediaRequestData = MediaRequestData( source = thumbnailSource, - kind = MediaRequestData.Kind.File(mediaInfo.name, mediaInfo.mimeType) + kind = MediaRequestData.Kind.File(mediaInfo.filename, mediaInfo.mimeType) ) AsyncImage( modifier = Modifier.fillMaxSize(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt index 33c63d4f72..66bed465ed 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt @@ -157,7 +157,7 @@ class AndroidLocalMediaActions @Inject constructor( @RequiresApi(Build.VERSION_CODES.Q) private fun saveOnDiskUsingMediaStore(localMedia: LocalMedia) { val contentValues = ContentValues().apply { - put(MediaStore.MediaColumns.DISPLAY_NAME, localMedia.info.name) + put(MediaStore.MediaColumns.DISPLAY_NAME, localMedia.info.filename) put(MediaStore.MediaColumns.MIME_TYPE, localMedia.info.mimeType) put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) } @@ -175,7 +175,7 @@ class AndroidLocalMediaActions @Inject constructor( private fun saveOnDiskUsingExternalStorageApi(localMedia: LocalMedia) { val target = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), - localMedia.info.name + localMedia.info.filename ) localMedia.openStream()?.use { input -> FileOutputStream(target).use { output -> diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt index d8e5af8233..06fefa2623 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt @@ -32,21 +32,36 @@ class AndroidLocalMediaFactory @Inject constructor( private val fileSizeFormatter: FileSizeFormatter, private val fileExtensionExtractor: FileExtensionExtractor, ) : LocalMediaFactory { - override fun createFromMediaFile(mediaFile: MediaFile, mediaInfo: MediaInfo): LocalMedia { - val uri = mediaFile.toFile().toUri() - return createFromUri( - uri = uri, - mimeType = mediaInfo.mimeType, - name = mediaInfo.name, - formattedFileSize = mediaInfo.formattedFileSize, - ) - } + override fun createFromMediaFile( + mediaFile: MediaFile, + mediaInfo: MediaInfo, + ): LocalMedia = createFromUri( + uri = mediaFile.toFile().toUri(), + mimeType = mediaInfo.mimeType, + name = mediaInfo.filename, + caption = mediaInfo.caption, + formattedFileSize = mediaInfo.formattedFileSize, + ) override fun createFromUri( uri: Uri, mimeType: String?, name: String?, formattedFileSize: String? + ): LocalMedia = createFromUri( + uri = uri, + mimeType = mimeType, + name = name, + caption = null, + formattedFileSize = formattedFileSize, + ) + + private fun createFromUri( + uri: Uri, + mimeType: String?, + name: String?, + caption: String?, + formattedFileSize: String? ): LocalMedia { val resolvedMimeType = mimeType ?: context.getMimeType(uri) ?: MimeTypes.OctetStream val fileName = name ?: context.getFileName(uri) ?: "" @@ -56,7 +71,8 @@ class AndroidLocalMediaFactory @Inject constructor( uri = uri, info = MediaInfo( mimeType = resolvedMimeType, - name = fileName, + filename = fileName, + caption = caption, formattedFileSize = fileSize, fileExtension = fileExtension ) diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt index af0a1e9b88..eb78517a36 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt @@ -29,7 +29,8 @@ class AndroidLocalMediaFactoryTest { assertThat(result.uri.toString()).endsWith("aPath") assertThat(result.info).isEqualTo( MediaInfo( - name = "an image file.jpg", + filename = "an image file.jpg", + caption = null, mimeType = MimeTypes.Jpeg, formattedFileSize = "4MB", fileExtension = "jpg", diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt index cc8b29da2b..43474e24bb 100644 --- a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt @@ -32,7 +32,8 @@ class FakeLocalMediaFactory( override fun createFromUri(uri: Uri, mimeType: String?, name: String?, formattedFileSize: String?): LocalMedia { val safeName = name ?: fallbackName val mediaInfo = MediaInfo( - name = safeName, + filename = safeName, + caption = null, mimeType = mimeType ?: fallbackMimeType, formattedFileSize = formattedFileSize ?: fallbackFileSize, fileExtension = fileExtensionExtractor.extractFromName(safeName) 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 08e95fcfcf..1d6e916124 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 @@ -265,15 +265,15 @@ class DefaultNotifiableEventResolver @Inject constructor( senderDisambiguatedDisplayName: String, ): String { return when (val messageType = content.messageType) { - is AudioMessageType -> messageType.body + is AudioMessageType -> messageType.bestDescription is VoiceMessageType -> stringProvider.getString(CommonStrings.common_voice_message) is EmoteMessageType -> "* $senderDisambiguatedDisplayName ${messageType.body}" - is FileMessageType -> messageType.body - is ImageMessageType -> messageType.body - is StickerMessageType -> messageType.body + is FileMessageType -> messageType.bestDescription + is ImageMessageType -> messageType.bestDescription + is StickerMessageType -> messageType.bestDescription is NoticeMessageType -> messageType.body is TextMessageType -> messageType.toPlainText(permalinkParser = permalinkParser) - is VideoMessageType -> messageType.body + is VideoMessageType -> messageType.bestDescription is LocationMessageType -> messageType.body is OtherMessageType -> messageType.body } @@ -299,7 +299,7 @@ class DefaultNotifiableEventResolver @Inject constructor( .getMediaFile( mediaSource = messageType.source, mimeType = messageType.info?.mimetype, - body = messageType.body, + body = messageType.filename, ) is VideoMessageType -> null // Use the thumbnail here? else -> null diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt index 143f134656..f8818cf341 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt @@ -47,7 +47,7 @@ interface NotificationMediaRepo { * * @param mediaSource the media source of the media. * @param mimeType the mime type of the media. - * @param body the body of the message. + * @param body optional body which will be used to name the file. * @return A [Result] holding either the media [File] from the cache directory or an [Exception]. */ suspend fun getMediaFile( 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 aae4d84d38..de29fcf1f0 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 @@ -187,7 +187,7 @@ class DefaultNotifiableEventResolverTest { aNotificationData( content = NotificationContent.MessageLike.RoomMessage( senderId = A_USER_ID_2, - messageType = AudioMessageType(body = "Audio", MediaSource("url"), null) + messageType = AudioMessageType("Audio", null, null, MediaSource("url"), null) ), ) ) @@ -206,7 +206,7 @@ class DefaultNotifiableEventResolverTest { aNotificationData( content = NotificationContent.MessageLike.RoomMessage( senderId = A_USER_ID_2, - messageType = VideoMessageType(body = "Video", null, null, MediaSource("url"), null) + messageType = VideoMessageType("Video", null, null, MediaSource("url"), null) ), ) ) @@ -225,7 +225,7 @@ class DefaultNotifiableEventResolverTest { aNotificationData( content = NotificationContent.MessageLike.RoomMessage( senderId = A_USER_ID_2, - messageType = VoiceMessageType(body = "Voice", MediaSource("url"), null, null) + messageType = VoiceMessageType("Voice", null, null, MediaSource("url"), null, null) ), ) ) @@ -263,7 +263,7 @@ class DefaultNotifiableEventResolverTest { aNotificationData( content = NotificationContent.MessageLike.RoomMessage( senderId = A_USER_ID_2, - messageType = StickerMessageType("Sticker", MediaSource("url"), null), + messageType = StickerMessageType("Sticker", null, null, MediaSource("url"), null), ), ) ) @@ -282,7 +282,7 @@ class DefaultNotifiableEventResolverTest { aNotificationData( content = NotificationContent.MessageLike.RoomMessage( senderId = A_USER_ID_2, - messageType = FileMessageType("File", MediaSource("url"), null), + messageType = FileMessageType("File", null, null, MediaSource("url"), null), ), ) ) From 10c42c141673b2ce7d4118e98957c8963c13ce97 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 2 Oct 2024 12:10:34 +0000 Subject: [PATCH 02/67] Update screenshots --- ...eatures.messages.impl.actionlist_SheetContent_Day_3_en.png | 4 ++-- ...tures.messages.impl.actionlist_SheetContent_Night_3_en.png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_3_en.png index 518d669991..07d4116d65 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c13e631e41cafa49321988065bedff6081fbc4aa99d9cb3a32b4d860888c7535 -size 42412 +oid sha256:0f5adfb435286a84587a66d6970b1a7f03a8cb53eac86956f61092d97ea16951 +size 43121 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_3_en.png index 59f8a02c54..eed6d921be 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35988d035ddf2eea2463586024703fc630f996c6e03a232fc65196ab5186b89a -size 41582 +oid sha256:87d68af6f69c67de710145b3fabbff40bc6c9a767542e5a62be80d084972cb28 +size 42221 From 8708daee67cb685edb90ef880e2444290e721d5f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 01:02:21 +0000 Subject: [PATCH 03/67] chore(deps): update mobile-dev-inc/action-maestro-cloud action to v1.9.2 --- .github/workflows/maestro.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index 3b11b427ae..2aeafa9fba 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -79,7 +79,7 @@ jobs: uses: actions/download-artifact@v4 with: name: elementx-apk-maestro - - uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 + - uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch' with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} From 97fb7bc2fa149dee91f38774712a9f449c23e6f4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Oct 2024 10:49:55 +0200 Subject: [PATCH 04/67] Let `AsyncAction.Confirming` be an interface, with a `AsyncAction.ConfirmingNoParams` data object. This will allow inheritance of `AsyncAction.Confirming` with parameter(s). --- .../logout/impl/AccountDeactivationPresenter.kt | 2 +- .../logout/impl/AccountDeactivationStateProvider.kt | 2 +- .../logout/impl/ui/AccountDeactivationActionDialog.kt | 2 +- .../logout/impl/AccountDeactivationPresenterTest.kt | 6 +++--- .../logout/impl/AccountDeactivationViewTest.kt | 2 +- .../api/response/AcceptDeclineInviteStateProvider.kt | 4 ++-- .../impl/response/AcceptDeclineInvitePresenter.kt | 2 +- .../features/lockscreen/impl/unlock/PinUnlockView.kt | 2 +- .../login/impl/screens/qrcode/scan/QrCodeScanView.kt | 2 +- .../logout/api/direct/DirectLogoutStateProvider.kt | 2 +- .../android/features/logout/impl/LogoutPresenter.kt | 2 +- .../features/logout/impl/LogoutStateProvider.kt | 2 +- .../logout/impl/direct/DirectLogoutPresenter.kt | 2 +- .../features/logout/impl/ui/LogoutActionDialog.kt | 2 +- .../features/logout/impl/LogoutPresenterTest.kt | 8 ++++---- .../android/features/logout/impl/LogoutViewTest.kt | 2 +- .../logout/impl/direct/DefaultDirectLogoutViewTest.kt | 6 +++--- .../logout/impl/direct/DirectLogoutPresenterTest.kt | 8 ++++---- .../impl/blockedusers/BlockedUsersPresenter.kt | 2 +- .../blockedusers/BlockedUsersStatePreviewProvider.kt | 2 +- .../impl/blockedusers/BlockedUserViewTest.kt | 4 ++-- .../moderation/RoomMembersModerationPresenter.kt | 6 +++--- .../RoomMembersModerationStatePreviewProvider.kt | 4 ++-- .../RolesAndPermissionsPresenter.kt | 4 ++-- .../RolesAndPermissionsStateProvider.kt | 4 ++-- .../changeroles/ChangeRolesPresenter.kt | 4 ++-- .../changeroles/ChangeRolesStateProvider.kt | 4 ++-- .../permissions/ChangeRoomPermissionsPresenter.kt | 2 +- .../ChangeRoomPermissionsStatePreviewProvider.kt | 2 +- .../moderation/RoomMembersModerationPresenterTest.kt | 4 ++-- .../moderation/RoomMembersModerationViewTest.kt | 8 ++++---- .../RolesAndPermissionPresenterTest.kt | 2 +- .../rolesandpermissions/RolesAndPermissionsViewTest.kt | 10 +++++----- .../changeroles/ChangeRolesPresenterTest.kt | 8 ++++---- .../changeroles/ChangeRolesViewTest.kt | 8 ++++---- .../permissions/ChangeRoomPermissionsPresenterTest.kt | 2 +- .../permissions/ChangeRoomPermissionsViewTest.kt | 4 ++-- .../impl/disable/SecureBackupDisablePresenter.kt | 2 +- .../impl/disable/SecureBackupDisableStateProvider.kt | 2 +- .../impl/disable/SecureBackupDisablePresenterTest.kt | 6 +++--- .../verifysession/impl/VerifySelfSessionView.kt | 2 +- .../android/libraries/architecture/AsyncAction.kt | 6 ++++-- .../components/async/AsyncActionProvider.kt | 2 +- .../designsystem/components/async/AsyncActionView.kt | 2 +- .../troubleshoot/impl/TroubleshootNotificationsView.kt | 4 ++-- .../troubleshoot/impl/TroubleshootTestSuite.kt | 2 +- 46 files changed, 86 insertions(+), 84 deletions(-) diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt index 8f0e895524..0cadab231f 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt @@ -51,7 +51,7 @@ class AccountDeactivationPresenter @Inject constructor( action ) } else { - action.value = AsyncAction.Confirming + action.value = AsyncAction.ConfirmingNoParams } AccountDeactivationEvents.CloseDialogs -> { action.value = AsyncAction.Uninitialized 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/AccountDeactivationStateProvider.kt index 07ef6590bb..5de54fe86f 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/AccountDeactivationStateProvider.kt @@ -20,7 +20,7 @@ open class AccountDeactivationStateProvider : PreviewParameterProvider Unit - AsyncAction.Confirming -> + is AsyncAction.Confirming -> AccountDeactivationConfirmationDialog( onSubmitClick = onConfirmClick, onDismiss = onDismissDialog diff --git a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenterTest.kt b/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenterTest.kt index b34ac2f391..eee88bd615 100644 --- a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenterTest.kt +++ b/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenterTest.kt @@ -74,7 +74,7 @@ class AccountDeactivationPresenterTest { skipItems(1) initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) val updatedState = awaitItem() - assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.Confirming) + assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams) updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) val updatedState2 = awaitItem() assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading) @@ -102,7 +102,7 @@ class AccountDeactivationPresenterTest { skipItems(2) initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) val updatedState = awaitItem() - assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.Confirming) + assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams) updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) val updatedState2 = awaitItem() assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading) @@ -135,7 +135,7 @@ class AccountDeactivationPresenterTest { skipItems(2) initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) val updatedState = awaitItem() - assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.Confirming) + assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams) updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) val updatedState2 = awaitItem() assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading) diff --git a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt b/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt index 06d27e463b..a7c616e4cf 100644 --- a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt +++ b/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt @@ -71,7 +71,7 @@ class AccountDeactivationViewTest { deactivateFormState = aDeactivateFormState( password = A_PASSWORD, ), - accountDeactivationAction = AsyncAction.Confirming, + accountDeactivationAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder, ), ) diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt index dcf7238b00..4c377590e6 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt @@ -20,13 +20,13 @@ open class AcceptDeclineInviteStateProvider : PreviewParameterProvider { currentInvite = Optional.of(event.invite) - declinedAction.value = AsyncAction.Confirming + declinedAction.value = AsyncAction.ConfirmingNoParams } is InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite -> { 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 8667b3806a..f0a611d56f 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 @@ -96,7 +96,7 @@ fun PinUnlockView( latestOnSuccessLogout(state.signOutAction.data) } } - AsyncAction.Confirming, + is AsyncAction.Confirming, is AsyncAction.Failure, AsyncAction.Uninitialized -> Unit } 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 2b38deed70..d32f66cebd 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 @@ -189,7 +189,7 @@ private fun ColumnScope.Buttons( } } AsyncAction.Uninitialized, - AsyncAction.Confirming -> Unit + is AsyncAction.Confirming -> Unit } } } 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/DirectLogoutStateProvider.kt index 62b52dbde8..b59eb087ca 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/DirectLogoutStateProvider.kt @@ -14,7 +14,7 @@ open class DirectLogoutStateProvider : PreviewParameterProvider get() = sequenceOf( aDirectLogoutState(), - aDirectLogoutState(logoutAction = AsyncAction.Confirming), + aDirectLogoutState(logoutAction = AsyncAction.ConfirmingNoParams), aDirectLogoutState(logoutAction = AsyncAction.Loading), aDirectLogoutState(logoutAction = AsyncAction.Failure(Exception("Error"))), aDirectLogoutState(logoutAction = AsyncAction.Success("success")), diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt index 65ffa75f02..d3a7e4c34b 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt @@ -64,7 +64,7 @@ class LogoutPresenter @Inject constructor( if (logoutAction.value.isConfirming() || event.ignoreSdkError) { localCoroutineScope.logout(logoutAction, event.ignoreSdkError) } else { - logoutAction.value = AsyncAction.Confirming + logoutAction.value = AsyncAction.ConfirmingNoParams } } LogoutEvents.CloseDialogs -> { 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/LogoutStateProvider.kt index 1f648a51ee..364cdbebef 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/LogoutStateProvider.kt @@ -21,7 +21,7 @@ open class LogoutStateProvider : PreviewParameterProvider { aLogoutState(isLastDevice = true), aLogoutState(isLastDevice = false, backupUploadState = BackupUploadState.Uploading(66, 200)), aLogoutState(isLastDevice = true, backupUploadState = BackupUploadState.Done), - aLogoutState(logoutAction = AsyncAction.Confirming), + aLogoutState(logoutAction = AsyncAction.ConfirmingNoParams), aLogoutState(logoutAction = AsyncAction.Loading), aLogoutState(logoutAction = AsyncAction.Failure(Exception("Failed to logout"))), aLogoutState(backupUploadState = BackupUploadState.SteadyException(SteadyStateException.Connection("No network"))), diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt index 4a5fead974..f78efc6cda 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt @@ -52,7 +52,7 @@ class DirectLogoutPresenter @Inject constructor( if (logoutAction.value.isConfirming() || event.ignoreSdkError) { localCoroutineScope.logout(logoutAction, event.ignoreSdkError) } else { - logoutAction.value = AsyncAction.Confirming + logoutAction.value = AsyncAction.ConfirmingNoParams } } DirectLogoutEvents.CloseDialogs -> { diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt index 44f01e738d..3c43f2e099 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt @@ -29,7 +29,7 @@ fun LogoutActionDialog( when (state) { AsyncAction.Uninitialized -> Unit - AsyncAction.Confirming -> + is AsyncAction.Confirming -> LogoutConfirmationDialog( onSubmitClick = onConfirmClick, onDismiss = onDismissDialog diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt index 348f983b66..b1e48838aa 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt @@ -107,7 +107,7 @@ class LogoutPresenterTest { val initialState = awaitFirstItem() initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() - assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.Confirming) + assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) initialState.eventSink.invoke(LogoutEvents.CloseDialogs) val finalState = awaitItem() assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized) @@ -123,7 +123,7 @@ class LogoutPresenterTest { val initialState = awaitFirstItem() initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() - assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.Confirming) + assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) confirmationState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) @@ -148,7 +148,7 @@ class LogoutPresenterTest { val initialState = awaitFirstItem() initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() - assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.Confirming) + assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) confirmationState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) @@ -180,7 +180,7 @@ class LogoutPresenterTest { val initialState = awaitFirstItem() initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() - assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.Confirming) + assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) confirmationState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt index 1621872813..5f59b141f2 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt @@ -48,7 +48,7 @@ class LogoutViewTest { val eventsRecorder = EventsRecorder() rule.setLogoutView( aLogoutState( - logoutAction = AsyncAction.Confirming, + logoutAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt index 9daf3cdf63..c859684e31 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt @@ -36,7 +36,7 @@ class DefaultDirectLogoutViewTest { val eventsRecorder = EventsRecorder() rule.setDefaultDirectLogoutView( state = aDirectLogoutState( - logoutAction = AsyncAction.Confirming, + logoutAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder, ) ) @@ -49,7 +49,7 @@ class DefaultDirectLogoutViewTest { val eventsRecorder = EventsRecorder() rule.setDefaultDirectLogoutView( state = aDirectLogoutState( - logoutAction = AsyncAction.Confirming, + logoutAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder, ) ) @@ -63,7 +63,7 @@ class DefaultDirectLogoutViewTest { val eventsRecorder = EventsRecorder() rule.setDefaultDirectLogoutView( state = aDirectLogoutState( - logoutAction = AsyncAction.Confirming, + logoutAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder, ) ) diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenterTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenterTest.kt index a490b7b10d..5835686365 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenterTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenterTest.kt @@ -88,7 +88,7 @@ class DirectLogoutPresenterTest { val initialState = awaitFirstItem() initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() - assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.Confirming) + assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) initialState.eventSink.invoke(DirectLogoutEvents.CloseDialogs) val finalState = awaitItem() assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized) @@ -104,7 +104,7 @@ class DirectLogoutPresenterTest { val initialState = awaitFirstItem() initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() - assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.Confirming) + assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) confirmationState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) @@ -129,7 +129,7 @@ class DirectLogoutPresenterTest { val initialState = awaitFirstItem() initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() - assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.Confirming) + assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) confirmationState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) @@ -161,7 +161,7 @@ class DirectLogoutPresenterTest { val initialState = awaitFirstItem() initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() - assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.Confirming) + assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) confirmationState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt index a957986e87..1bf6935f5c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt @@ -67,7 +67,7 @@ class BlockedUsersPresenter @Inject constructor( when (event) { is BlockedUsersEvents.Unblock -> { pendingUserToUnblock = event.userId - unblockUserAction.value = AsyncAction.Confirming + unblockUserAction.value = AsyncAction.ConfirmingNoParams } BlockedUsersEvents.ConfirmUnblock -> { pendingUserToUnblock?.let { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewProvider.kt index 15b6c6e10f..8bf5b026f8 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewProvider.kt @@ -19,7 +19,7 @@ class BlockedUsersStatePreviewProvider : PreviewParameterProvider() rule.setBlockedUsersView( aBlockedUsersState( - unblockUserAction = AsyncAction.Confirming, + unblockUserAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) @@ -78,7 +78,7 @@ class BlockedUserViewTest { val eventsRecorder = EventsRecorder() rule.setBlockedUsersView( aBlockedUsersState( - unblockUserAction = AsyncAction.Confirming, + unblockUserAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt index 0b9e744dd2..97e518fb47 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt @@ -77,7 +77,7 @@ class RoomMembersModerationPresenter @Inject constructor( coroutineScope.launch { selectedMember = event.roomMember if (event.roomMember.membership == RoomMembershipState.BAN && canBan()) { - unbanUserAsyncAction.value = AsyncAction.Confirming + unbanUserAsyncAction.value = AsyncAction.ConfirmingNoParams } else { moderationActions = buildList { add(ModerationAction.DisplayProfile(event.roomMember.userId)) @@ -109,7 +109,7 @@ class RoomMembersModerationPresenter @Inject constructor( coroutineScope.banUser(it.userId, banUserAsyncAction) } } else { - banUserAsyncAction.value = AsyncAction.Confirming + banUserAsyncAction.value = AsyncAction.ConfirmingNoParams } } is RoomMembersModerationEvents.UnbanUser -> { @@ -119,7 +119,7 @@ class RoomMembersModerationPresenter @Inject constructor( coroutineScope.unbanUser(it.userId, unbanUserAsyncAction) } } else { - unbanUserAsyncAction.value = AsyncAction.Confirming + unbanUserAsyncAction.value = AsyncAction.ConfirmingNoParams } } is RoomMembersModerationEvents.Reset -> { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStatePreviewProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStatePreviewProvider.kt index d7fb509995..2642cc501a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStatePreviewProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStatePreviewProvider.kt @@ -56,11 +56,11 @@ class RoomMembersModerationStatePreviewProvider : PreviewParameterProvider { - changeOwnRoleAction.value = AsyncAction.Confirming + changeOwnRoleAction.value = AsyncAction.ConfirmingNoParams } is RolesAndPermissionsEvents.CancelPendingAction -> { changeOwnRoleAction.value = AsyncAction.Uninitialized @@ -77,7 +77,7 @@ class RolesAndPermissionsPresenter @Inject constructor( is RolesAndPermissionsEvents.ResetPermissions -> if (resetPermissionsAction.value.isConfirming()) { coroutineScope.resetPermissions(resetPermissionsAction) } else { - resetPermissionsAction.value = AsyncAction.Confirming + resetPermissionsAction.value = AsyncAction.ConfirmingNoParams } } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsStateProvider.kt index 9dbaa3da96..53cd602c8b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsStateProvider.kt @@ -18,7 +18,7 @@ class RolesAndPermissionsStateProvider : PreviewParameterProvider { if (role == RoomMember.Role.ADMIN && selectedUsers != usersWithRole && !saveState.value.isConfirming()) { // Confirm adding admin - saveState.value = AsyncAction.Confirming + saveState.value = AsyncAction.ConfirmingNoParams } else if (!saveState.value.isLoading()) { coroutineScope.save(usersWithRole.value, selectedUsers, saveState) } @@ -145,7 +145,7 @@ class ChangeRolesPresenter @AssistedInject constructor( is ChangeRolesEvent.Exit -> { exitState.value = if (exitState.value.isUninitialized() && hasPendingChanges) { // Has pending changes, confirm exit - AsyncAction.Confirming + AsyncAction.ConfirmingNoParams } else { // No pending changes, exit immediately AsyncAction.Success(Unit) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesStateProvider.kt index b48677423e..82a8f0696b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesStateProvider.kt @@ -36,8 +36,8 @@ class ChangeRolesStateProvider : PreviewParameterProvider { searchResults = SearchBarResultState.Results(MembersByRole(aRoomMemberList().take(1).toImmutableList())), selectedUsers = aMatrixUserList().take(1).toImmutableList(), ), - aChangeRolesStateWithSelectedUsers().copy(exitState = AsyncAction.Confirming), - aChangeRolesStateWithSelectedUsers().copy(savingState = AsyncAction.Confirming), + aChangeRolesStateWithSelectedUsers().copy(exitState = AsyncAction.ConfirmingNoParams), + aChangeRolesStateWithSelectedUsers().copy(savingState = AsyncAction.ConfirmingNoParams), aChangeRolesStateWithSelectedUsers().copy(savingState = AsyncAction.Loading), aChangeRolesStateWithSelectedUsers().copy(savingState = AsyncAction.Success(Unit)), aChangeRolesStateWithSelectedUsers().copy(savingState = AsyncAction.Failure(Exception("boom"))), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt index ba69ed2b08..882ca58be1 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt @@ -95,7 +95,7 @@ class ChangeRoomPermissionsPresenter @AssistedInject constructor( confirmExitAction = if (!hasChanges || confirmExitAction.isConfirming()) { AsyncAction.Success(Unit) } else { - AsyncAction.Confirming + AsyncAction.ConfirmingNoParams } } is ChangeRoomPermissionsEvent.ResetPendingActions -> { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStatePreviewProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStatePreviewProvider.kt index d6099dc7f8..d916131f30 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStatePreviewProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStatePreviewProvider.kt @@ -26,7 +26,7 @@ class ChangeRoomPermissionsStatePreviewProvider : PreviewParameterProvider() rule.setRolesAndPermissionsView( state = aRolesAndPermissionsState( - resetPermissionsAction = AsyncAction.Confirming, + resetPermissionsAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), ) @@ -111,7 +111,7 @@ class RolesAndPermissionsViewTest { val recorder = EventsRecorder() rule.setRolesAndPermissionsView( state = aRolesAndPermissionsState( - resetPermissionsAction = AsyncAction.Confirming, + resetPermissionsAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), ) @@ -124,7 +124,7 @@ class RolesAndPermissionsViewTest { val recorder = EventsRecorder() rule.setRolesAndPermissionsView( state = aRolesAndPermissionsState( - changeOwnRoleAction = AsyncAction.Confirming, + changeOwnRoleAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), ) @@ -138,7 +138,7 @@ class RolesAndPermissionsViewTest { val recorder = EventsRecorder() rule.setRolesAndPermissionsView( state = aRolesAndPermissionsState( - changeOwnRoleAction = AsyncAction.Confirming, + changeOwnRoleAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), ) @@ -152,7 +152,7 @@ class RolesAndPermissionsViewTest { val recorder = EventsRecorder() rule.setRolesAndPermissionsView( state = aRolesAndPermissionsState( - changeOwnRoleAction = AsyncAction.Confirming, + changeOwnRoleAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), ) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesPresenterTest.kt index bff07e06fc..632cd051af 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesPresenterTest.kt @@ -229,7 +229,7 @@ class ChangeRolesPresenterTest { awaitItem().eventSink(ChangeRolesEvent.Exit) val confirmingState = awaitItem() - assertThat(confirmingState.exitState).isEqualTo(AsyncAction.Confirming) + assertThat(confirmingState.exitState).isEqualTo(AsyncAction.ConfirmingNoParams) confirmingState.eventSink(ChangeRolesEvent.CancelExit) assertThat(awaitItem().exitState).isEqualTo(AsyncAction.Uninitialized) @@ -257,7 +257,7 @@ class ChangeRolesPresenterTest { skipItems(1) updatedState.eventSink(ChangeRolesEvent.Exit) - assertThat(awaitItem().exitState).isEqualTo(AsyncAction.Confirming) + assertThat(awaitItem().exitState).isEqualTo(AsyncAction.ConfirmingNoParams) updatedState.eventSink(ChangeRolesEvent.Exit) assertThat(awaitItem().exitState).isEqualTo(AsyncAction.Success(Unit)) @@ -284,7 +284,7 @@ class ChangeRolesPresenterTest { initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2))) awaitItem().eventSink(ChangeRolesEvent.Save) val confirmingState = awaitItem() - assertThat(confirmingState.savingState).isEqualTo(AsyncAction.Confirming) + assertThat(confirmingState.savingState).isEqualTo(AsyncAction.ConfirmingNoParams) confirmingState.eventSink(ChangeRolesEvent.Save) assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Success(Unit)) @@ -309,7 +309,7 @@ class ChangeRolesPresenterTest { awaitItem().eventSink(ChangeRolesEvent.Save) val confirmingState = awaitItem() - assertThat(confirmingState.savingState).isEqualTo(AsyncAction.Confirming) + assertThat(confirmingState.savingState).isEqualTo(AsyncAction.ConfirmingNoParams) confirmingState.eventSink(ChangeRolesEvent.CancelSave) assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Uninitialized) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesViewTest.kt index 949d102340..8615cb5209 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesViewTest.kt @@ -140,7 +140,7 @@ class ChangeRolesViewTest { rule.setChangeRolesContent( state = aChangeRolesState( isSearchActive = true, - exitState = AsyncAction.Confirming, + exitState = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder, ), ) @@ -156,7 +156,7 @@ class ChangeRolesViewTest { rule.setChangeRolesContent( state = aChangeRolesState( isSearchActive = true, - exitState = AsyncAction.Confirming, + exitState = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder, ), ) @@ -173,7 +173,7 @@ class ChangeRolesViewTest { state = aChangeRolesState( role = RoomMember.Role.ADMIN, isSearchActive = true, - savingState = AsyncAction.Confirming, + savingState = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder, ), ) @@ -190,7 +190,7 @@ class ChangeRolesViewTest { state = aChangeRolesState( role = RoomMember.Role.ADMIN, isSearchActive = true, - savingState = AsyncAction.Confirming, + savingState = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder, ), ) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/permissions/ChangeRoomPermissionsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/permissions/ChangeRoomPermissionsPresenterTest.kt index b57d8df1ab..f963ba28c2 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/permissions/ChangeRoomPermissionsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/permissions/ChangeRoomPermissionsPresenterTest.kt @@ -267,7 +267,7 @@ class ChangeRoomPermissionsPresenterTest { assertThat(awaitItem().hasChanges).isTrue() state.eventSink(ChangeRoomPermissionsEvent.Exit) - assertThat(awaitItem().confirmExitAction).isEqualTo(AsyncAction.Confirming) + assertThat(awaitItem().confirmExitAction).isEqualTo(AsyncAction.ConfirmingNoParams) state.eventSink(ChangeRoomPermissionsEvent.Exit) assertThat(awaitItem().confirmExitAction).isEqualTo(AsyncAction.Success(Unit)) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/permissions/ChangeRoomPermissionsViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/permissions/ChangeRoomPermissionsViewTest.kt index f85e59ea32..a73973609a 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/permissions/ChangeRoomPermissionsViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/permissions/ChangeRoomPermissionsViewTest.kt @@ -82,7 +82,7 @@ class ChangeRoomPermissionsViewTest { state = aChangeRoomPermissionsState( section = ChangeRoomPermissionsSection.RoomDetails, hasChanges = true, - confirmExitAction = AsyncAction.Confirming, + confirmExitAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), eventsRecorder = recorder, @@ -98,7 +98,7 @@ class ChangeRoomPermissionsViewTest { state = aChangeRoomPermissionsState( section = ChangeRoomPermissionsSection.RoomDetails, hasChanges = true, - confirmExitAction = AsyncAction.Confirming, + confirmExitAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), eventsRecorder = recorder, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt index def6e5c2d3..4e661e0a13 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt @@ -40,7 +40,7 @@ class SecureBackupDisablePresenter @Inject constructor( is SecureBackupDisableEvents.DisableBackup -> if (disableAction.value.isConfirming()) { coroutineScope.disableBackup(disableAction) } else { - disableAction.value = AsyncAction.Confirming + disableAction.value = AsyncAction.ConfirmingNoParams } SecureBackupDisableEvents.DismissDialogs -> { disableAction.value = AsyncAction.Uninitialized 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/SecureBackupDisableStateProvider.kt index 8c47eeb0a3..3c2969e35e 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/SecureBackupDisableStateProvider.kt @@ -15,7 +15,7 @@ open class SecureBackupDisableStateProvider : PreviewParameterProvider get() = sequenceOf( aSecureBackupDisableState(), - aSecureBackupDisableState(disableAction = AsyncAction.Confirming), + aSecureBackupDisableState(disableAction = AsyncAction.ConfirmingNoParams), aSecureBackupDisableState(disableAction = AsyncAction.Loading), aSecureBackupDisableState(disableAction = AsyncAction.Failure(Exception("Failed to disable"))), // Add other states here diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt index 5a3d1caab9..b0d6399c04 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt @@ -47,7 +47,7 @@ class SecureBackupDisablePresenterTest { val initialState = awaitItem() initialState.eventSink(SecureBackupDisableEvents.DisableBackup) val state = awaitItem() - assertThat(state.disableAction).isEqualTo(AsyncAction.Confirming) + assertThat(state.disableAction).isEqualTo(AsyncAction.ConfirmingNoParams) initialState.eventSink(SecureBackupDisableEvents.DismissDialogs) val finalState = awaitItem() assertThat(finalState.disableAction).isEqualTo(AsyncAction.Uninitialized) @@ -64,7 +64,7 @@ class SecureBackupDisablePresenterTest { assertThat(initialState.disableAction).isEqualTo(AsyncAction.Uninitialized) initialState.eventSink(SecureBackupDisableEvents.DisableBackup) val state = awaitItem() - assertThat(state.disableAction).isEqualTo(AsyncAction.Confirming) + assertThat(state.disableAction).isEqualTo(AsyncAction.ConfirmingNoParams) initialState.eventSink(SecureBackupDisableEvents.DisableBackup) val loadingState = awaitItem() assertThat(loadingState.disableAction).isInstanceOf(AsyncAction.Loading::class.java) @@ -88,7 +88,7 @@ class SecureBackupDisablePresenterTest { assertThat(initialState.disableAction).isEqualTo(AsyncAction.Uninitialized) initialState.eventSink(SecureBackupDisableEvents.DisableBackup) val state = awaitItem() - assertThat(state.disableAction).isEqualTo(AsyncAction.Confirming) + assertThat(state.disableAction).isEqualTo(AsyncAction.ConfirmingNoParams) initialState.eventSink(SecureBackupDisableEvents.DisableBackup) val loadingState = awaitItem() assertThat(loadingState.disableAction).isInstanceOf(AsyncAction.Loading::class.java) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt index 2364688c03..5b0c9105ad 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt @@ -158,7 +158,7 @@ fun VerifySelfSessionView( latestOnSuccessLogout(state.signOutAction.data) } } - AsyncAction.Confirming, + is AsyncAction.Confirming, is AsyncAction.Failure, AsyncAction.Uninitialized -> Unit } diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/AsyncAction.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/AsyncAction.kt index 48fe1d336b..e674a170bd 100644 --- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/AsyncAction.kt +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/AsyncAction.kt @@ -26,7 +26,9 @@ sealed interface AsyncAction { /** * Represents an operation that is currently waiting for user confirmation. */ - data object Confirming : AsyncAction + interface Confirming : AsyncAction + + data object ConfirmingNoParams : Confirming /** * Represents an operation that is currently ongoing. @@ -70,7 +72,7 @@ sealed interface AsyncAction { fun isUninitialized(): Boolean = this == Uninitialized - fun isConfirming(): Boolean = this == Confirming + fun isConfirming(): Boolean = this is Confirming fun isLoading(): Boolean = this == Loading 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/AsyncActionProvider.kt index 97b2dc2bdf..87cb43af75 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/AsyncActionProvider.kt @@ -14,7 +14,7 @@ open class AsyncActionProvider : PreviewParameterProvider> { override val values: Sequence> get() = sequenceOf( AsyncAction.Uninitialized, - AsyncAction.Confirming, + AsyncAction.ConfirmingNoParams, AsyncAction.Loading, AsyncAction.Failure(Exception("An error occurred")), AsyncAction.Success(Unit), 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 be3e2ef7d8..15accbd36c 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 @@ -42,7 +42,7 @@ fun AsyncActionView( ) { when (async) { AsyncAction.Uninitialized -> Unit - AsyncAction.Confirming -> confirmationDialog() + is AsyncAction.Confirming -> confirmationDialog() is AsyncAction.Loading -> progressDialog() is AsyncAction.Failure -> { if (onRetry == null) { 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 c9b8b1b5fd..5d9220c3bd 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 @@ -122,7 +122,7 @@ private fun ColumnScope.TroubleshootTestView( private fun ColumnScope.TroubleshootNotificationsContent(state: TroubleshootNotificationsState) { when (state.testSuiteState.mainState) { AsyncAction.Loading, - AsyncAction.Confirming, + is AsyncAction.Confirming, is AsyncAction.Success, is AsyncAction.Failure -> { TestSuiteView( @@ -150,7 +150,7 @@ private fun ColumnScope.TroubleshootNotificationsContent(state: TroubleshootNoti }) RunTestButton(state = state) } - AsyncAction.Confirming -> { + is AsyncAction.Confirming -> { ListItem(headlineContent = { Text( text = stringResource(id = R.string.troubleshoot_notifications_screen_waiting) diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootTestSuite.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootTestSuite.kt index 8bea320553..9b7de10325 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootTestSuite.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootTestSuite.kt @@ -102,7 +102,7 @@ fun List.computeMainState(): AsyncAction AsyncAction.Loading else -> { if (any { it.status is NotificationTroubleshootTestState.Status.WaitingForUser }) { - AsyncAction.Confirming + AsyncAction.ConfirmingNoParams } else if (any { it.status is NotificationTroubleshootTestState.Status.Failure }) { AsyncAction.Failure(Exception("Some tests failed")) } else { From f2df8a2064dcf84c7e636ab40355cdbf1ee90c48 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Oct 2024 11:02:47 +0200 Subject: [PATCH 05/67] AsyncActionView: provide the value to the confirmationDialog lambda. --- .../designsystem/components/async/AsyncActionView.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 15accbd36c..412c033e9f 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 @@ -34,7 +34,7 @@ fun AsyncActionView( async: AsyncAction, onSuccess: (T) -> Unit, onErrorDismiss: () -> Unit, - confirmationDialog: @Composable () -> Unit = { }, + confirmationDialog: @Composable (AsyncAction.Confirming) -> Unit = { }, progressDialog: @Composable () -> Unit = { AsyncActionViewDefaults.ProgressDialog() }, errorTitle: @Composable (Throwable) -> String = { ErrorDialogDefaults.title }, errorMessage: @Composable (Throwable) -> String = { it.message ?: it.toString() }, @@ -42,7 +42,7 @@ fun AsyncActionView( ) { when (async) { AsyncAction.Uninitialized -> Unit - is AsyncAction.Confirming -> confirmationDialog() + is AsyncAction.Confirming -> confirmationDialog(async) is AsyncAction.Loading -> progressDialog() is AsyncAction.Failure -> { if (onRetry == null) { From e4907694445e1d51a3a5f6797992354a02951da2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Oct 2024 11:19:18 +0200 Subject: [PATCH 06/67] AcceptDeclineInviteState: create ConfirmingDeclineInvite to host InviteData when confirming decline of invite. --- .../api/response/AcceptDeclineInviteState.kt | 2 -- .../AcceptDeclineInviteStateProvider.kt | 13 +++------- .../api/response/ConfirmingDeclineInvite.kt | 14 ++++++++++ .../response/AcceptDeclineInvitePresenter.kt | 24 +++-------------- .../impl/response/AcceptDeclineInviteView.kt | 12 ++++----- .../InternalAcceptDeclineInviteEvents.kt | 3 ++- .../AcceptDeclineInvitePresenterTest.kt | 26 ++++++------------- .../matrix/test/room/FakeInvitedRoom.kt | 5 ++-- 8 files changed, 40 insertions(+), 59 deletions(-) create mode 100644 features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/ConfirmingDeclineInvite.kt diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteState.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteState.kt index e0e369e481..7734b3bb29 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteState.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteState.kt @@ -9,10 +9,8 @@ package io.element.android.features.invite.api.response import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId -import java.util.Optional data class AcceptDeclineInviteState( - val invite: Optional, val acceptAction: AsyncAction, val declineAction: AsyncAction, val eventSink: (AcceptDeclineInviteEvents) -> Unit, diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt index 4c377590e6..a4f3695369 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt @@ -10,23 +10,20 @@ package io.element.android.features.invite.api.response import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId -import java.util.Optional open class AcceptDeclineInviteStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAcceptDeclineInviteState(), anAcceptDeclineInviteState( - invite = Optional.of( - InviteData(RoomId("!room:matrix.org"), isDm = true, roomName = "Alice"), + declineAction = ConfirmingDeclineInvite( + InviteData(RoomId("!room:matrix.org"), isDm = true, roomName = "Alice") ), - declineAction = AsyncAction.ConfirmingNoParams, ), anAcceptDeclineInviteState( - invite = Optional.of( - InviteData(RoomId("!room:matrix.org"), isDm = false, roomName = "Some room"), + declineAction = ConfirmingDeclineInvite( + InviteData(RoomId("!room:matrix.org"), isDm = false, roomName = "Some room") ), - declineAction = AsyncAction.ConfirmingNoParams, ), anAcceptDeclineInviteState( acceptAction = AsyncAction.Failure(Throwable("Whoops")), @@ -38,12 +35,10 @@ open class AcceptDeclineInviteStateProvider : PreviewParameterProvider = Optional.empty(), acceptAction: AsyncAction = AsyncAction.Uninitialized, declineAction: AsyncAction = AsyncAction.Uninitialized, eventSink: (AcceptDeclineInviteEvents) -> Unit = {} ) = AcceptDeclineInviteState( - invite = invite, acceptAction = acceptAction, declineAction = declineAction, eventSink = eventSink, diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/ConfirmingDeclineInvite.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/ConfirmingDeclineInvite.kt new file mode 100644 index 0000000000..c2231019c8 --- /dev/null +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/ConfirmingDeclineInvite.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.invite.api.response + +import io.element.android.libraries.architecture.AsyncAction + +data class ConfirmingDeclineInvite( + val inviteData: InviteData, +) : AsyncAction.Confirming diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt index 5ebf3cbb76..082120c6f3 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt @@ -9,15 +9,13 @@ package io.element.android.features.invite.impl.response import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents import io.element.android.features.invite.api.response.AcceptDeclineInviteState -import io.element.android.features.invite.api.response.InviteData +import io.element.android.features.invite.api.response.ConfirmingDeclineInvite import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -29,9 +27,7 @@ import io.element.android.libraries.matrix.api.room.join.JoinRoom import io.element.android.libraries.push.api.notifications.NotificationCleaner import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import java.util.Optional import javax.inject.Inject -import kotlin.jvm.optionals.getOrNull class AcceptDeclineInvitePresenter @Inject constructor( private val client: MatrixClient, @@ -43,35 +39,22 @@ class AcceptDeclineInvitePresenter @Inject constructor( val localCoroutineScope = rememberCoroutineScope() val acceptedAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val declinedAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - var currentInvite by remember { - mutableStateOf>(Optional.empty()) - } fun handleEvents(event: AcceptDeclineInviteEvents) { when (event) { is AcceptDeclineInviteEvents.AcceptInvite -> { - // currentInvite is used to render the decline confirmation dialog - // and to reuse the roomId when the user confirm the rejection of the invitation. - // Just set it to empty here. - currentInvite = Optional.empty() localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction) } is AcceptDeclineInviteEvents.DeclineInvite -> { - currentInvite = Optional.of(event.invite) - declinedAction.value = AsyncAction.ConfirmingNoParams + declinedAction.value = ConfirmingDeclineInvite(event.invite) } is InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite -> { - declinedAction.value = AsyncAction.Uninitialized - currentInvite.getOrNull()?.let { - localCoroutineScope.declineInvite(it.roomId, declinedAction) - } - currentInvite = Optional.empty() + localCoroutineScope.declineInvite(event.roomId, declinedAction) } is InternalAcceptDeclineInviteEvents.CancelDeclineInvite -> { - currentInvite = Optional.empty() declinedAction.value = AsyncAction.Uninitialized } @@ -86,7 +69,6 @@ class AcceptDeclineInvitePresenter @Inject constructor( } return AcceptDeclineInviteState( - invite = currentInvite, acceptAction = acceptedAction.value, declineAction = declinedAction.value, eventSink = ::handleEvents diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt index 94593d0764..ecbc601352 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.invite.api.response.AcceptDeclineInviteState import io.element.android.features.invite.api.response.AcceptDeclineInviteStateProvider +import io.element.android.features.invite.api.response.ConfirmingDeclineInvite import io.element.android.features.invite.api.response.InviteData import io.element.android.features.invite.impl.R import io.element.android.libraries.designsystem.components.async.AsyncActionView @@ -22,7 +23,6 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.ui.strings.CommonStrings -import kotlin.jvm.optionals.getOrNull @Composable fun AcceptDeclineInviteView( @@ -45,13 +45,13 @@ fun AcceptDeclineInviteView( onErrorDismiss = { state.eventSink(InternalAcceptDeclineInviteEvents.DismissDeclineError) }, - confirmationDialog = { - val invite = state.invite.getOrNull() - if (invite != null) { + confirmationDialog = { confirming -> + // Note: confirming will always be of type ConfirmingDeclineInvite. + if (confirming is ConfirmingDeclineInvite) { DeclineConfirmationDialog( - invite = invite, + invite = confirming.inviteData, onConfirmClick = { - state.eventSink(InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite) + state.eventSink(InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite(confirming.inviteData.roomId)) }, onDismissClick = { state.eventSink(InternalAcceptDeclineInviteEvents.CancelDeclineInvite) diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/InternalAcceptDeclineInviteEvents.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/InternalAcceptDeclineInviteEvents.kt index 779106e1c3..60b96805e6 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/InternalAcceptDeclineInviteEvents.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/InternalAcceptDeclineInviteEvents.kt @@ -8,9 +8,10 @@ package io.element.android.features.invite.impl.response import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents +import io.element.android.libraries.matrix.api.core.RoomId sealed interface InternalAcceptDeclineInviteEvents : AcceptDeclineInviteEvents { - data object ConfirmDeclineInvite : InternalAcceptDeclineInviteEvents + data class ConfirmDeclineInvite(val roomId: RoomId) : InternalAcceptDeclineInviteEvents data object CancelDeclineInvite : InternalAcceptDeclineInviteEvents data object DismissAcceptError : InternalAcceptDeclineInviteEvents data object DismissDeclineError : InternalAcceptDeclineInviteEvents diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt index f194404f8e..6c1057638f 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt @@ -10,6 +10,7 @@ package io.element.android.features.invite.impl.response import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents +import io.element.android.features.invite.api.response.ConfirmingDeclineInvite import io.element.android.features.invite.api.response.InviteData import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.MatrixClient @@ -33,7 +34,6 @@ import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -import java.util.Optional class AcceptDeclineInvitePresenterTest { @get:Rule @@ -46,7 +46,6 @@ class AcceptDeclineInvitePresenterTest { awaitItem().also { state -> assertThat(state.acceptAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java) - assertThat(state.invite).isEqualTo(Optional.empty()) } } } @@ -61,17 +60,13 @@ class AcceptDeclineInvitePresenterTest { AcceptDeclineInviteEvents.DeclineInvite(inviteData) ) } - skipItems(1) awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.of(inviteData)) - assertThat(state.declineAction).isInstanceOf(AsyncAction.Confirming::class.java) + assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData)) state.eventSink( InternalAcceptDeclineInviteEvents.CancelDeclineInvite ) } - skipItems(1) awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.empty()) assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java) } } @@ -93,22 +88,20 @@ class AcceptDeclineInvitePresenterTest { AcceptDeclineInviteEvents.DeclineInvite(inviteData) ) } - skipItems(1) awaitItem().also { state -> + assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData)) state.eventSink( - InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite + InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite(inviteData.roomId) ) } - skipItems(2) + assertThat(awaitItem().declineAction.isLoading()).isTrue() awaitItem().also { state -> assertThat(state.declineAction).isInstanceOf(AsyncAction.Failure::class.java) state.eventSink( InternalAcceptDeclineInviteEvents.DismissDeclineError ) } - skipItems(1) awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.empty()) assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java) } cancelAndConsumeRemainingEvents() @@ -141,13 +134,13 @@ class AcceptDeclineInvitePresenterTest { AcceptDeclineInviteEvents.DeclineInvite(inviteData) ) } - skipItems(1) awaitItem().also { state -> + assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData)) state.eventSink( - InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite + InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite(inviteData.roomId) ) } - skipItems(2) + assertThat(awaitItem().declineAction.isLoading()).isTrue() awaitItem().also { state -> assertThat(state.declineAction).isInstanceOf(AsyncAction.Success::class.java) } @@ -173,7 +166,6 @@ class AcceptDeclineInvitePresenterTest { ) } awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.empty()) assertThat(state.acceptAction).isEqualTo(AsyncAction.Loading) } awaitItem().also { state -> @@ -183,7 +175,6 @@ class AcceptDeclineInvitePresenterTest { ) } awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.empty()) assertThat(state.acceptAction).isInstanceOf(AsyncAction.Uninitialized::class.java) } cancelAndConsumeRemainingEvents() @@ -220,7 +211,6 @@ class AcceptDeclineInvitePresenterTest { ) } awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.empty()) assertThat(state.acceptAction).isEqualTo(AsyncAction.Loading) } awaitItem().also { state -> diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt index 3224d1fd2f..06416e2c80 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt @@ -13,14 +13,15 @@ import io.element.android.libraries.matrix.api.room.InvitedRoom import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.tests.testutils.simulateLongTask class FakeInvitedRoom( override val sessionId: SessionId = A_SESSION_ID, override val roomId: RoomId = A_ROOM_ID, private val declineInviteResult: () -> Result = { lambdaError() } ) : InvitedRoom { - override suspend fun declineInvite(): Result { - return declineInviteResult() + override suspend fun declineInvite(): Result = simulateLongTask { + declineInviteResult() } override fun close() = Unit From 964fbe4de3177730df227a05d5dbfd467ab6b67e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Oct 2024 11:35:58 +0200 Subject: [PATCH 07/67] Split long lines --- .../ChangeRoomPermissionsStatePreviewProvider.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStatePreviewProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStatePreviewProvider.kt index d916131f30..9dcaa15b43 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStatePreviewProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStatePreviewProvider.kt @@ -26,7 +26,11 @@ class ChangeRoomPermissionsStatePreviewProvider : PreviewParameterProvider Date: Thu, 10 Oct 2024 14:11:41 +0200 Subject: [PATCH 08/67] No need to launch a coroutine here. --- .../roomdetails/impl/members/RoomMemberListPresenter.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt index c6fd2bc0b8..48df1a6ae4 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt @@ -14,7 +14,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dagger.assisted.Assisted @@ -33,7 +32,6 @@ import io.element.android.libraries.matrix.api.room.powerlevels.canInvite import io.element.android.libraries.matrix.api.room.roomMembers import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class RoomMemberListPresenter @AssistedInject constructor( @@ -50,7 +48,6 @@ class RoomMemberListPresenter @AssistedInject constructor( @Composable override fun present(): RoomMemberListState { - val coroutineScope = rememberCoroutineScope() var roomMembers: AsyncData by remember { mutableStateOf(AsyncData.Loading()) } var searchQuery by rememberSaveable { mutableStateOf("") } var searchResults by remember { @@ -135,13 +132,12 @@ class RoomMemberListPresenter @AssistedInject constructor( when (event) { is RoomMemberListEvents.OnSearchActiveChanged -> isSearchActive = event.active is RoomMemberListEvents.UpdateSearchQuery -> searchQuery = event.query - is RoomMemberListEvents.RoomMemberSelected -> coroutineScope.launch { + is RoomMemberListEvents.RoomMemberSelected -> if (roomModerationState.canDisplayModerationActions) { roomModerationState.eventSink(RoomMembersModerationEvents.SelectRoomMember(event.roomMember)) } else { navigator.openRoomMemberDetails(event.roomMember.userId) } - } } } From d7ac725bc29d34a11a14e01504157b5ab37a4cd2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 10 Oct 2024 12:56:21 +0200 Subject: [PATCH 09/67] Ensure selectedRoomMember is not null to reduce code indentation. --- .../moderation/RoomMembersModerationView.kt | 148 +++++++++--------- 1 file changed, 73 insertions(+), 75 deletions(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationView.kt index d420c98a37..60938627c8 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationView.kt @@ -62,7 +62,7 @@ fun RoomMembersModerationView( modifier: Modifier = Modifier, ) { Box(modifier = modifier) { - if (state.actions.isNotEmpty()) { + if (state.selectedRoomMember != null && state.actions.isNotEmpty()) { RoomMemberActionsBottomSheet( roomMember = state.selectedRoomMember, actions = state.actions, @@ -186,97 +186,95 @@ fun RoomMembersModerationView( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun RoomMemberActionsBottomSheet( - roomMember: RoomMember?, + roomMember: RoomMember, actions: ImmutableList, onSelectAction: (ModerationAction) -> Unit, onDismiss: () -> Unit, ) { val coroutineScope = rememberCoroutineScope() - if (roomMember != null && actions.isNotEmpty()) { - val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - ModalBottomSheet( - modifier = Modifier.systemBarsPadding(), - sheetState = bottomSheetState, - onDismissRequest = { - coroutineScope.launch { - bottomSheetState.hide() - onDismiss() - } - }, + val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + ModalBottomSheet( + modifier = Modifier.systemBarsPadding(), + sheetState = bottomSheetState, + onDismissRequest = { + coroutineScope.launch { + bottomSheetState.hide() + onDismiss() + } + }, + ) { + Column( + modifier = Modifier.padding(vertical = 16.dp) ) { - Column( - modifier = Modifier.padding(vertical = 16.dp) - ) { - Avatar( - avatarData = roomMember.getAvatarData(size = AvatarSize.RoomListManageUser), - modifier = Modifier - .padding(bottom = 28.dp) - .align(Alignment.CenterHorizontally) - ) - roomMember.displayName?.let { - Text( - text = it, - style = ElementTheme.typography.fontHeadingLgBold, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - textAlign = TextAlign.Center, - modifier = Modifier - .padding(start = 16.dp, end = 16.dp, bottom = 8.dp) - .fillMaxWidth() - ) - } + Avatar( + avatarData = roomMember.getAvatarData(size = AvatarSize.RoomListManageUser), + modifier = Modifier + .padding(bottom = 28.dp) + .align(Alignment.CenterHorizontally) + ) + roomMember.displayName?.let { Text( - text = roomMember.userId.toString(), - style = ElementTheme.typography.fontBodyLgRegular, - color = ElementTheme.colors.textSecondary, + text = it, + style = ElementTheme.typography.fontHeadingLgBold, maxLines = 1, overflow = TextOverflow.Ellipsis, textAlign = TextAlign.Center, modifier = Modifier - .padding(horizontal = 16.dp) + .padding(start = 16.dp, end = 16.dp, bottom = 8.dp) .fillMaxWidth() ) - Spacer(modifier = Modifier.height(32.dp)) + } + Text( + text = roomMember.userId.toString(), + style = ElementTheme.typography.fontBodyLgRegular, + color = ElementTheme.colors.textSecondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + textAlign = TextAlign.Center, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth() + ) + Spacer(modifier = Modifier.height(32.dp)) - for (action in actions) { - when (action) { - is ModerationAction.DisplayProfile -> { - ListItem( - headlineContent = { Text(stringResource(R.string.screen_room_member_list_manage_member_user_info)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Info())), - onClick = { - coroutineScope.launch { - onSelectAction(action) - bottomSheetState.hide() - } + for (action in actions) { + when (action) { + is ModerationAction.DisplayProfile -> { + ListItem( + headlineContent = { Text(stringResource(R.string.screen_room_member_list_manage_member_user_info)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Info())), + onClick = { + coroutineScope.launch { + onSelectAction(action) + bottomSheetState.hide() } - ) - } - is ModerationAction.KickUser -> { - ListItem( - headlineContent = { Text(stringResource(R.string.screen_room_member_list_manage_member_remove)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())), - onClick = { - coroutineScope.launch { - bottomSheetState.hide() - onSelectAction(action) - } + } + ) + } + is ModerationAction.KickUser -> { + ListItem( + headlineContent = { Text(stringResource(R.string.screen_room_member_list_manage_member_remove)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())), + onClick = { + coroutineScope.launch { + bottomSheetState.hide() + onSelectAction(action) } - ) - } - is ModerationAction.BanUser -> { - ListItem( - headlineContent = { Text(stringResource(R.string.screen_room_member_list_manage_member_remove_confirmation_ban)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())), - style = ListItemStyle.Destructive, - onClick = { - coroutineScope.launch { - bottomSheetState.hide() - onSelectAction(action) - } + } + ) + } + is ModerationAction.BanUser -> { + ListItem( + headlineContent = { Text(stringResource(R.string.screen_room_member_list_manage_member_remove_confirmation_ban)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())), + style = ListItemStyle.Destructive, + onClick = { + coroutineScope.launch { + bottomSheetState.hide() + onSelectAction(action) } - ) - } + } + ) } } } From ccaa34de831c98d0d1d7ec0257032009f1cf5d91 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 10 Oct 2024 14:10:18 +0200 Subject: [PATCH 10/67] Fix issue on canInvite refresh. --- .../roomdetails/impl/members/RoomMemberListPresenter.kt | 8 +++----- .../android/libraries/matrix/ui/room/MatrixRoomState.kt | 8 ++++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt index c6fd2bc0b8..8da03e7aec 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt @@ -12,7 +12,6 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable @@ -29,8 +28,8 @@ import io.element.android.libraries.designsystem.theme.components.SearchBarResul import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.RoomMembershipState -import io.element.android.libraries.matrix.api.room.powerlevels.canInvite import io.element.android.libraries.matrix.api.room.roomMembers +import io.element.android.libraries.matrix.ui.room.canInviteAsState import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -59,9 +58,8 @@ class RoomMemberListPresenter @AssistedInject constructor( var isSearchActive by rememberSaveable { mutableStateOf(false) } val membersState by room.membersStateFlow.collectAsState() - val canInvite by produceState(initialValue = false, key1 = membersState) { - value = room.canInvite().getOrElse { false } - } + val syncUpdateFlow = room.syncUpdateFlow.collectAsState() + val canInvite by room.canInviteAsState(syncUpdateFlow.value) val roomModerationState = roomMembersModerationPresenter.present() diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt index 3197fd7c02..3dd81344fc 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.produceState import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.powerlevels.canInvite import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage @@ -26,6 +27,13 @@ fun MatrixRoom.canSendMessageAsState(type: MessageEventType, updateKey: Long): S } } +@Composable +fun MatrixRoom.canInviteAsState(updateKey: Long): State { + return produceState(initialValue = false, key1 = updateKey) { + value = canInvite().getOrElse { false } + } +} + @Composable fun MatrixRoom.canRedactOwnAsState(updateKey: Long): State { return produceState(initialValue = false, key1 = updateKey) { From 2ff3d9cf726fe7cfc763b624554feb92bff29f9a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:50:37 +0000 Subject: [PATCH 11/67] Update dependency io.sentry:sentry-android to v7.15.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 06870e8f61..0bbe821690 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -191,7 +191,7 @@ zxing_cpp = "io.github.zxing-cpp:android:2.2.0" # Analytics posthog = "com.posthog:posthog-android:3.8.2" -sentry = "io.sentry:sentry-android:7.14.0" +sentry = "io.sentry:sentry-android:7.15.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.25.0" From 8ec78b8eef05005610d89247d9c885e5217ea79e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 15 Oct 2024 08:55:33 +0200 Subject: [PATCH 12/67] Fix typo in the description. --- fastlane/metadata/android/en-US/full_description.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt index b0863151ea..2c30284eb9 100644 --- a/fastlane/metadata/android/en-US/full_description.txt +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -2,7 +2,7 @@ Element X brings you both sovereign & seamless collaboration built on Matrix. The collaboration capabilities include chat & video calls with the modern set of features such as: • public & private channels - • room moderation & access conUpdatetrol + • room moderation & access control • replies, reactions, polls, read receipts, pinned messages, etc. • simultaneous chat & calls (picture in picture) • decentralized & federated communication across organizations From 46a719168a4a956d3133223d7b3f642f0e4b5a14 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 15 Oct 2024 08:56:07 +0200 Subject: [PATCH 13/67] Add permission rationale (required by Google and already done on the PlayStore console) --- fastlane/metadata/android/en-US/full_description.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt index 2c30284eb9..f3708552d3 100644 --- a/fastlane/metadata/android/en-US/full_description.txt +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -32,4 +32,8 @@ Enjoy the freedom of the Matrix open standard! You have native interoperability Enjoy your right to private conversations - free from data mining, ads and all the rest of it - and stay secure. Only the people in your conversation can read your messages. Chat across multiple devices -Stay in touch wherever you are with fully synchronized message history across all your devices, even those running Element legacy app, and on the web at https://app.element.io \ No newline at end of file +Stay in touch wherever you are with fully synchronized message history across all your devices, even those running Element legacy app, and on the web at https://app.element.io + +The application requires the android.permission.REQUEST_INSTALL_PACKAGES permission to enable the installation of applications received as attachments, ensuring seamless and convenient access to new software within the app. + +The application requires the USE_FULL_SCREEN_INTENT permission to ensure our users can effectively receive call notifications even when their devices are locked. From e8eae1eb97ee060aed9b4bda3c8807855de05a3e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 15 Oct 2024 10:29:53 +0200 Subject: [PATCH 14/67] RTL: ensure the top start corner is cropped. --- .../timeline/components/MessageEventBubble.kt | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) 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 f080bc58dd..1e69dbaa72 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 @@ -18,7 +18,10 @@ import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ripple import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -29,7 +32,10 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.onGloballyPositioned +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.compound.theme.ElementTheme import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition @@ -101,6 +107,8 @@ fun MessageEventBubble( val bubbleShape = bubbleShape() val radiusPx = (avatarRadius + SENDER_AVATAR_BORDER_WIDTH).toPx() val yOffsetPx = -(NEGATIVE_MARGIN_FOR_BUBBLE + avatarRadius).toPx() + val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl + var contentWidthPx by remember { mutableFloatStateOf(0f) } BoxWithConstraints( modifier = modifier .graphicsLayer { @@ -112,7 +120,7 @@ fun MessageEventBubble( drawCircle( color = Color.Black, center = Offset( - x = 0f, + x = if (isRtl) contentWidthPx else 0f, y = yOffsetPx, ), radius = radiusPx, @@ -129,7 +137,9 @@ fun MessageEventBubble( .testTag(TestTags.messageBubble) .widthIn( min = MIN_BUBBLE_WIDTH, - max = (constraints.maxWidth * BUBBLE_WIDTH_RATIO).toInt().toDp() + max = (constraints.maxWidth * BUBBLE_WIDTH_RATIO) + .toInt() + .toDp() ) .clip(bubbleShape) .combinedClickable( @@ -137,7 +147,10 @@ fun MessageEventBubble( onLongClick = onLongClick, indication = ripple(), interactionSource = interactionSource - ), + ) + .onGloballyPositioned { coordinates -> + contentWidthPx = coordinates.size.width.toFloat() + }, color = backgroundBubbleColor, shape = bubbleShape, content = content From 98fbb04fae6f2879c0feba267068a9f5a3fc6b11 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 15 Oct 2024 10:39:59 +0200 Subject: [PATCH 15/67] RTL: ensure the sender information are displayed at the start of the screen. --- .../messages/impl/timeline/components/TimelineItemEventRow.kt | 2 ++ 1 file changed, 2 insertions(+) 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 67342203bd..06567f2d32 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 @@ -301,6 +301,8 @@ private fun TimelineItemEventRowContent( Modifier .constrainAs(sender) { top.linkTo(parent.top) + // Required for correct RTL layout + start.linkTo(parent.start) } .padding(horizontal = 16.dp) .zIndex(1f) From 591ec2e8d8b8468a63ad056b510137ecd6ff9707 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 15 Oct 2024 14:50:26 +0200 Subject: [PATCH 16/67] ElementCall: request audio focus and start CallForeground service only when the call is effectively started. --- .../call/impl/ui/CallScreenPresenter.kt | 1 + .../features/call/impl/ui/CallScreenState.kt | 1 + .../call/impl/ui/CallScreenStateProvider.kt | 2 ++ .../call/impl/ui/ElementCallActivity.kt | 24 +++++++++---------- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt index 3a4a31f190..918acc1200 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt @@ -180,6 +180,7 @@ class CallScreenPresenter @AssistedInject constructor( urlState = urlState.value, webViewError = webViewError, userAgent = userAgent, + isCallActive = isJoinedCall, isInWidgetMode = isInWidgetMode, eventSink = { handleEvents(it) }, ) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt index 48a4672e1a..7da23f24ff 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt @@ -13,6 +13,7 @@ data class CallScreenState( val urlState: AsyncData, val webViewError: String?, val userAgent: String, + val isCallActive: Boolean, val isInWidgetMode: Boolean, val eventSink: (CallScreenEvents) -> Unit, ) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt index bb4794749d..f891c4c526 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt @@ -24,6 +24,7 @@ internal fun aCallScreenState( urlState: AsyncData = AsyncData.Success("https://call.element.io/some-actual-call?with=parameters"), webViewError: String? = null, userAgent: String = "", + isCallActive: Boolean = true, isInWidgetMode: Boolean = false, eventSink: (CallScreenEvents) -> Unit = {}, ): CallScreenState { @@ -31,6 +32,7 @@ internal fun aCallScreenState( urlState = urlState, webViewError = webViewError, userAgent = userAgent, + isCallActive = isCallActive, isInWidgetMode = isInWidgetMode, eventSink = eventSink, ) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt index b55c39f41b..63aae37e8f 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt @@ -26,6 +26,7 @@ import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberUpdatedState @@ -95,7 +96,6 @@ class ElementCallActivity : pictureInPicturePresenter.setPipView(this) audioManager = getSystemService(AUDIO_SERVICE) as AudioManager - requestAudioFocus() setContent { val pipState = pictureInPicturePresenter.present() @@ -103,6 +103,11 @@ class ElementCallActivity : ElementThemeApp(appPreferencesStore) { val state = presenter.present() eventSink = state.eventSink + LaunchedEffect(state.isCallActive) { + if (state.isCallActive) { + setCallIsActive() + } + } CallScreenView( state = state, pipState = pipState, @@ -115,6 +120,11 @@ class ElementCallActivity : } } + private fun setCallIsActive() { + requestAudioFocus() + CallForegroundService.start(this) + } + @Composable private fun ListenToAndroidEvents(pipState: PictureInPictureState) { val pipEventSink by rememberUpdatedState(pipState.eventSink) @@ -156,18 +166,6 @@ class ElementCallActivity : setCallType(intent) } - override fun onStart() { - super.onStart() - CallForegroundService.stop(this) - } - - override fun onStop() { - super.onStop() - if (!isFinishing && !isChangingConfigurations) { - CallForegroundService.start(this) - } - } - override fun onDestroy() { super.onDestroy() releaseAudioFocus() From 35d94bbda70ad944a5f3b581f8670621922675d6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 15 Oct 2024 14:54:06 +0200 Subject: [PATCH 17/67] ElementCall: CallForegroundService now use the Microphone permissions. --- features/call/impl/src/main/AndroidManifest.xml | 6 +++--- .../call/impl/services/CallForegroundService.kt | 14 ++++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/features/call/impl/src/main/AndroidManifest.xml b/features/call/impl/src/main/AndroidManifest.xml index 8337a45b69..8049f66294 100644 --- a/features/call/impl/src/main/AndroidManifest.xml +++ b/features/call/impl/src/main/AndroidManifest.xml @@ -21,8 +21,8 @@ - - + + @@ -80,7 +80,7 @@ android:name=".services.CallForegroundService" android:enabled="true" android:exported="false" - android:foregroundServiceType="phoneCall" /> + android:foregroundServiceType="microphone" /> = Build.VERSION_CODES.Q) { - ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL + val serviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE } else { 0 } From 9fc6b01a0ca38fdeceae615d59d6af4edd263164 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 15 Oct 2024 18:02:23 +0200 Subject: [PATCH 18/67] Add userId in identity change warning banner #3678 --- .../identity/IdentityChangeStatePresenter.kt | 4 ++-- .../identity/IdentityChangeStateProvider.kt | 24 +++++++++++++++---- .../identity/IdentityChangeStateView.kt | 20 +++++++++++++--- .../crypto/identity/IdentityRoomMember.kt | 2 +- .../libraries/matrix/api/core/UserId.kt | 5 ++++ .../libraries/matrix/api/room/RoomMember.kt | 6 +++++ .../src/main/res/values/localazy.xml | 2 ++ 7 files changed, 53 insertions(+), 10 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt index a4a13986e8..16bc39fac8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt @@ -105,13 +105,13 @@ class IdentityChangeStatePresenter @Inject constructor( private fun RoomMember.toIdentityRoomMember() = IdentityRoomMember( userId = userId, - disambiguatedDisplayName = disambiguatedDisplayName, + displayNameOrDefault = displayNameOrDefault, avatarData = getAvatarData(AvatarSize.ComposerAlert), ) private fun createDefaultRoomMemberForIdentityChange(userId: UserId) = IdentityRoomMember( userId = userId, - disambiguatedDisplayName = userId.value, + displayNameOrDefault = userId.extractedDisplayName, avatarData = AvatarData( id = userId.value, name = null, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt index fa70bebe6a..2a36c5e23f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt @@ -20,8 +20,16 @@ class IdentityChangeStateProvider : PreviewParameterProvider = emptyList(), ) = IdentityChangeState( @@ -38,7 +54,7 @@ internal fun anIdentityChangeState( internal fun anIdentityRoomMember( userId: UserId = UserId("@alice:example.com"), - disambiguatedDisplayName: String = userId.value, + displayNameOrDefault: String = userId.extractedDisplayName, avatarData: AvatarData = AvatarData( id = userId.value, name = null, @@ -47,6 +63,6 @@ internal fun anIdentityRoomMember( ), ) = IdentityRoomMember( userId = userId, - disambiguatedDisplayName = disambiguatedDisplayName, + displayNameOrDefault = displayNameOrDefault, avatarData = avatarData, ) 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 6ff167a8a7..aac05f8684 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 @@ -40,13 +40,27 @@ fun IdentityChangeStateView( avatar = pinViolationIdentityChange.identityRoomMember.avatarData, content = buildAnnotatedString { val learnMoreStr = stringResource(CommonStrings.action_learn_more) + val displayName = pinViolationIdentityChange.identityRoomMember.displayNameOrDefault + val userIdStr = stringResource( + CommonStrings.crypto_identity_change_pin_violation_new_user_id, + pinViolationIdentityChange.identityRoomMember.userId, + ) val fullText = stringResource( - id = CommonStrings.crypto_identity_change_pin_violation, - pinViolationIdentityChange.identityRoomMember.disambiguatedDisplayName, + id = CommonStrings.crypto_identity_change_pin_violation_new, + displayName, + userIdStr, learnMoreStr, ) - val learnMoreStartIndex = fullText.indexOf(learnMoreStr) append(fullText) + val userIdStartIndex = fullText.indexOf(userIdStr) + addStyle( + style = SpanStyle( + fontWeight = FontWeight.Bold, + ), + start = userIdStartIndex, + end = userIdStartIndex + userIdStr.length, + ) + val learnMoreStartIndex = fullText.lastIndexOf(learnMoreStr) addStyle( style = SpanStyle( textDecoration = TextDecoration.Underline, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityRoomMember.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityRoomMember.kt index f5ad9295b3..4acfbd5181 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityRoomMember.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityRoomMember.kt @@ -12,6 +12,6 @@ import io.element.android.libraries.matrix.api.core.UserId data class IdentityRoomMember( val userId: UserId, - val disambiguatedDisplayName: String, + val displayNameOrDefault: String, val avatarData: AvatarData, ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/UserId.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/UserId.kt index e03069a243..ae6c3de5bd 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/UserId.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/UserId.kt @@ -24,4 +24,9 @@ value class UserId(val value: String) : Serializable { } override fun toString(): String = value + + val extractedDisplayName: String + get() = value + .removePrefix("@") + .substringBefore(":") } 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 52d45bfc11..cabd222b72 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 @@ -51,6 +51,12 @@ data class RoomMember( isNameAmbiguous -> "$displayName ($userId)" else -> displayName } + + val displayNameOrDefault: String + get() = when { + displayName == null -> userId.extractedDisplayName + else -> displayName + } } enum class RoomMembershipState { diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index f3adc853f2..24e35e6e58 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -252,6 +252,8 @@ Reason: %1$s." "Waiting for this message" "You" "%1$s\'s identity appears to have changed. %2$s" + "%1$s’s %2$s identity appears to have changed. %3$s" + "(%1$s)" "Confirmation" "Error" "Success" From d8daa1c2a96ddef4b473c0ad6644396754e07c78 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 15 Oct 2024 16:13:17 +0000 Subject: [PATCH 19/67] Update screenshots --- ....impl.crypto.identity_IdentityChangeStateView_Day_1_en.png | 4 ++-- ....impl.crypto.identity_IdentityChangeStateView_Day_2_en.png | 3 +++ ...mpl.crypto.identity_IdentityChangeStateView_Night_1_en.png | 4 ++-- ...mpl.crypto.identity_IdentityChangeStateView_Night_2_en.png | 3 +++ ...rypto.identity_MessagesViewWithIdentityChange_Day_1_en.png | 4 ++-- ...rypto.identity_MessagesViewWithIdentityChange_Day_2_en.png | 3 +++ ...pto.identity_MessagesViewWithIdentityChange_Night_1_en.png | 4 ++-- ...pto.identity_MessagesViewWithIdentityChange_Night_2_en.png | 3 +++ 8 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en.png index 66783ec6cc..2103b4fffa 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24481773bfccc5eb1ebf3f9955cdc77e8b3b5130d4fa56f96df732e3627ea3c6 -size 21018 +oid sha256:66419e3ac6da49bd535a005759cba38fe4b57d5307ad9ad3cb3e2fa4eed38890 +size 25136 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en.png new file mode 100644 index 0000000000..c5e829c113 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35651217adb52b19136f99922d7573071084ae7c4109feb525d182274ab4df78 +size 25131 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en.png index 93bd368a94..9077b8eefe 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1421adb601d9a8050a5ed1b60aba8a05b8eab61aaf18d3936226efe891acd8b6 -size 23880 +oid sha256:d2831daee948b612f15623948ef49c497732a143fa50f331e82e689decd5a4c0 +size 27967 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en.png new file mode 100644 index 0000000000..485eff2c4b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3055ebf4ef01148506cac22bfd7c05aea5315d4a63952affc998c918cd6055b6 +size 27880 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 fec7097b6a..f69e6e4bc4 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:8d37a9ffee50f8e9ea0c9fd50c61310969c4fbaa99cf858481b3f42f8db4467d -size 61102 +oid sha256:38b158a90790d6183df45914382ec16275ae8e4c5e7054c988d20d179cfebc44 +size 64996 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 new file mode 100644 index 0000000000..60fddc6079 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3b3a017453f60f98f245374089fa604848f5e764be20463d63b0ebc51eadd18 +size 65118 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 2cbc735bd8..e944aae39d 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:d0952d9cf3812ca419e144936faf523e0b865a22a61d2a55e07f089d9e6e9009 -size 64824 +oid sha256:5c4f814532fb387a3a38de6fbbf31563884de658ab7d930fe5e91b3fd299f355 +size 69088 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 new file mode 100644 index 0000000000..9d4d8d2416 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9bfcbe306160d48ac80ad1a6c4e0d611da72a376bdfe3532ace022d9bbb5c9a +size 69181 From 9dcce54121b7a0c79922343644d3de7554d71a64 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 20:20:59 +0000 Subject: [PATCH 20/67] Update plugin licensee to v1.12.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 419f323b42..13733a101d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -238,5 +238,5 @@ sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } knit = { id = "org.jetbrains.kotlinx.knit", version = "0.5.0" } sonarqube = "org.sonarqube:5.1.0.4882" -licensee = "app.cash.licensee:1.11.0" +licensee = "app.cash.licensee:1.12.0" compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } From a9091c6ea6da9add11be4960f2fb6c56d92a5bda Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 09:28:06 +0200 Subject: [PATCH 21/67] Set Active call immediately when not in widget mode. --- .../android/features/call/impl/ui/ElementCallActivity.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt index 63aae37e8f..f36565556e 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt @@ -103,8 +103,9 @@ class ElementCallActivity : ElementThemeApp(appPreferencesStore) { val state = presenter.present() eventSink = state.eventSink - LaunchedEffect(state.isCallActive) { - if (state.isCallActive) { + LaunchedEffect(state.isCallActive, state.isInWidgetMode) { + // Note when not in WidgetMode, isCallActive will never be true, so consider the call is active + if (state.isCallActive || !state.isInWidgetMode) { setCallIsActive() } } From 36c3b1cff5dada3525c72a8fdb9d29ffee2a9462 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 09:31:07 +0200 Subject: [PATCH 22/67] Neat: do not compute `audioAttributes` if not necessary. --- .../android/features/call/impl/ui/ElementCallActivity.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt index f36565556e..a685ef7b9a 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt @@ -230,10 +230,10 @@ class ElementCallActivity : @Suppress("DEPRECATION") private fun requestAudioFocus() { - val audioAttributes = AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) - .build() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val audioAttributes = AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) + .build() val request = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(audioAttributes) .build() @@ -246,7 +246,6 @@ class ElementCallActivity : AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE, ) - audioFocusChangeListener = listener } } From 9e5ce39c4d5f55a9af11c083017f181dc2b3c051 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 09:54:39 +0200 Subject: [PATCH 23/67] Add test on isCallActive --- .../call/ui/CallScreenPresenterTest.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt index f0e5708db3..d33f16b29e 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt @@ -73,6 +73,7 @@ class CallScreenPresenterTest { assertThat(initialState.urlState).isEqualTo(AsyncData.Success("https://call.element.io")) assertThat(initialState.webViewError).isNull() assertThat(initialState.isInWidgetMode).isFalse() + assertThat(initialState.isCallActive).isFalse() analyticsLambda.assertions().isNeverCalled() joinedCallLambda.assertions().isCalledOnce() } @@ -106,6 +107,7 @@ class CallScreenPresenterTest { joinedCallLambda.assertions().isCalledOnce() val initialState = awaitItem() assertThat(initialState.urlState).isInstanceOf(AsyncData.Success::class.java) + assertThat(initialState.isCallActive).isFalse() assertThat(initialState.isInWidgetMode).isTrue() assertThat(widgetProvider.getWidgetCalled).isTrue() assertThat(widgetDriver.runCalledCount).isEqualTo(1) @@ -203,6 +205,44 @@ class CallScreenPresenterTest { } } + @Test + fun `present - a received room member message makes the call to be active`() = runTest { + val navigator = FakeCallScreenNavigator() + val widgetDriver = FakeMatrixWidgetDriver() + val presenter = createCallScreenPresenter( + callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID), + widgetDriver = widgetDriver, + navigator = navigator, + dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), + screenTracker = FakeScreenTracker {}, + ) + val messageInterceptor = FakeWidgetMessageInterceptor() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + val initialState = awaitItem() + assertThat(initialState.isCallActive).isFalse() + initialState.eventSink(CallScreenEvents.SetupMessageChannels(messageInterceptor)) + messageInterceptor.givenInterceptedMessage( + """ + { + "action":"send_event", + "api":"fromWidget", + "widgetId":"1", + "requestId":"1", + "data":{ + "type":"org.matrix.msc3401.call.member" + } + } + """.trimIndent() + ) + skipItems(1) + val finalState = awaitItem() + assertThat(finalState.isCallActive).isTrue() + } + } + @Test fun `present - automatically starts the Matrix client sync when on RoomCall`() = runTest { val navigator = FakeCallScreenNavigator() From fe6c381500ad322d39d33adcb45a6dae83804227 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 10:01:04 +0200 Subject: [PATCH 24/67] Fix tests. --- .../crypto/identity/IdentityChangeStatePresenterTest.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt index 4ff0c8f340..92774f4feb 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt @@ -69,7 +69,7 @@ class IdentityChangeStatePresenterTest { } @Test - fun `present - when the room emits identity change, but the feature is disabled, the presenter emits new state`() = runTest { + fun `present - when the room emits identity change, but the feature is disabled, the presenter does not emit new state`() = runTest { val room = FakeMatrixRoom( isEncrypted = true, ) @@ -106,7 +106,7 @@ class IdentityChangeStatePresenterTest { } @Test - fun `present - when the clear room emits identity change, the presenter does not emits new state`() = runTest { + fun `present - when the clear room emits identity change, the presenter does not emit new state`() = runTest { val room = FakeMatrixRoom(isEncrypted = false) val presenter = createIdentityChangeStatePresenter(room) presenter.test { @@ -128,6 +128,7 @@ class IdentityChangeStatePresenterTest { assertThat(finalItem.roomMemberIdentityStateChanges).hasSize(1) val value = finalItem.roomMemberIdentityStateChanges.first() assertThat(value.identityRoomMember.userId).isEqualTo(A_USER_ID_2) + assertThat(value.identityRoomMember.displayNameOrDefault).isEqualTo(A_USER_ID_2.extractedDisplayName) assertThat(value.identityState).isEqualTo(IdentityState.PinViolation) } } @@ -163,14 +164,14 @@ class IdentityChangeStatePresenterTest { assertThat(finalItem.roomMemberIdentityStateChanges).hasSize(1) val value = finalItem.roomMemberIdentityStateChanges.first() assertThat(value.identityRoomMember.userId).isEqualTo(A_USER_ID_2) - assertThat(value.identityRoomMember.disambiguatedDisplayName).isEqualTo("Alice") + assertThat(value.identityRoomMember.displayNameOrDefault).isEqualTo("Alice") assertThat(value.identityRoomMember.avatarData.size).isEqualTo(AvatarSize.ComposerAlert) assertThat(value.identityState).isEqualTo(IdentityState.PinViolation) } } @Test - fun `present - when the user pin the identity, the presenter invokes the encryption service api`() = + fun `present - when the user pins the identity, the presenter invokes the encryption service api`() = runTest { val lambda = lambdaRecorder> { Result.success(Unit) } val encryptionService = FakeEncryptionService( From 202a7d0544dfcde0f6a156f0ed458c74e7f4ffff Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Oct 2024 11:47:01 +0200 Subject: [PATCH 25/67] Remove runBlocking usage in LambdaRecorder. --- .../tests/testutils/lambda/LambdaRecorder.kt | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/LambdaRecorder.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/LambdaRecorder.kt index 2fa38e04c1..b2798e60e4 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/LambdaRecorder.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/LambdaRecorder.kt @@ -7,8 +7,6 @@ package io.element.android.tests.testutils.lambda -import kotlinx.coroutines.runBlocking - /** * A recorder that can be used to record the parameters of lambda invocation. */ @@ -95,21 +93,21 @@ inline fun lambdaAnyRecorder( class LambdaNoParamRecorder(ensureNeverCalled: Boolean, val block: () -> R) : LambdaRecorder(ensureNeverCalled), () -> R { override fun invoke(): R { onInvoke() - return runBlocking { block() } + return block() } } class LambdaOneParamRecorder(ensureNeverCalled: Boolean, val block: (T) -> R) : LambdaRecorder(ensureNeverCalled), (T) -> R { override fun invoke(p: T): R { onInvoke(p) - return runBlocking { block(p) } + return block(p) } } class LambdaTwoParamsRecorder(ensureNeverCalled: Boolean, val block: (T1, T2) -> R) : LambdaRecorder(ensureNeverCalled), (T1, T2) -> R { override fun invoke(p1: T1, p2: T2): R { onInvoke(p1, p2) - return runBlocking { block(p1, p2) } + return block(p1, p2) } } @@ -118,7 +116,7 @@ class LambdaThreeParamsRecorder(ensureNeverCalled: B ), (T1, T2, T3) -> R { override fun invoke(p1: T1, p2: T2, p3: T3): R { onInvoke(p1, p2, p3) - return runBlocking { block(p1, p2, p3) } + return block(p1, p2, p3) } } @@ -127,7 +125,7 @@ class LambdaFourParamsRecorder(ensureNeverCal ), (T1, T2, T3, T4) -> R { override fun invoke(p1: T1, p2: T2, p3: T3, p4: T4): R { onInvoke(p1, p2, p3, p4) - return runBlocking { block(p1, p2, p3, p4) } + return block(p1, p2, p3, p4) } } @@ -139,7 +137,7 @@ class LambdaFiveParamsRecorder( ), (T1, T2, T3, T4, T5) -> R { override fun invoke(p1: T1, p2: T2, p3: T3, p4: T4, p5: T5): R { onInvoke(p1, p2, p3, p4, p5) - return runBlocking { block(p1, p2, p3, p4, p5) } + return block(p1, p2, p3, p4, p5) } } @@ -149,7 +147,7 @@ class LambdaSixParamsRecorder( ) : LambdaRecorder(ensureNeverCalled), (T1, T2, T3, T4, T5, T6) -> R { override fun invoke(p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6): R { onInvoke(p1, p2, p3, p4, p5, p6) - return runBlocking { block(p1, p2, p3, p4, p5, p6) } + return block(p1, p2, p3, p4, p5, p6) } } @@ -159,7 +157,7 @@ class LambdaSevenParamsRecorder R { override fun invoke(p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7): R { onInvoke(p1, p2, p3, p4, p5, p6, p7) - return runBlocking { block(p1, p2, p3, p4, p5, p6, p7) } + return block(p1, p2, p3, p4, p5, p6, p7) } } @@ -169,7 +167,7 @@ class LambdaEightParamsRecorder R { override fun invoke(p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7, p8: T8): R { onInvoke(p1, p2, p3, p4, p5, p6, p7, p8) - return runBlocking { block(p1, p2, p3, p4, p5, p6, p7, p8) } + return block(p1, p2, p3, p4, p5, p6, p7, p8) } } @@ -179,7 +177,7 @@ class LambdaNineParamsRecorder R { override fun invoke(p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7, p8: T8, p9: T9): R { onInvoke(p1, p2, p3, p4, p5, p6, p7, p8, p9) - return runBlocking { block(p1, p2, p3, p4, p5, p6, p7, p8, p9) } + return block(p1, p2, p3, p4, p5, p6, p7, p8, p9) } } From 74725bc89fbee27a2288b5b144bb93ea03fe385b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Oct 2024 11:56:11 +0200 Subject: [PATCH 26/67] Safer code. --- .../preferences/impl/developer/DeveloperSettingsPresenter.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 ea10778580..844474e1e1 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 @@ -115,7 +115,9 @@ class DeveloperSettingsPresenter @Inject constructor( DeveloperSettingsEvents.ClearCache -> coroutineScope.clearCache(clearCacheAction) is DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled -> coroutineScope.launch { appPreferencesStore.setSimplifiedSlidingSyncEnabled(event.isEnabled) - logoutUseCase.logout(ignoreSdkError = true) + runCatching { + logoutUseCase.logout(ignoreSdkError = true) + } } is DeveloperSettingsEvents.SetHideImagesAndVideos -> coroutineScope.launch { appPreferencesStore.setHideImagesAndVideos(event.value) From 85b1b9b542c15be19b77be68e312ddf9175643e1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Oct 2024 11:52:57 +0200 Subject: [PATCH 27/67] Let FakeKnockRoom and FakeLogoutUseCase use simulateLongTask --- .../element/android/features/joinroom/impl/FakeKnockRoom.kt | 5 ++++- .../android/features/logout/test/FakeLogoutUseCase.kt | 5 +++-- .../impl/developer/DeveloperSettingsPresenterTest.kt | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/FakeKnockRoom.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/FakeKnockRoom.kt index ae60b6c9bb..e60707d165 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/FakeKnockRoom.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/FakeKnockRoom.kt @@ -9,9 +9,12 @@ package io.element.android.features.joinroom.impl import io.element.android.features.joinroom.impl.di.KnockRoom import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.tests.testutils.simulateLongTask class FakeKnockRoom( var lambda: (RoomId) -> Result = { Result.success(Unit) } ) : KnockRoom { - override suspend fun invoke(roomId: RoomId) = lambda(roomId) + override suspend fun invoke(roomId: RoomId) = simulateLongTask { + lambda(roomId) + } } diff --git a/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt b/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt index 11fb968a1f..4f3475f21c 100644 --- a/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt +++ b/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt @@ -9,11 +9,12 @@ package io.element.android.features.logout.test import io.element.android.features.logout.api.LogoutUseCase import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.tests.testutils.simulateLongTask class FakeLogoutUseCase( var logoutLambda: (Boolean) -> String? = { lambdaError() } ) : LogoutUseCase { - override suspend fun logout(ignoreSdkError: Boolean): String? { - return logoutLambda(ignoreSdkError) + override suspend fun logout(ignoreSdkError: Boolean): String? = simulateLongTask { + logoutLambda(ignoreSdkError) } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt index c893de7f55..51750d4bfb 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 @@ -26,6 +26,7 @@ import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.awaitLastSequentialItem import io.element.android.tests.testutils.lambda.lambdaRecorder +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -168,11 +169,15 @@ class DeveloperSettingsPresenterTest { initialState.eventSink(DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled(true)) assertThat(awaitItem().isSimpleSlidingSyncEnabled).isTrue() assertThat(preferences.isSimplifiedSlidingSyncEnabledFlow().first()).isTrue() + // Give time for the logout to be called, but for some reason using runCurrent() is not enough + delay(2) logoutCallRecorder.assertions().isCalledOnce() initialState.eventSink(DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled(false)) assertThat(awaitItem().isSimpleSlidingSyncEnabled).isFalse() assertThat(preferences.isSimplifiedSlidingSyncEnabledFlow().first()).isFalse() + // Give time for the logout to be called, but for some reason using runCurrent() is not enough + delay(2) logoutCallRecorder.assertions().isCalledExactly(times = 2) } } From 6b2aa7eb8c16b4956c018740e37c4894873229a6 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 16 Oct 2024 11:10:58 +0200 Subject: [PATCH 28/67] Refresh room summaries when date or time changes in the device (#3683) * Add `DateTimeObserver` to rebuild the room summary data when the date/time changes. * Add time changed action too, to trigger when the user manually changes date/time * Fix timezone issue by adding `TimezoneProvider`, fix tests * Create test for `DateTimeObserver` usage in `RoomListDataSource` * Create aRoomListRoomSummaryFactory function. * Improve test by faking the lastMessageTimestampFormatter --------- Co-authored-by: Benoit Marty --- .../roomlist/impl/RoomListPresenter.kt | 2 - .../impl/datasource/RoomListDataSource.kt | 30 ++++- .../roomlist/impl/RoomListPresenterTest.kt | 17 ++- .../impl/datasource/RoomListDataSourceTest.kt | 106 ++++++++++++++++++ .../RoomListRoomSummaryFactoryTest.kt | 19 ++++ .../search/RoomListSearchPresenterTest.kt | 4 +- .../androidutils/system/DateTimeObserver.kt | 61 ++++++++++ .../api/LastMessageTimestampFormatter.kt | 2 +- .../dateformatter/impl/DateFormatters.kt | 5 +- .../impl/LocalDateTimeProvider.kt | 7 +- .../dateformatter/impl/TimezoneProvider.kt | 14 +++ .../impl/di/DateFormatterModule.kt | 3 +- ...efaultLastMessageTimestampFormatterTest.kt | 6 +- .../api/RoomLastMessageFormatter.kt | 2 +- samples/minimal/build.gradle.kts | 1 + .../android/samples/minimal/RoomListScreen.kt | 9 +- 16 files changed, 262 insertions(+), 26 deletions(-) create mode 100644 features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSourceTest.kt create mode 100644 features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactoryTest.kt create mode 100644 libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/DateTimeObserver.kt create mode 100644 libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/TimezoneProvider.kt diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index f8e468637b..248d788f6c 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -51,7 +51,6 @@ import io.element.android.libraries.matrix.api.core.RoomId 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.roomlist.RoomList -import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import io.element.android.libraries.push.api.notifications.NotificationCleaner @@ -93,7 +92,6 @@ class RoomListPresenter @Inject constructor( private val logoutPresenter: Presenter, ) : Presenter { private val encryptionService: EncryptionService = client.encryptionService() - private val syncService: SyncService = client.syncService() @Composable override fun present(): RoomListState { diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index 08c12fb5c4..3ea2549934 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -10,6 +10,7 @@ package io.element.android.features.roomlist.impl.datasource import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.libraries.androidutils.diff.DiffCacheUpdater import io.element.android.libraries.androidutils.diff.MutableListDiffCache +import io.element.android.libraries.androidutils.system.DateTimeObserver import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService @@ -36,9 +37,11 @@ class RoomListDataSource @Inject constructor( private val coroutineDispatchers: CoroutineDispatchers, private val notificationSettingsService: NotificationSettingsService, private val appScope: CoroutineScope, + private val dateTimeObserver: DateTimeObserver, ) { init { observeNotificationSettings() + observeDateTimeChanges() } private val _allRooms = MutableSharedFlow>(replay = 1) @@ -77,6 +80,17 @@ class RoomListDataSource @Inject constructor( .launchIn(appScope) } + private fun observeDateTimeChanges() { + dateTimeObserver.changes + .onEach { event -> + when (event) { + is DateTimeObserver.Event.TimeZoneChanged -> rebuildAllRoomSummaries() + is DateTimeObserver.Event.DateChanged -> rebuildAllRoomSummaries() + } + } + .launchIn(appScope) + } + private suspend fun replaceWith(roomSummaries: List) = withContext(coroutineDispatchers.computation) { lock.withLock { diffCacheUpdater.updateWith(roomSummaries) @@ -84,9 +98,13 @@ class RoomListDataSource @Inject constructor( } } - private suspend fun buildAndEmitAllRooms(roomSummaries: List) { + private suspend fun buildAndEmitAllRooms(roomSummaries: List, useCache: Boolean = true) { val roomListRoomSummaries = diffCache.indices().mapNotNull { index -> - diffCache.get(index) ?: buildAndCacheItem(roomSummaries, index) + if (useCache) { + diffCache.get(index) ?: buildAndCacheItem(roomSummaries, index) + } else { + buildAndCacheItem(roomSummaries, index) + } } _allRooms.emit(roomListRoomSummaries.toImmutableList()) } @@ -96,4 +114,12 @@ class RoomListDataSource @Inject constructor( diffCache[index] = roomListSummary return roomListSummary } + + private suspend fun rebuildAllRoomSummaries() { + lock.withLock { + roomListService.allRooms.summaries.replayCache.firstOrNull()?.let { roomSummaries -> + buildAndEmitAllRooms(roomSummaries, useCache = false) + } + } + } } diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt index 6dfd1b0a0a..cb7ef2852a 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt @@ -22,13 +22,14 @@ import io.element.android.features.logout.api.direct.aDirectLogoutState import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.features.roomlist.impl.datasource.RoomListDataSource -import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory +import io.element.android.features.roomlist.impl.datasource.aRoomListRoomSummaryFactory import io.element.android.features.roomlist.impl.filters.RoomListFiltersState import io.element.android.features.roomlist.impl.filters.aRoomListFiltersState import io.element.android.features.roomlist.impl.model.createRoomListRoomSummary import io.element.android.features.roomlist.impl.search.RoomListSearchEvents import io.element.android.features.roomlist.impl.search.RoomListSearchState import io.element.android.features.roomlist.impl.search.aRoomListSearchState +import io.element.android.libraries.androidutils.system.DateTimeObserver import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter import io.element.android.libraries.dateformatter.test.A_FORMATTED_DATE @@ -83,6 +84,7 @@ import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy @@ -649,13 +651,14 @@ class RoomListPresenterTest { leaveRoomPresenter = { leaveRoomState }, roomListDataSource = RoomListDataSource( roomListService = client.roomListService, - roomListRoomSummaryFactory = RoomListRoomSummaryFactory( + roomListRoomSummaryFactory = aRoomListRoomSummaryFactory( lastMessageTimestampFormatter = lastMessageTimestampFormatter, roomLastMessageFormatter = roomLastMessageFormatter, ), coroutineDispatchers = testCoroutineDispatchers(), notificationSettingsService = client.notificationSettingsService(), - appScope = backgroundScope + appScope = backgroundScope, + dateTimeObserver = FakeDateTimeObserver(), ), featureFlagService = featureFlagService, indicatorService = DefaultIndicatorService( @@ -672,3 +675,11 @@ class RoomListPresenterTest { logoutPresenter = { aDirectLogoutState() }, ) } + +class FakeDateTimeObserver : DateTimeObserver { + override val changes = MutableSharedFlow(extraBufferCapacity = 1) + + fun given(event: DateTimeObserver.Event) { + changes.tryEmit(event) + } +} diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSourceTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSourceTest.kt new file mode 100644 index 0000000000..f02c53e6f6 --- /dev/null +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSourceTest.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.roomlist.impl.datasource + +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.features.roomlist.impl.FakeDateTimeObserver +import io.element.android.libraries.androidutils.system.DateTimeObserver +import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter +import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService +import io.element.android.libraries.matrix.test.room.aRoomSummary +import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import java.time.Instant + +class RoomListDataSourceTest { + @Test + fun `when DateTimeObserver gets a date change, the room summaries are refreshed`() = runTest { + val roomListService = FakeRoomListService().apply { + postState(RoomListService.State.Running) + postAllRooms(listOf(aRoomSummary())) + } + val dateTimeObserver = FakeDateTimeObserver() + val lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter() + lastMessageTimestampFormatter.givenFormat("Today") + val roomListDataSource = createRoomListDataSource( + roomListService = roomListService, + roomListRoomSummaryFactory = aRoomListRoomSummaryFactory( + lastMessageTimestampFormatter = lastMessageTimestampFormatter, + ), + dateTimeObserver = dateTimeObserver, + ) + + roomListDataSource.allRooms.test { + // Observe room list items changes + roomListDataSource.launchIn(backgroundScope) + // Get the initial room list + val initialRoomList = awaitItem() + assertThat(initialRoomList).isNotEmpty() + assertThat(initialRoomList.first().timestamp).isEqualTo("Today") + lastMessageTimestampFormatter.givenFormat("Yesterday") + // Trigger a date change + dateTimeObserver.given(DateTimeObserver.Event.DateChanged(Instant.MIN, Instant.now())) + // Check there is a new list and it's not the same as the previous one + val newRoomList = awaitItem() + assertThat(newRoomList).isNotSameInstanceAs(initialRoomList) + assertThat(newRoomList.first().timestamp).isEqualTo("Yesterday") + } + } + + @Test + fun `when DateTimeObserver gets a time zone change, the room summaries are refreshed`() = runTest { + val roomListService = FakeRoomListService().apply { + postState(RoomListService.State.Running) + postAllRooms(listOf(aRoomSummary())) + } + val dateTimeObserver = FakeDateTimeObserver() + val lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter() + lastMessageTimestampFormatter.givenFormat("Today") + val roomListDataSource = createRoomListDataSource( + roomListService = roomListService, + roomListRoomSummaryFactory = aRoomListRoomSummaryFactory( + lastMessageTimestampFormatter = lastMessageTimestampFormatter, + ), + dateTimeObserver = dateTimeObserver, + ) + roomListDataSource.allRooms.test { + // Observe room list items changes + roomListDataSource.launchIn(backgroundScope) + // Get the initial room list + val initialRoomList = awaitItem() + assertThat(initialRoomList).isNotEmpty() + assertThat(initialRoomList.first().timestamp).isEqualTo("Today") + lastMessageTimestampFormatter.givenFormat("Yesterday") + // Trigger a timezone change + dateTimeObserver.given(DateTimeObserver.Event.TimeZoneChanged) + // Check there is a new list and it's not the same as the previous one + val newRoomList = awaitItem() + assertThat(newRoomList).isNotSameInstanceAs(initialRoomList) + assertThat(newRoomList.first().timestamp).isEqualTo("Yesterday") + } + } + + private fun TestScope.createRoomListDataSource( + roomListService: FakeRoomListService = FakeRoomListService(), + roomListRoomSummaryFactory: RoomListRoomSummaryFactory = aRoomListRoomSummaryFactory(), + notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(), + dateTimeObserver: FakeDateTimeObserver = FakeDateTimeObserver(), + ) = RoomListDataSource( + roomListService = roomListService, + roomListRoomSummaryFactory = roomListRoomSummaryFactory, + coroutineDispatchers = testCoroutineDispatchers(), + notificationSettingsService = notificationSettingsService, + appScope = backgroundScope, + dateTimeObserver = dateTimeObserver, + ) +} diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactoryTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactoryTest.kt new file mode 100644 index 0000000000..8a26120a9e --- /dev/null +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactoryTest.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.roomlist.impl.datasource + +import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter +import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter + +fun aRoomListRoomSummaryFactory( + lastMessageTimestampFormatter: LastMessageTimestampFormatter = LastMessageTimestampFormatter { _ -> "Today" }, + roomLastMessageFormatter: RoomLastMessageFormatter = RoomLastMessageFormatter { _, _ -> "Hey" } +) = RoomListRoomSummaryFactory( + lastMessageTimestampFormatter = lastMessageTimestampFormatter, + roomLastMessageFormatter = roomLastMessageFormatter +) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTest.kt index 98e65c830b..6ede9544ec 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTest.kt @@ -11,7 +11,7 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory +import io.element.android.features.roomlist.impl.datasource.aRoomListRoomSummaryFactory import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -142,7 +142,7 @@ fun TestScope.createRoomListSearchPresenter( return RoomListSearchPresenter( dataSource = RoomListSearchDataSource( roomListService = roomListService, - roomSummaryFactory = RoomListRoomSummaryFactory( + roomSummaryFactory = aRoomListRoomSummaryFactory( lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter(), roomLastMessageFormatter = FakeRoomLastMessageFormatter(), ), diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/DateTimeObserver.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/DateTimeObserver.kt new file mode 100644 index 0000000000..e37cee7794 --- /dev/null +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/DateTimeObserver.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.androidutils.system + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.androidutils.system.DateTimeObserver.Event +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.ApplicationContext +import io.element.android.libraries.di.SingleIn +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import java.time.Instant +import javax.inject.Inject + +interface DateTimeObserver { + val changes: Flow + + sealed interface Event { + data object TimeZoneChanged : Event + data class DateChanged(val previous: Instant, val new: Instant) : Event + } +} + +@ContributesBinding(AppScope::class) +@SingleIn(AppScope::class) +class DefaultDateTimeObserver @Inject constructor( + @ApplicationContext context: Context +) : DateTimeObserver { + private val dateTimeReceiver = object : BroadcastReceiver() { + private var lastTime = Instant.now() + + override fun onReceive(context: Context, intent: Intent) { + val newDate = Instant.now() + when (intent.action) { + Intent.ACTION_TIMEZONE_CHANGED -> changes.tryEmit(Event.TimeZoneChanged) + Intent.ACTION_DATE_CHANGED -> changes.tryEmit(Event.DateChanged(lastTime, newDate)) + Intent.ACTION_TIME_CHANGED -> changes.tryEmit(Event.DateChanged(lastTime, newDate)) + } + lastTime = newDate + } + } + + override val changes = MutableSharedFlow(extraBufferCapacity = 10) + + init { + context.registerReceiver(dateTimeReceiver, IntentFilter().apply { + addAction(Intent.ACTION_TIMEZONE_CHANGED) + addAction(Intent.ACTION_DATE_CHANGED) + addAction(Intent.ACTION_TIME_CHANGED) + }) + } +} diff --git a/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/LastMessageTimestampFormatter.kt b/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/LastMessageTimestampFormatter.kt index b3f82b1efe..c5b9778669 100644 --- a/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/LastMessageTimestampFormatter.kt +++ b/libraries/dateformatter/api/src/main/kotlin/io/element/android/libraries/dateformatter/api/LastMessageTimestampFormatter.kt @@ -7,6 +7,6 @@ package io.element.android.libraries.dateformatter.api -interface LastMessageTimestampFormatter { +fun interface LastMessageTimestampFormatter { fun format(timestamp: Long?): String } diff --git a/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt index d15b376d3a..a78cc81c24 100644 --- a/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt +++ b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt @@ -11,7 +11,6 @@ import android.text.format.DateFormat import android.text.format.DateUtils import kotlinx.datetime.Clock import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.TimeZone import kotlinx.datetime.toInstant import kotlinx.datetime.toJavaLocalDate import kotlinx.datetime.toJavaLocalDateTime @@ -25,7 +24,7 @@ import kotlin.math.absoluteValue class DateFormatters @Inject constructor( private val locale: Locale, private val clock: Clock, - private val timeZone: TimeZone, + private val timeZoneProvider: TimezoneProvider, ) { private val onlyTimeFormatter: DateTimeFormatter by lazy { DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withLocale(locale) @@ -70,7 +69,7 @@ class DateFormatters @Inject constructor( return if (period.years.absoluteValue >= 1) { formatDateWithYear(dateToFormat) } else if (useRelative && period.days.absoluteValue < 2 && period.months.absoluteValue < 1) { - getRelativeDay(dateToFormat.toInstant(timeZone).toEpochMilliseconds()) + getRelativeDay(dateToFormat.toInstant(timeZoneProvider.provide()).toEpochMilliseconds()) } else { formatDateWithMonth(dateToFormat) } diff --git a/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/LocalDateTimeProvider.kt b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/LocalDateTimeProvider.kt index 5779b390ef..19e1407f67 100644 --- a/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/LocalDateTimeProvider.kt +++ b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/LocalDateTimeProvider.kt @@ -10,21 +10,20 @@ package io.element.android.libraries.dateformatter.impl import kotlinx.datetime.Clock import kotlinx.datetime.Instant import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime import javax.inject.Inject class LocalDateTimeProvider @Inject constructor( private val clock: Clock, - private val timezone: TimeZone, + private val timezoneProvider: TimezoneProvider, ) { fun providesNow(): LocalDateTime { val now: Instant = clock.now() - return now.toLocalDateTime(timezone) + return now.toLocalDateTime(timezoneProvider.provide()) } fun providesFromTimestamp(timestamp: Long): LocalDateTime { val tsInstant = Instant.fromEpochMilliseconds(timestamp) - return tsInstant.toLocalDateTime(timezone) + return tsInstant.toLocalDateTime(timezoneProvider.provide()) } } diff --git a/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/TimezoneProvider.kt b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/TimezoneProvider.kt new file mode 100644 index 0000000000..8809422e4e --- /dev/null +++ b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/TimezoneProvider.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.dateformatter.impl + +import kotlinx.datetime.TimeZone + +fun interface TimezoneProvider { + fun provide(): TimeZone +} diff --git a/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/di/DateFormatterModule.kt b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/di/DateFormatterModule.kt index dc7055a3bc..568bee5378 100644 --- a/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/di/DateFormatterModule.kt +++ b/libraries/dateformatter/impl/src/main/kotlin/io/element/android/libraries/dateformatter/impl/di/DateFormatterModule.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.dateformatter.impl.di import com.squareup.anvil.annotations.ContributesTo import dagger.Module import dagger.Provides +import io.element.android.libraries.dateformatter.impl.TimezoneProvider import io.element.android.libraries.di.AppScope import kotlinx.datetime.Clock import kotlinx.datetime.TimeZone @@ -25,5 +26,5 @@ object DateFormatterModule { fun providesLocale(): Locale = Locale.getDefault() @Provides - fun providesTimezone(): TimeZone = TimeZone.currentSystemDefault() + fun providesTimezone(): TimezoneProvider = TimezoneProvider { TimeZone.currentSystemDefault() } } diff --git a/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt b/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt index c1a684cdf5..5c8de4462b 100644 --- a/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt +++ b/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt @@ -93,7 +93,7 @@ class DefaultLastMessageTimestampFormatterTest { val now = "1980-04-06T18:35:24.00Z" val dat = "1979-04-06T18:35:24.00Z" val clock = FakeClock().apply { givenInstant(Instant.parse(now)) } - val dateFormatters = DateFormatters(Locale.US, clock, TimeZone.UTC) + val dateFormatters = DateFormatters(Locale.US, clock) { TimeZone.UTC } assertThat(dateFormatters.formatDateWithFullFormat(Instant.parse(dat).toLocalDateTime(TimeZone.UTC))).isEqualTo("Friday, April 6, 1979") } @@ -102,8 +102,8 @@ class DefaultLastMessageTimestampFormatterTest { */ private fun createFormatter(@Suppress("SameParameterValue") currentDate: String): LastMessageTimestampFormatter { val clock = FakeClock().apply { givenInstant(Instant.parse(currentDate)) } - val localDateTimeProvider = LocalDateTimeProvider(clock, TimeZone.UTC) - val dateFormatters = DateFormatters(Locale.US, clock, TimeZone.UTC) + val localDateTimeProvider = LocalDateTimeProvider(clock) { TimeZone.UTC } + val dateFormatters = DateFormatters(Locale.US, clock) { TimeZone.UTC } return DefaultLastMessageTimestampFormatter(localDateTimeProvider, dateFormatters) } } diff --git a/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/RoomLastMessageFormatter.kt b/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/RoomLastMessageFormatter.kt index e5eabdb9d4..e6815e5bcf 100644 --- a/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/RoomLastMessageFormatter.kt +++ b/libraries/eventformatter/api/src/main/kotlin/io/element/android/libraries/eventformatter/api/RoomLastMessageFormatter.kt @@ -9,6 +9,6 @@ package io.element.android.libraries.eventformatter.api import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem -interface RoomLastMessageFormatter { +fun interface RoomLastMessageFormatter { fun format(event: EventTimelineItem, isDmRoom: Boolean): CharSequence? } diff --git a/samples/minimal/build.gradle.kts b/samples/minimal/build.gradle.kts index ae7b6d58d4..e32054a272 100644 --- a/samples/minimal/build.gradle.kts +++ b/samples/minimal/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation(projects.libraries.permissions.noop) implementation(projects.libraries.sessionStorage.implMemory) implementation(projects.libraries.designsystem) + implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) implementation(projects.libraries.core) implementation(projects.libraries.network) diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index 59969ec622..0db3c9da51 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -24,6 +24,7 @@ import io.element.android.features.roomlist.impl.filters.RoomListFiltersPresente import io.element.android.features.roomlist.impl.filters.selection.DefaultFilterSelectionStrategy import io.element.android.features.roomlist.impl.search.RoomListSearchDataSource import io.element.android.features.roomlist.impl.search.RoomListSearchPresenter +import io.element.android.libraries.androidutils.system.DefaultDateTimeObserver import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.dateformatter.impl.DateFormatters import io.element.android.libraries.dateformatter.impl.DefaultLastMessageTimestampFormatter @@ -59,9 +60,8 @@ class RoomListScreen( ) { private val clock = Clock.System private val locale = Locale.getDefault() - private val timeZone = TimeZone.currentSystemDefault() - private val dateTimeProvider = LocalDateTimeProvider(clock, timeZone) - private val dateFormatters = DateFormatters(locale, clock, timeZone) + private val dateTimeProvider = LocalDateTimeProvider(clock) { TimeZone.currentSystemDefault() } + private val dateFormatters = DateFormatters(locale, clock) { TimeZone.currentSystemDefault() } private val sessionVerificationService = matrixClient.sessionVerificationService() private val encryptionService = matrixClient.encryptionService() private val stringProvider = AndroidStringProvider(context.resources) @@ -92,7 +92,8 @@ class RoomListScreen( roomListRoomSummaryFactory = roomListRoomSummaryFactory, coroutineDispatchers = coroutineDispatchers, notificationSettingsService = matrixClient.notificationSettingsService(), - appScope = Singleton.appScope + appScope = Singleton.appScope, + dateTimeObserver = DefaultDateTimeObserver(context), ), indicatorService = DefaultIndicatorService( sessionVerificationService = sessionVerificationService, From 2513ae769fff2206ae78c4e89f5562fed4a2e5ef Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Oct 2024 16:39:32 +0200 Subject: [PATCH 29/67] Improve test `Classes extending 'PreviewParameterProvider' name MUST end with 'Provider' and MUST contain provided class name` --- .../tests/konsist/KonsistClassNameTest.kt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) 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 4257599019..8f7d446d67 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 @@ -9,6 +9,7 @@ package io.element.android.tests.konsist import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.bumble.appyx.core.node.Node +import com.google.common.truth.Truth.assertThat import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.ext.list.withAllParentsOf import com.lemonappdev.konsist.api.ext.list.withAnnotationNamed @@ -44,19 +45,26 @@ class KonsistClassNameTest { @Test fun `Classes extending 'PreviewParameterProvider' name MUST end with 'Provider' and MUST contain provided class name`() { - Konsist.scopeFromProject() + Konsist.scopeFromProduction() .classes() .withAllParentsOf(PreviewParameterProvider::class) - .assertTrue { + .also { + // Check that classes are actually found + assertThat(it.size).isGreaterThan(100) + } + .assertTrue { klass -> // Cannot find a better way to get the type of the generic - val providedType = it.text + val providedType = klass.text + .substringAfter("PreviewParameterProvider<") .substringBefore(">") - .substringAfter("<") // Get the substring before the first '<' to remove the generic type .substringBefore("<") .removeSuffix("?") .replace(".", "") - it.name.endsWith("Provider") && (it.name.contains("IconList") || it.name.contains(providedType)) + val name = klass.name + name.endsWith("Provider") && + name.endsWith("PreviewProvider").not() && + name.contains(providedType) } } From 0113cd522db511a2b95598073d4592282b84e828 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 14 Oct 2024 16:51:12 +0200 Subject: [PATCH 30/67] Rename some classes which implement PreviewParameterProvider according to the new naming convention. `Provider` suffix is enough and more used than `PreviewProvider`, so let's make the codebase more consistent. --- ...eviewProvider.kt => QrCodeConfirmationStepProvider.kt} | 2 +- .../screens/qrcode/confirmation/QrCodeConfirmationView.kt | 2 +- ...atePreviewProvider.kt => BlockedUsersStateProvider.kt} | 2 +- .../preferences/impl/blockedusers/BlockedUsersView.kt | 2 +- ...wProvider.kt => RoomMembersModerationStateProvider.kt} | 2 +- .../impl/members/moderation/RoomMembersModerationView.kt | 2 +- ...wProvider.kt => ChangeRoomPermissionsStateProvider.kt} | 2 +- .../permissions/ChangeRoomPermissionsView.kt | 2 +- .../android/libraries/designsystem/components/BigIcon.kt | 4 ++-- .../libraries/designsystem/components/PageTitle.kt | 2 +- .../android/libraries/designsystem/icons/IconsPreview.kt | 8 ++++---- 11 files changed, 15 insertions(+), 15 deletions(-) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/{QrCodeConfirmationStepPreviewProvider.kt => QrCodeConfirmationStepProvider.kt} (85%) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/{BlockedUsersStatePreviewProvider.kt => BlockedUsersStateProvider.kt} (94%) rename features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/{RoomMembersModerationStatePreviewProvider.kt => RoomMembersModerationStateProvider.kt} (97%) rename features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/{ChangeRoomPermissionsStatePreviewProvider.kt => ChangeRoomPermissionsStateProvider.kt} (96%) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepPreviewProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepProvider.kt similarity index 85% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepPreviewProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepProvider.kt index 53f656d88c..a0f85faab5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepPreviewProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationStepProvider.kt @@ -9,7 +9,7 @@ package io.element.android.features.login.impl.screens.qrcode.confirmation import androidx.compose.ui.tooling.preview.PreviewParameterProvider -class QrCodeConfirmationStepPreviewProvider : PreviewParameterProvider { +class QrCodeConfirmationStepProvider : 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 29c6b746c6..83806daff5 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(QrCodeConfirmationStepPreviewProvider::class) step: QrCodeConfirmationStep) { +internal fun QrCodeConfirmationViewPreview(@PreviewParameter(QrCodeConfirmationStepProvider::class) step: QrCodeConfirmationStep) { ElementPreview { QrCodeConfirmationView( step = step, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStateProvider.kt similarity index 94% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewProvider.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStateProvider.kt index 8bf5b026f8..9c3bc75cfa 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStateProvider.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.toPersistentList -class BlockedUsersStatePreviewProvider : PreviewParameterProvider { +class BlockedUsersStateProvider : 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 8332fc959a..1704925bde 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 @@ -123,7 +123,7 @@ private fun BlockedUserItem( @PreviewsDayNight @Composable -internal fun BlockedUsersViewPreview(@PreviewParameter(BlockedUsersStatePreviewProvider::class) state: BlockedUsersState) { +internal fun BlockedUsersViewPreview(@PreviewParameter(BlockedUsersStateProvider::class) state: BlockedUsersState) { ElementPreview { BlockedUsersView( state = state, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStatePreviewProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt similarity index 97% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStatePreviewProvider.kt rename to features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt index 2642cc501a..221bce15ff 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStatePreviewProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.room.RoomMember import kotlinx.collections.immutable.toPersistentList -class RoomMembersModerationStatePreviewProvider : PreviewParameterProvider { +class RoomMembersModerationStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomMembersModerationState( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationView.kt index 60938627c8..f2fa89beca 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationView.kt @@ -284,7 +284,7 @@ private fun RoomMemberActionsBottomSheet( @PreviewsDayNight @Composable -internal fun RoomMembersModerationViewPreview(@PreviewParameter(RoomMembersModerationStatePreviewProvider::class) state: RoomMembersModerationState) { +internal fun RoomMembersModerationViewPreview(@PreviewParameter(RoomMembersModerationStateProvider::class) state: RoomMembersModerationState) { ElementPreview { Box( modifier = Modifier diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStatePreviewProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStateProvider.kt similarity index 96% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStatePreviewProvider.kt rename to features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStateProvider.kt index 9dcaa15b43..0978d918f9 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStatePreviewProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStateProvider.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels import kotlinx.collections.immutable.toPersistentList -class ChangeRoomPermissionsStatePreviewProvider : PreviewParameterProvider { +class ChangeRoomPermissionsStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aChangeRoomPermissionsState(section = ChangeRoomPermissionsSection.RoomDetails), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt index 9bfe2ebffb..b5af67895f 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt @@ -180,7 +180,7 @@ private fun titleForSection(item: RoomPermissionType): String = when (item) { @PreviewsDayNight @Composable -internal fun ChangeRoomPermissionsViewPreview(@PreviewParameter(ChangeRoomPermissionsStatePreviewProvider::class) state: ChangeRoomPermissionsState) { +internal fun ChangeRoomPermissionsViewPreview(@PreviewParameter(ChangeRoomPermissionsStateProvider::class) state: ChangeRoomPermissionsState) { ElementPreview { ChangeRoomPermissionsView( state = state, 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 f44b61602e..6140373b21 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 @@ -126,7 +126,7 @@ object BigIcon { internal fun BigIconPreview() { ElementPreview { Row(horizontalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.padding(10.dp)) { - val provider = BigIconStylePreviewProvider() + val provider = BigIconStyleProvider() for (style in provider.values) { BigIcon(style = style) } @@ -134,7 +134,7 @@ internal fun BigIconPreview() { } } -internal class BigIconStylePreviewProvider : PreviewParameterProvider { +internal class BigIconStyleProvider : 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/PageTitle.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PageTitle.kt index d8e83c2a89..2180acfa10 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PageTitle.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PageTitle.kt @@ -99,7 +99,7 @@ fun PageTitle( @PreviewsDayNight @Composable -internal fun PageTitleWithIconFullPreview(@PreviewParameter(BigIconStylePreviewProvider::class) style: BigIcon.Style) { +internal fun PageTitleWithIconFullPreview(@PreviewParameter(BigIconStyleProvider::class) style: BigIcon.Style) { ElementPreview { PageTitle( modifier = Modifier.padding(top = 24.dp), diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt index ce28a985ba..b619064710 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt @@ -30,7 +30,7 @@ import io.element.android.libraries.designsystem.theme.components.Text import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toPersistentList -internal class CompoundIconListPreviewProvider : PreviewParameterProvider { +internal class CompoundIconChunkProvider : PreviewParameterProvider { override val values: Sequence get() { val chunks = CompoundIcons.allResIds.chunked(36) @@ -41,7 +41,7 @@ internal class CompoundIconListPreviewProvider : PreviewParameterProvider { +internal class OtherIconChunkProvider : PreviewParameterProvider { override val values: Sequence get() { val chunks = iconsOther.chunked(36) @@ -60,7 +60,7 @@ internal data class IconChunk( @PreviewsDayNight @Composable -internal fun IconsCompoundPreview(@PreviewParameter(CompoundIconListPreviewProvider::class) chunk: IconChunk) = ElementPreview { +internal fun IconsCompoundPreview(@PreviewParameter(CompoundIconChunkProvider::class) chunk: IconChunk) = ElementPreview { IconsPreview( title = "R.drawable.ic_compound_* ${chunk.index}/${chunk.total}", iconsList = chunk.icons, @@ -73,7 +73,7 @@ internal fun IconsCompoundPreview(@PreviewParameter(CompoundIconListPreviewProvi @PreviewsDayNight @Composable -internal fun IconsOtherPreview(@PreviewParameter(OtherIconListPreviewProvider::class) iconChunk: IconChunk) = ElementPreview { +internal fun IconsOtherPreview(@PreviewParameter(OtherIconChunkProvider::class) iconChunk: IconChunk) = ElementPreview { IconsPreview( title = "R.drawable.ic_* ${iconChunk.index}/${iconChunk.total}", iconsList = iconChunk.icons, From 4ec7e6fa87dbf88d521c79f31ee901bd2fffe68e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 11:26:26 +0200 Subject: [PATCH 31/67] Use size from the DrawScope. --- .../impl/timeline/components/MessageEventBubble.kt | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) 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 1e69dbaa72..50a1dd2a94 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 @@ -18,10 +18,7 @@ import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ripple import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -32,7 +29,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.LayoutDirection @@ -108,7 +104,6 @@ fun MessageEventBubble( val radiusPx = (avatarRadius + SENDER_AVATAR_BORDER_WIDTH).toPx() val yOffsetPx = -(NEGATIVE_MARGIN_FOR_BUBBLE + avatarRadius).toPx() val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl - var contentWidthPx by remember { mutableFloatStateOf(0f) } BoxWithConstraints( modifier = modifier .graphicsLayer { @@ -120,7 +115,7 @@ fun MessageEventBubble( drawCircle( color = Color.Black, center = Offset( - x = if (isRtl) contentWidthPx else 0f, + x = if (isRtl) size.width else 0f, y = yOffsetPx, ), radius = radiusPx, @@ -147,10 +142,7 @@ fun MessageEventBubble( onLongClick = onLongClick, indication = ripple(), interactionSource = interactionSource - ) - .onGloballyPositioned { coordinates -> - contentWidthPx = coordinates.size.width.toFloat() - }, + ), color = backgroundBubbleColor, shape = bubbleShape, content = content From d468a009d5480bc913e2bca30fa9d7c88687511a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 10 Oct 2024 13:44:24 +0200 Subject: [PATCH 32/67] Room moderation: make it more reactive and simplify the code. --- .../members/moderation/ConfirmingBanUser.kt | 15 +++ .../moderation/RoomMembersModerationEvents.kt | 3 +- .../RoomMembersModerationPresenter.kt | 96 +++++++++---------- .../RoomMembersModerationStateProvider.kt | 2 +- .../moderation/RoomMembersModerationView.kt | 23 +++-- .../RoomMembersModerationPresenterTest.kt | 51 ++++++---- .../RoomMembersModerationViewTest.kt | 7 +- .../matrix/ui/room/MatrixRoomState.kt | 33 +++++++ 8 files changed, 143 insertions(+), 87 deletions(-) create mode 100644 features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingBanUser.kt diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingBanUser.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingBanUser.kt new file mode 100644 index 0000000000..fbfa60b5cc --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingBanUser.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.roomdetails.impl.members.moderation + +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.room.RoomMember + +data class ConfirmingBanUser( + val roomMember: RoomMember, +) : AsyncAction.Confirming diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationEvents.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationEvents.kt index 02700264f2..15304207f2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationEvents.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationEvents.kt @@ -7,12 +7,13 @@ package io.element.android.features.roomdetails.impl.members.moderation +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomMember sealed interface RoomMembersModerationEvents { data class SelectRoomMember(val roomMember: RoomMember) : RoomMembersModerationEvents data object KickUser : RoomMembersModerationEvents data object BanUser : RoomMembersModerationEvents - data object UnbanUser : RoomMembersModerationEvents + data class UnbanUser(val userId: UserId) : RoomMembersModerationEvents data object Reset : RoomMembersModerationEvents } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt index 97e518fb47..c025fa1bbc 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt @@ -10,9 +10,9 @@ package io.element.android.features.roomdetails.impl.members.moderation import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue @@ -21,16 +21,15 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runUpdatingState import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.core.extensions.finally import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState -import io.element.android.libraries.matrix.api.room.isDm -import io.element.android.libraries.matrix.api.room.powerlevels.canBan -import io.element.android.libraries.matrix.api.room.powerlevels.canKick +import io.element.android.libraries.matrix.ui.room.canBanAsState +import io.element.android.libraries.matrix.ui.room.canKickAsState +import io.element.android.libraries.matrix.ui.room.isDmAsState +import io.element.android.libraries.matrix.ui.room.userPowerLevelAsState import io.element.android.services.analytics.api.AnalyticsService -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.drop @@ -45,21 +44,39 @@ class RoomMembersModerationPresenter @Inject constructor( ) : Presenter { private var selectedMember by mutableStateOf(null) - private suspend fun canBan() = room.canBan().getOrDefault(false) - private suspend fun canKick() = room.canKick().getOrDefault(false) - @Composable override fun present(): RoomMembersModerationState { val coroutineScope = rememberCoroutineScope() - var moderationActions by remember { mutableStateOf(persistentListOf()) } - val syncUpdateFlow = room.syncUpdateFlow.collectAsState() - val canDisplayModerationActions by produceState( - initialValue = false, - key1 = syncUpdateFlow.value - ) { - value = !room.isDm && (canBan() || canKick()) + val canBan by room.canBanAsState(syncUpdateFlow.value) + val canKick by room.canKickAsState(syncUpdateFlow.value) + val isDm by room.isDmAsState(syncUpdateFlow.value) + val currentUserMemberPowerLevel by room.userPowerLevelAsState(syncUpdateFlow.value) + + val canDisplayModerationActions by remember { + derivedStateOf { !isDm && (canBan || canKick) } } + val canDisplayBannedUsers by remember { + derivedStateOf { !isDm && canBan } + } + val moderationActions by remember { + derivedStateOf { + buildList { + selectedMember?.let { roomMember -> + add(ModerationAction.DisplayProfile(roomMember.userId)) + if (currentUserMemberPowerLevel > roomMember.powerLevel) { + if (canKick) { + add(ModerationAction.KickUser(roomMember.userId)) + } + if (canBan) { + add(ModerationAction.BanUser(roomMember.userId)) + } + } + } + }.toPersistentList() + } + } + val kickUserAsyncAction = remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction) } val banUserAsyncAction = @@ -67,64 +84,37 @@ class RoomMembersModerationPresenter @Inject constructor( val unbanUserAsyncAction = remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction) } - val canDisplayBannedUsers by produceState(initialValue = false) { - value = !room.isDm && canBan() - } - fun handleEvent(event: RoomMembersModerationEvents) { when (event) { is RoomMembersModerationEvents.SelectRoomMember -> { - coroutineScope.launch { + if (event.roomMember.membership == RoomMembershipState.BAN && canBan) { + unbanUserAsyncAction.value = ConfirmingBanUser(event.roomMember) + } else { selectedMember = event.roomMember - if (event.roomMember.membership == RoomMembershipState.BAN && canBan()) { - unbanUserAsyncAction.value = AsyncAction.ConfirmingNoParams - } else { - moderationActions = buildList { - add(ModerationAction.DisplayProfile(event.roomMember.userId)) - val currentUserMemberPowerLevel = room.userRole(room.sessionId) - .getOrDefault(RoomMember.Role.USER) - .powerLevel - if (currentUserMemberPowerLevel > event.roomMember.powerLevel) { - if (canKick()) { - add(ModerationAction.KickUser(event.roomMember.userId)) - } - if (canBan()) { - add(ModerationAction.BanUser(event.roomMember.userId)) - } - } - }.toPersistentList() - } } } is RoomMembersModerationEvents.KickUser -> { - moderationActions = persistentListOf() selectedMember?.let { coroutineScope.kickUser(it.userId, kickUserAsyncAction) } + selectedMember = null } is RoomMembersModerationEvents.BanUser -> { if (banUserAsyncAction.value.isConfirming()) { - moderationActions = persistentListOf() selectedMember?.let { coroutineScope.banUser(it.userId, banUserAsyncAction) } + selectedMember = null } else { banUserAsyncAction.value = AsyncAction.ConfirmingNoParams } } is RoomMembersModerationEvents.UnbanUser -> { - if (unbanUserAsyncAction.value.isConfirming()) { - moderationActions = persistentListOf() - selectedMember?.let { - coroutineScope.unbanUser(it.userId, unbanUserAsyncAction) - } - } else { - unbanUserAsyncAction.value = AsyncAction.ConfirmingNoParams - } + // We are already confirming when we are reaching this point + coroutineScope.unbanUser(event.userId, unbanUserAsyncAction) } is RoomMembersModerationEvents.Reset -> { selectedMember = null - moderationActions = persistentListOf() kickUserAsyncAction.value = AsyncAction.Uninitialized banUserAsyncAction.value = AsyncAction.Uninitialized unbanUserAsyncAction.value = AsyncAction.Uninitialized @@ -149,7 +139,7 @@ class RoomMembersModerationPresenter @Inject constructor( kickUserAction: MutableState>, ) = runActionAndWaitForMembershipChange(kickUserAction) { analyticsService.capture(RoomModeration(RoomModeration.Action.KickMember)) - room.kickUser(userId).finally { selectedMember = null } + room.kickUser(userId) } private fun CoroutineScope.banUser( @@ -157,7 +147,7 @@ class RoomMembersModerationPresenter @Inject constructor( banUserAction: MutableState>, ) = runActionAndWaitForMembershipChange(banUserAction) { analyticsService.capture(RoomModeration(RoomModeration.Action.BanMember)) - room.banUser(userId).finally { selectedMember = null } + room.banUser(userId) } private fun CoroutineScope.unbanUser( @@ -165,7 +155,7 @@ class RoomMembersModerationPresenter @Inject constructor( unbanUserAction: MutableState>, ) = runActionAndWaitForMembershipChange(unbanUserAction) { analyticsService.capture(RoomModeration(RoomModeration.Action.UnbanMember)) - room.unbanUser(userId).finally { selectedMember = null } + room.unbanUser(userId) } private fun CoroutineScope.runActionAndWaitForMembershipChange( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt index 221bce15ff..2c2fb6d27c 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt @@ -60,7 +60,7 @@ class RoomMembersModerationStateProvider : PreviewParameterProvider { - state.selectedRoomMember?.let { + if (action is ConfirmingBanUser) { ConfirmationDialog( title = stringResource(R.string.screen_room_member_list_manage_member_unban_title), content = stringResource(R.string.screen_room_member_list_manage_member_unban_message), submitText = stringResource(R.string.screen_room_member_list_manage_member_unban_action), - onSubmitClick = { state.eventSink(RoomMembersModerationEvents.UnbanUser) }, + onSubmitClick = { + val userDisplayName = action.roomMember.getBestName() + asyncIndicatorState.enqueue { + AsyncIndicator.Loading(text = stringResource(R.string.screen_room_member_list_unbanning_user, userDisplayName)) + } + state.eventSink(RoomMembersModerationEvents.UnbanUser(action.roomMember.userId)) + }, onDismiss = { state.eventSink(RoomMembersModerationEvents.Reset) }, ) } } - is AsyncAction.Loading -> { - LaunchedEffect(action) { - val userDisplayName = state.selectedRoomMember?.getBestName().orEmpty() - asyncIndicatorState.enqueue { - AsyncIndicator.Loading(text = stringResource(R.string.screen_room_member_list_unbanning_user, userDisplayName)) - } - } - } is AsyncAction.Failure -> { Timber.e(action.error, "Failed to unban user.") LaunchedEffect(action) { @@ -178,7 +176,8 @@ fun RoomMembersModerationView( is AsyncAction.Success -> { LaunchedEffect(action) { asyncIndicatorState.clear() } } - else -> Unit + is AsyncAction.Loading, + AsyncAction.Uninitialized -> Unit } } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt index a4308e23aa..1480f80716 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt @@ -14,6 +14,7 @@ import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.RoomModeration import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.aVictor +import io.element.android.features.roomdetails.impl.members.moderation.ConfirmingBanUser import io.element.android.features.roomdetails.impl.members.moderation.ModerationAction import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationPresenter @@ -37,7 +38,14 @@ import org.junit.Test class RoomMembersModerationPresenterTest { @Test fun `canDisplayModerationActions - when room is DM is false`() = runTest { - val room = FakeMatrixRoom(isDirect = true, isPublic = true, activeMemberCount = 2).apply { + val room = FakeMatrixRoom( + isDirect = true, + isPublic = true, + activeMemberCount = 2, + canKickResult = { Result.success(true) }, + canBanResult = { Result.success(true) }, + userRoleResult = { Result.success(RoomMember.Role.ADMIN) }, + ).apply { givenRoomInfo(aRoomInfo(isDirect = true, isPublic = false, activeMembersCount = 2)) } val presenter = createRoomMembersModerationPresenter(matrixRoom = room) @@ -53,6 +61,7 @@ class RoomMembersModerationPresenterTest { activeMemberCount = 10, canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, + userRoleResult = { Result.success(RoomMember.Role.ADMIN) }, ) val presenter = createRoomMembersModerationPresenter(matrixRoom = room) presenter.test { @@ -66,7 +75,9 @@ class RoomMembersModerationPresenterTest { val room = FakeMatrixRoom( isDirect = false, activeMemberCount = 10, + canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, + userRoleResult = { Result.success(RoomMember.Role.ADMIN) }, ) val presenter = createRoomMembersModerationPresenter(matrixRoom = room) presenter.test { @@ -141,8 +152,8 @@ class RoomMembersModerationPresenterTest { skipItems(1) awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember)) with(awaitItem()) { - assertThat(selectedRoomMember).isNotNull() - assertThat(unbanUserAsyncAction).isEqualTo(AsyncAction.ConfirmingNoParams) + assertThat(selectedRoomMember).isNull() + assertThat(unbanUserAsyncAction).isEqualTo(ConfirmingBanUser(selectedMember)) } } } @@ -165,8 +176,9 @@ class RoomMembersModerationPresenterTest { awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember)) awaitItem().eventSink(RoomMembersModerationEvents.KickUser) skipItems(1) - assertThat(awaitItem().actions).isEmpty() - assertThat(awaitItem().kickUserAsyncAction).isEqualTo(AsyncAction.Loading) + val loadingState = awaitItem() + assertThat(loadingState.actions).isEmpty() + assertThat(loadingState.kickUserAsyncAction).isEqualTo(AsyncAction.Loading) with(awaitItem()) { assertThat(kickUserAsyncAction).isEqualTo(AsyncAction.Success(Unit)) assertThat(selectedRoomMember).isNull() @@ -198,8 +210,10 @@ class RoomMembersModerationPresenterTest { // Confirm confirmingState.eventSink(RoomMembersModerationEvents.BanUser) skipItems(1) - assertThat(awaitItem().actions).isEmpty() - assertThat(awaitItem().banUserAsyncAction).isEqualTo(AsyncAction.Loading) + val loadingItem = awaitItem() + assertThat(loadingItem.actions).isEmpty() + assertThat(loadingItem.selectedRoomMember).isNull() + assertThat(loadingItem.banUserAsyncAction).isEqualTo(AsyncAction.Loading) with(awaitItem()) { assertThat(banUserAsyncAction).isEqualTo(AsyncAction.Success(Unit)) assertThat(selectedRoomMember).isNull() @@ -225,11 +239,14 @@ class RoomMembersModerationPresenterTest { presenter.present() }.test { skipItems(1) - // Displays confirmation dialog + // Displays unban confirmation dialog awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember)) + val confirmingState = awaitItem() + assertThat(confirmingState.selectedRoomMember).isNull() + assertThat(confirmingState.actions).isEmpty() + assertThat(confirmingState.unbanUserAsyncAction).isEqualTo(ConfirmingBanUser(selectedMember)) // Confirms unban - awaitItem().eventSink(RoomMembersModerationEvents.UnbanUser) - assertThat(awaitItem().actions).isEmpty() + confirmingState.eventSink(RoomMembersModerationEvents.UnbanUser(selectedMember.userId)) assertThat(awaitItem().unbanUserAsyncAction).isEqualTo(AsyncAction.Loading) with(awaitItem()) { assertThat(unbanUserAsyncAction).isEqualTo(AsyncAction.Success(Unit)) @@ -251,12 +268,13 @@ class RoomMembersModerationPresenterTest { presenter.present() }.test { skipItems(1) - // Displays confirmation dialog + // Select a user awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor())) // Reset state awaitItem().eventSink(RoomMembersModerationEvents.Reset) - assertThat(awaitItem().selectedRoomMember).isNull() - assertThat(awaitItem().actions).isEmpty() + val finalItem = awaitItem() + assertThat(finalItem.selectedRoomMember).isNull() + assertThat(finalItem.actions).isEmpty() } } @@ -278,7 +296,7 @@ class RoomMembersModerationPresenterTest { // Kick user and fail awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor())) awaitItem().eventSink(RoomMembersModerationEvents.KickUser) - skipItems(2) + skipItems(1) assertThat(awaitItem().kickUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().kickUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java) // Reset it @@ -289,7 +307,7 @@ class RoomMembersModerationPresenterTest { initialItem.eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor())) awaitItem().eventSink(RoomMembersModerationEvents.BanUser) awaitItem().eventSink(RoomMembersModerationEvents.BanUser) - skipItems(2) + skipItems(1) assertThat(awaitItem().banUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().banUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java) // Reset it @@ -300,8 +318,7 @@ class RoomMembersModerationPresenterTest { initialItem.eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor().copy(membership = RoomMembershipState.BAN))) val confirmingState = awaitItem() assertThat(confirmingState.unbanUserAsyncAction).isInstanceOf(AsyncAction.Confirming::class.java) - confirmingState.eventSink(RoomMembersModerationEvents.UnbanUser) - skipItems(1) + confirmingState.eventSink(RoomMembersModerationEvents.UnbanUser(aVictor().userId)) assertThat(awaitItem().unbanUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().unbanUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java) // Reset it diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt index 723f94d20a..41821411f9 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.roomdetails.impl.R import io.element.android.features.roomdetails.impl.members.anAlice +import io.element.android.features.roomdetails.impl.members.moderation.ConfirmingBanUser import io.element.android.features.roomdetails.impl.members.moderation.ModerationAction import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationState @@ -164,7 +165,7 @@ class RoomMembersModerationViewTest { val roomMember = anAlice() val state = aRoomMembersModerationState( selectedRoomMember = roomMember, - unbanUserAsyncAction = AsyncAction.ConfirmingNoParams, + unbanUserAsyncAction = ConfirmingBanUser(roomMember), eventSink = eventsRecorder ) rule.setRoomMembersModerationView( @@ -181,7 +182,7 @@ class RoomMembersModerationViewTest { val roomMember = anAlice() val state = aRoomMembersModerationState( selectedRoomMember = roomMember, - unbanUserAsyncAction = AsyncAction.ConfirmingNoParams, + unbanUserAsyncAction = ConfirmingBanUser(roomMember), eventSink = eventsRecorder ) rule.setRoomMembersModerationView( @@ -189,7 +190,7 @@ class RoomMembersModerationViewTest { ) // Note: the string key semantics is not perfect here :/ rule.clickOn(R.string.screen_room_member_list_manage_member_unban_action) - eventsRecorder.assertSingle(RoomMembersModerationEvents.UnbanUser) + eventsRecorder.assertSingle(RoomMembersModerationEvents.UnbanUser(roomMember.userId)) } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt index 3dd81344fc..81ae3e6b89 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt @@ -15,7 +15,10 @@ import androidx.compose.runtime.produceState import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.isDm +import io.element.android.libraries.matrix.api.room.powerlevels.canBan import io.element.android.libraries.matrix.api.room.powerlevels.canInvite +import io.element.android.libraries.matrix.api.room.powerlevels.canKick import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage @@ -62,6 +65,36 @@ fun MatrixRoom.canPinUnpin(updateKey: Long): State { } } +@Composable +fun MatrixRoom.isDmAsState(updateKey: Long): State { + return produceState(initialValue = false, key1 = updateKey) { + value = isDm + } +} + +@Composable +fun MatrixRoom.canKickAsState(updateKey: Long): State { + return produceState(initialValue = false, key1 = updateKey) { + value = canKick().getOrElse { false } + } +} + +@Composable +fun MatrixRoom.canBanAsState(updateKey: Long): State { + return produceState(initialValue = false, key1 = updateKey) { + value = canBan().getOrElse { false } + } +} + +@Composable +fun MatrixRoom.userPowerLevelAsState(updateKey: Long): State { + return produceState(initialValue = 0, key1 = updateKey) { + value = userRole(sessionId) + .getOrDefault(RoomMember.Role.USER) + .powerLevel + } +} + @Composable fun MatrixRoom.isOwnUserAdmin(): Boolean { val roomInfo by roomInfoFlow.collectAsState(initial = null) From 0baa0dbfc38d43716959905ae0e6478b1d9d9ae1 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 16 Oct 2024 08:26:49 +0000 Subject: [PATCH 33/67] Update screenshots --- ....members.moderation_RoomMembersModerationView_Day_5_en.png | 4 ++-- ...embers.moderation_RoomMembersModerationView_Night_5_en.png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en.png index dfd31916cb..1b6fb4bab8 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e0b84ff9cdbc6cc203304ff350789437533f9f7a1d95e8a196cce3585c454ec -size 9143 +oid sha256:96a867cb12498cbdc97957bee07855dfaa13602baddaf933aff2b666ef4c7650 +size 3642 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en.png index 24e6d2e293..d6fd8eeb70 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b4fd072108b60d09d5c37c56e0e97272a1664ca5d21a27152e111539ac1a640 -size 7861 +oid sha256:5bb36ccd718f3fec5b04f1bc812dc7718b5ea7fa4619c8b031466297a8d016fd +size 3659 From 9aafcc7564abc2c72829bdb29b6d1943eee07732 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 11:58:54 +0200 Subject: [PATCH 34/67] The name ConfirmingBanUser was wrong since it was used to confirm a unban action. Better to keep a generic name, in case this class is used for any other action. --- .../{ConfirmingBanUser.kt => ConfirmingRoomMemberAction.kt} | 2 +- .../members/moderation/RoomMembersModerationPresenter.kt | 4 +++- .../moderation/RoomMembersModerationStateProvider.kt | 2 +- .../impl/members/moderation/RoomMembersModerationView.kt | 2 +- .../moderation/RoomMembersModerationPresenterTest.kt | 6 +++--- .../members/moderation/RoomMembersModerationViewTest.kt | 6 +++--- 6 files changed, 12 insertions(+), 10 deletions(-) rename features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/{ConfirmingBanUser.kt => ConfirmingRoomMemberAction.kt} (91%) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingBanUser.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingRoomMemberAction.kt similarity index 91% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingBanUser.kt rename to features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingRoomMemberAction.kt index fbfa60b5cc..29e4e6b451 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingBanUser.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingRoomMemberAction.kt @@ -10,6 +10,6 @@ package io.element.android.features.roomdetails.impl.members.moderation import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.room.RoomMember -data class ConfirmingBanUser( +data class ConfirmingRoomMemberAction( val roomMember: RoomMember, ) : AsyncAction.Confirming diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt index c025fa1bbc..700344b139 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt @@ -88,8 +88,10 @@ class RoomMembersModerationPresenter @Inject constructor( when (event) { is RoomMembersModerationEvents.SelectRoomMember -> { if (event.roomMember.membership == RoomMembershipState.BAN && canBan) { - unbanUserAsyncAction.value = ConfirmingBanUser(event.roomMember) + // In this case the view will render a dialog to confirm the unbanning of the user + unbanUserAsyncAction.value = ConfirmingRoomMemberAction(event.roomMember) } else { + // In this case the view will render a bottom sheet. selectedMember = event.roomMember } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt index 2c2fb6d27c..8f05d36da6 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt @@ -60,7 +60,7 @@ class RoomMembersModerationStateProvider : PreviewParameterProvider { - if (action is ConfirmingBanUser) { + if (action is ConfirmingRoomMemberAction) { ConfirmationDialog( title = stringResource(R.string.screen_room_member_list_manage_member_unban_title), content = stringResource(R.string.screen_room_member_list_manage_member_unban_message), diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt index 1480f80716..423b93fc63 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt @@ -14,7 +14,7 @@ import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.RoomModeration import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.aVictor -import io.element.android.features.roomdetails.impl.members.moderation.ConfirmingBanUser +import io.element.android.features.roomdetails.impl.members.moderation.ConfirmingRoomMemberAction import io.element.android.features.roomdetails.impl.members.moderation.ModerationAction import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationPresenter @@ -153,7 +153,7 @@ class RoomMembersModerationPresenterTest { awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember)) with(awaitItem()) { assertThat(selectedRoomMember).isNull() - assertThat(unbanUserAsyncAction).isEqualTo(ConfirmingBanUser(selectedMember)) + assertThat(unbanUserAsyncAction).isEqualTo(ConfirmingRoomMemberAction(selectedMember)) } } } @@ -244,7 +244,7 @@ class RoomMembersModerationPresenterTest { val confirmingState = awaitItem() assertThat(confirmingState.selectedRoomMember).isNull() assertThat(confirmingState.actions).isEmpty() - assertThat(confirmingState.unbanUserAsyncAction).isEqualTo(ConfirmingBanUser(selectedMember)) + assertThat(confirmingState.unbanUserAsyncAction).isEqualTo(ConfirmingRoomMemberAction(selectedMember)) // Confirms unban confirmingState.eventSink(RoomMembersModerationEvents.UnbanUser(selectedMember.userId)) assertThat(awaitItem().unbanUserAsyncAction).isEqualTo(AsyncAction.Loading) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt index 41821411f9..02fdff8036 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.roomdetails.impl.R import io.element.android.features.roomdetails.impl.members.anAlice -import io.element.android.features.roomdetails.impl.members.moderation.ConfirmingBanUser +import io.element.android.features.roomdetails.impl.members.moderation.ConfirmingRoomMemberAction import io.element.android.features.roomdetails.impl.members.moderation.ModerationAction import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationState @@ -165,7 +165,7 @@ class RoomMembersModerationViewTest { val roomMember = anAlice() val state = aRoomMembersModerationState( selectedRoomMember = roomMember, - unbanUserAsyncAction = ConfirmingBanUser(roomMember), + unbanUserAsyncAction = ConfirmingRoomMemberAction(roomMember), eventSink = eventsRecorder ) rule.setRoomMembersModerationView( @@ -182,7 +182,7 @@ class RoomMembersModerationViewTest { val roomMember = anAlice() val state = aRoomMembersModerationState( selectedRoomMember = roomMember, - unbanUserAsyncAction = ConfirmingBanUser(roomMember), + unbanUserAsyncAction = ConfirmingRoomMemberAction(roomMember), eventSink = eventsRecorder ) rule.setRoomMembersModerationView( From 2c66e3a128c989560eb1cfb8370d8311e32d60f4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 13:01:09 +0200 Subject: [PATCH 35/67] Change type of parameter to MessageComposerMode.Special --- .../element/android/libraries/textcomposer/ComposerModeView.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 f9988adfba..4d5a9ec6f7 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 @@ -37,7 +37,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable internal fun ComposerModeView( - composerMode: MessageComposerMode, + composerMode: MessageComposerMode.Special, onResetComposerMode: () -> Unit, ) { when (composerMode) { @@ -52,7 +52,6 @@ internal fun ComposerModeView( onResetComposerMode = onResetComposerMode, ) } - else -> Unit } } From 751b5f73bf49edc9bd8adbb6e139952eb2f64b79 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 13:37:50 +0200 Subject: [PATCH 36/67] Reduce end padding of the composer, to give more space when composing messages. --- .../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 1f2e041ac5..a3920f5ec5 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 @@ -432,7 +432,7 @@ private fun TextInputBox( val defaultTypography = ElementTheme.typography.fontBodyLgRegular Box( modifier = Modifier - .padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 42.dp) + .padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp) // Apply test tag only once, otherwise 2 nodes will have it (both the normal and subcomposing one) and tests will fail .then(if (!subcomposing) Modifier.testTag(TestTags.textEditor) else Modifier), contentAlignment = Alignment.CenterStart, From 097d0567ac40cd6a4d6556639072aa9204e8704c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 13:50:58 +0200 Subject: [PATCH 37/67] Extract methods aMessageComposerModeEdit() and aMessageComposerModeReply() --- .../libraries/textcomposer/TextComposer.kt | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 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 a3920f5ec5..57cc810a7c 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 @@ -579,7 +579,7 @@ internal fun TextComposerEditPreview() = ElementPreview { ATextComposer( TextEditorState.Rich(aRichTextEditorState(initialText = "A message", initialFocus = true)), voiceMessageState = VoiceMessageState.Idle, - composerMode = MessageComposerMode.Edit(EventId("$1234"), TransactionId("1234"), "Some text"), + composerMode = aMessageComposerModeEdit(), enableVoiceMessages = true, ) })) @@ -592,7 +592,7 @@ internal fun MarkdownTextComposerEditPreview() = ElementPreview { ATextComposer( TextEditorState.Markdown(aMarkdownTextEditorState(initialText = "A message", initialFocus = true)), voiceMessageState = VoiceMessageState.Idle, - composerMode = MessageComposerMode.Edit(EventId("$1234"), TransactionId("1234"), "Some text"), + composerMode = aMessageComposerModeEdit(), enableVoiceMessages = true, ) })) @@ -604,9 +604,8 @@ internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider ATextComposer( state = TextEditorState.Rich(aRichTextEditorState()), voiceMessageState = VoiceMessageState.Idle, - composerMode = MessageComposerMode.Reply( + composerMode = aMessageComposerModeReply( replyToDetails = inReplyToDetails, - hideImage = false, ), enableVoiceMessages = true, ) @@ -718,3 +717,21 @@ fun aRichTextEditorState( initialMarkdown = initialMarkdown, initialFocus = initialFocus, ) + +fun aMessageComposerModeEdit( + eventId: EventId? = EventId("$1234"), + transactionId: TransactionId? = TransactionId("1234"), + content: String = "Some text", +) = MessageComposerMode.Edit( + eventId = eventId, + transactionId = transactionId, + content = content +) + +fun aMessageComposerModeReply( + replyToDetails: InReplyToDetails, + hideImage: Boolean = false, +) = MessageComposerMode.Reply( + replyToDetails = replyToDetails, + hideImage = hideImage, +) From 07848a30dc6f4d9c49cb3fb2728675354dd4ec61 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 13:52:17 +0200 Subject: [PATCH 38/67] Add preview on ComposerModeView --- .../textcomposer/ComposerModeView.kt | 26 ++++++++++++++++--- .../MessageComposerModeSpecialProvider.kt | 24 +++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerModeSpecialProvider.kt 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 4d5a9ec6f7..a66667448c 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 @@ -25,9 +25,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails @@ -39,14 +42,18 @@ import io.element.android.libraries.ui.strings.CommonStrings internal fun ComposerModeView( composerMode: MessageComposerMode.Special, onResetComposerMode: () -> Unit, + modifier: Modifier = Modifier, ) { when (composerMode) { is MessageComposerMode.Edit -> { - EditingModeView(onResetComposerMode = onResetComposerMode) + EditingModeView( + modifier = modifier, + onResetComposerMode = onResetComposerMode, + ) } is MessageComposerMode.Reply -> { ReplyToModeView( - modifier = Modifier.padding(8.dp), + modifier = modifier.padding(8.dp), replyToDetails = composerMode.replyToDetails, hideImage = composerMode.hideImage, onResetComposerMode = onResetComposerMode, @@ -58,11 +65,12 @@ internal fun ComposerModeView( @Composable private fun EditingModeView( onResetComposerMode: () -> Unit, + modifier: Modifier = Modifier, ) { Row( horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically, - modifier = Modifier + modifier = modifier .fillMaxWidth() .padding(start = 12.dp) ) { @@ -134,3 +142,15 @@ private fun ReplyToModeView( ) } } + +@PreviewsDayNight +@Composable +internal fun ComposerModeViewPreview( + @PreviewParameter(MessageComposerModeSpecialProvider::class) mode: MessageComposerMode.Special +) = ElementPreview { + ComposerModeView( + composerMode = mode, + onResetComposerMode = {}, + modifier = Modifier.background(ElementTheme.colors.bgSubtleSecondary) + ) +} 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/MessageComposerModeSpecialProvider.kt new file mode 100644 index 0000000000..c7d2edc6ac --- /dev/null +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerModeSpecialProvider.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.textcomposer + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsProvider +import io.element.android.libraries.textcomposer.model.MessageComposerMode + +class MessageComposerModeSpecialProvider : PreviewParameterProvider { + override val values: Sequence = sequenceOf( + aMessageComposerModeEdit() + ) + + // Keep only 3 values from InReplyToDetailsProvider + InReplyToDetailsProvider().values.take(3).map { + aMessageComposerModeReply( + replyToDetails = it + ) + } +} From 82519cf7a469540f619aa9f4e0226d88ae418822 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 13:57:29 +0200 Subject: [PATCH 39/67] Reduce start padding, the InReplyToView already have a 8 end padding. --- .../element/android/libraries/textcomposer/ComposerModeView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a66667448c..218a8653cf 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 @@ -131,7 +131,7 @@ private fun ReplyToModeView( contentDescription = stringResource(CommonStrings.action_close), tint = MaterialTheme.colorScheme.secondary, modifier = Modifier - .padding(end = 4.dp, top = 4.dp, start = 16.dp, bottom = 16.dp) + .padding(end = 4.dp, top = 4.dp, start = 8.dp, bottom = 16.dp) .size(16.dp) .clickable( enabled = true, From f209f81dfc823904f4edf79d6ad9aac7aba74002 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 16 Oct 2024 12:28:04 +0000 Subject: [PATCH 40/67] Update screenshots --- .../libraries.textcomposer_ComposerModeView_Day_0_en.png | 3 +++ .../libraries.textcomposer_ComposerModeView_Day_1_en.png | 3 +++ .../libraries.textcomposer_ComposerModeView_Day_2_en.png | 3 +++ .../libraries.textcomposer_ComposerModeView_Day_3_en.png | 3 +++ .../libraries.textcomposer_ComposerModeView_Night_0_en.png | 3 +++ .../libraries.textcomposer_ComposerModeView_Night_1_en.png | 3 +++ .../libraries.textcomposer_ComposerModeView_Night_2_en.png | 3 +++ .../libraries.textcomposer_ComposerModeView_Night_3_en.png | 3 +++ ...libraries.textcomposer_TextComposerFormatting_Day_0_en.png | 4 ++-- ...braries.textcomposer_TextComposerFormatting_Night_0_en.png | 4 ++-- .../libraries.textcomposer_TextComposerReply_Day_11_en.png | 4 ++-- .../libraries.textcomposer_TextComposerReply_Day_1_en.png | 4 ++-- .../libraries.textcomposer_TextComposerReply_Night_11_en.png | 4 ++-- .../libraries.textcomposer_TextComposerReply_Night_1_en.png | 4 ++-- .../libraries.textcomposer_TextComposerSimple_Day_0_en.png | 4 ++-- .../libraries.textcomposer_TextComposerSimple_Night_0_en.png | 4 ++-- 16 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_3_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_3_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_0_en.png new file mode 100644 index 0000000000..8268a75735 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d4d42730ec3015b56f13fcce98c4be694133b2101272e8f9173a5f938aef5ec +size 5455 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_1_en.png new file mode 100644 index 0000000000..9f798f1b0b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc65eecc2dc531039f061e96df9777e8a3cdb2813c94750fc81e2b2a71f12c55 +size 11175 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_2_en.png new file mode 100644 index 0000000000..13598fe9e2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3455702ce77e03aaa36b3da997a06bc52b626203a094779f8baa96357bab1d08 +size 18565 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_3_en.png new file mode 100644 index 0000000000..94191af9fc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfa41df7bd57609c97d68d0883fb9a747082e1e1dc69a3659b1efc5a37b92a9b +size 7818 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_0_en.png new file mode 100644 index 0000000000..6ebfe7bf20 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a7f8b640efe8ff0fb996883593ec89fe65c34a12ee500475633e58c344d5668 +size 5307 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_1_en.png new file mode 100644 index 0000000000..08f9db9903 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f68b17150dce91f4ac010d12e3380fb664addcac0410f5ab1cbcdec24ce3776f +size 10936 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_2_en.png new file mode 100644 index 0000000000..4640944ca8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:437b3abdf98913c571c288ef4fdc5623d327b1b8d20ef27f8551f109e1dacab7 +size 18151 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_3_en.png new file mode 100644 index 0000000000..a88d3f0cf4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_ComposerModeView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4193cdc78f243d5eb4bf25a1bc7dbc8aaf4996e7f900bdb4741b175a26632b2d +size 7645 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png index fc12ee3b7e..522dac8461 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2665294be484f65721617ca1bfb4fb412adafcdacd3bbdaceee842f5f080cf9 -size 41165 +oid sha256:9c1096dd5654c5c6bcd274812f51fd7a278084f63cc2879c4217c3f1bd8fbaaa +size 41102 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png index 5a4b122041..1411021e07 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f00398a7bce27b1fd2c639a1340b551ac0ec1c161230688470233d4be9f5b9f -size 38568 +oid sha256:afc6b93d2e4ce6bcb02c38eccb115e61f0ebd682a7c0b8037e74c20952d8ae2b +size 38429 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png index 431f6e60ff..ec2f06f1f7 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:727d84a024a40ea3524d26e528deb8deaa1b7a91c6d44a040eac724312c7ee5b -size 19358 +oid sha256:0fe4485cd7a3194f9805e8ca3f83146ca4ef84bc8fe7b7d314908c27fc1f55e9 +size 19341 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png index 314072ad7a..9c21394fa3 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f896d0fac1c193a37f18ccf8b1a218364a5c493657fc46ffb8c9d851d1cf7361 -size 22239 +oid sha256:40061abbccaf9416b767383eb7c31aa213490dddae7d5ebf0a43e160fcfaa0d4 +size 22480 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png index a06e3b44ca..f4592dbda9 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4bb1269080c567c03210191e8486f79382a70f14a96133578957f48701a5de0 -size 18439 +oid sha256:2dfd6f04f64d1a6a750c67b10f55f74fa423c884b8788b5e5de520bdaa7b6564 +size 18446 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png index 9a2cafe64e..66aeaddc68 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae50ed7f1afa9d323d54756388a808353d9cf4981cb324099ffdb6129006294c -size 21103 +oid sha256:4af010eaab0dedba3220446203abd237c7f9817df08e8458896e5e6edcef7f87 +size 21295 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Day_0_en.png index 7a935b8dff..6f43f90e61 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20f60dd84cc313d34e4cd37279cd2abadefa8aa7e99319f0e73b24f4f6490a0a -size 45599 +oid sha256:6c9d3fca9aaaa854d0fe1242a9ff043f17258827e1197c92ae251e251670e65b +size 44970 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Night_0_en.png index d22d436019..11897e4584 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0daa64d76ad618f694b8778d86aeda96eb56dbb0ec7117063d6423ef488429b4 -size 43580 +oid sha256:79d62f9d342571992714742ec7db4ca98e4631dc33305ea7d1306c39258328d0 +size 42990 From 8acc4173d3afa2b6b27a52358ca07fbbe2ee397b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 14:18:46 +0200 Subject: [PATCH 41/67] SDK 0.2.54 --- 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 13733a101d..8b43d04069 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -168,7 +168,7 @@ jsoup = "org.jsoup:jsoup:1.18.1" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.53" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.54" matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } From 3f58f4b793c5d8aa4a8d051cc83ad054a0495fbd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 14:39:20 +0200 Subject: [PATCH 42/67] Fix API update regrading OidcPrompt. --- .../ConfirmAccountProviderPresenter.kt | 3 +- .../api/auth/MatrixAuthenticationService.kt | 2 +- .../libraries/matrix/api/auth/OidcPrompt.kt | 51 +++++++++++++++++++ .../libraries/matrix/impl/auth/OidcPrompt.kt | 22 ++++++++ .../auth/RustMatrixAuthenticationService.kt | 8 ++- .../auth/FakeMatrixAuthenticationService.kt | 3 +- 6 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt 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 c15c84744f..63720a613b 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 @@ -27,6 +27,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.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.api.auth.OidcPrompt import io.element.android.libraries.oidc.api.OidcAction import io.element.android.libraries.oidc.api.OidcActionFlow import kotlinx.coroutines.CoroutineScope @@ -92,7 +93,7 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor( val matrixHomeServerDetails = authenticationService.getHomeserverDetails().value!! if (matrixHomeServerDetails.supportsOidcLogin) { // Retrieve the details right now - LoginFlow.OidcFlow(authenticationService.getOidcUrl().getOrThrow()) + LoginFlow.OidcFlow(authenticationService.getOidcUrl(OidcPrompt.Consent).getOrThrow()) } else if (params.isAccountCreation) { val url = webClientUrlForAuthenticationRetriever.retrieve(homeserverUrl) LoginFlow.AccountCreationFlow(url) 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 bb3d396202..b7b44ba45e 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 @@ -43,7 +43,7 @@ interface MatrixAuthenticationService { /** * Get the Oidc url to display to the user. */ - suspend fun getOidcUrl(): Result + suspend fun getOidcUrl(prompt: OidcPrompt): Result /** * Cancel Oidc login sequence. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt new file mode 100644 index 0000000000..2444425f3a --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api.auth + +sealed interface OidcPrompt { + /** + * The Authorization Server must not display any authentication or consent + * user interface pages. + */ + data object None : OidcPrompt + + /** + * The Authorization Server should prompt the End-User for + * reauthentication. + */ + data object Login : OidcPrompt + + /** + * The Authorization Server should prompt the End-User for consent before + * returning information to the Client. + */ + data object Consent : OidcPrompt + + /** + * The Authorization Server should prompt the End-User to select a user + * account. + * + * This enables an End-User who has multiple accounts at the Authorization + * Server to select amongst the multiple accounts that they might have + * current sessions for. + */ + data object SelectAccount : OidcPrompt + + /** + * The Authorization Server should prompt the End-User to create a user + * account. + * + * Defined in [Initiating User Registration via OpenID Connect](https://openid.net/specs/openid-connect-prompt-create-1_0.html). + */ + data object Create : OidcPrompt + + /** + * An unknown value. + */ + data class Unknown(val value: String) : OidcPrompt +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt new file mode 100644 index 0000000000..a5b9e4d28f --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.auth + +import io.element.android.libraries.matrix.api.auth.OidcPrompt +import org.matrix.rustcomponents.sdk.OidcPrompt as RustOidcPrompt + +internal fun OidcPrompt.toRustPrompt(): RustOidcPrompt { + return when (this) { + OidcPrompt.None -> RustOidcPrompt.None + OidcPrompt.Login -> RustOidcPrompt.Login + OidcPrompt.Consent -> RustOidcPrompt.Consent + OidcPrompt.SelectAccount -> RustOidcPrompt.SelectAccount + OidcPrompt.Create -> RustOidcPrompt.Create + is OidcPrompt.Unknown -> RustOidcPrompt.Unknown(value) + } +} 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 47a81e9f65..434b0b2aef 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 @@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OidcPrompt import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep @@ -181,11 +182,14 @@ class RustMatrixAuthenticationService @Inject constructor( private var pendingOidcAuthorizationData: OidcAuthorizationData? = null - override suspend fun getOidcUrl(): Result { + override suspend fun getOidcUrl(prompt: OidcPrompt): Result { return withContext(coroutineDispatchers.io) { runCatching { val client = currentClient ?: error("You need to call `setHomeserver()` first") - val oidcAuthenticationData = client.urlForOidcLogin(oidcConfigurationProvider.get()) + val oidcAuthenticationData = client.urlForOidc( + oidcConfiguration = oidcConfigurationProvider.get(), + prompt = prompt.toRustPrompt(), + ) val url = oidcAuthenticationData.loginUrl() pendingOidcAuthorizationData = oidcAuthenticationData OidcDetails(url) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt index 8c18629817..2846542890 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt @@ -11,6 +11,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OidcPrompt import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep @@ -80,7 +81,7 @@ class FakeMatrixAuthenticationService( return importCreatedSessionLambda(externalSession) } - override suspend fun getOidcUrl(): Result = simulateLongTask { + override suspend fun getOidcUrl(prompt: OidcPrompt): Result = simulateLongTask { oidcError?.let { Result.failure(it) } ?: Result.success(A_OIDC_DATA) } From 2724815d798a8234a827d5927adf082e524eba6e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 14:57:30 +0200 Subject: [PATCH 43/67] Fix other API change: `body` renamed to `filename` --- .../voicemessages/timeline/VoiceMessageMediaRepo.kt | 10 +++++----- .../voicemessages/timeline/VoiceMessagePlayer.kt | 12 ++++++------ .../voicemessages/timeline/VoiceMessagePresenter.kt | 2 +- .../timeline/DefaultVoiceMessageMediaRepoTest.kt | 2 +- .../timeline/DefaultVoiceMessagePlayerTest.kt | 2 +- .../timeline/VoiceMessagePresenterTest.kt | 4 ++-- .../libraries/matrix/api/media/MatrixMediaLoader.kt | 4 ++-- .../libraries/matrix/impl/media/RustMediaLoader.kt | 4 ++-- .../matrix/test/media/FakeMatrixMediaLoader.kt | 2 +- .../mediaviewer/api/viewer/MediaViewerPresenter.kt | 2 +- .../notifications/DefaultNotifiableEventResolver.kt | 2 +- .../push/impl/notifications/NotificationMediaRepo.kt | 8 ++++---- .../notifications/fake/FakeNotificationMediaRepo.kt | 2 +- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt index d6bd4ea301..f1d8e5f987 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt @@ -35,12 +35,12 @@ interface VoiceMessageMediaRepo { * * @param mediaSource the media source of the voice message. * @param mimeType the mime type of the voice message. - * @param body the body of the voice message. + * @param filename the filename of the voice message. */ fun create( mediaSource: MediaSource, mimeType: String?, - body: String?, + filename: String?, ): VoiceMessageMediaRepo } @@ -61,7 +61,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor( private val matrixMediaLoader: MatrixMediaLoader, @Assisted private val mediaSource: MediaSource, @Assisted("mimeType") private val mimeType: String?, - @Assisted("body") private val body: String?, + @Assisted("filename") private val filename: String?, ) : VoiceMessageMediaRepo { @ContributesBinding(RoomScope::class) @AssistedFactory @@ -69,7 +69,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor( override fun create( mediaSource: MediaSource, @Assisted("mimeType") mimeType: String?, - @Assisted("body") body: String?, + @Assisted("filename") filename: String?, ): DefaultVoiceMessageMediaRepo } @@ -79,7 +79,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor( else -> matrixMediaLoader.downloadMediaFile( source = mediaSource, mimeType = mimeType, - body = body, + filename = filename, ).mapCatching { it.use { mediaFile -> val dest = cachedFile.apply { parentFile?.mkdirs() } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt index 42969b9754..aa339e3365 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt @@ -37,13 +37,13 @@ interface VoiceMessagePlayer { * @param eventId The eventId of the voice message event. * @param mediaSource The media source of the voice message. * @param mimeType The mime type of the voice message. - * @param body The body of the voice message. + * @param filename The filename of the voice message. */ fun create( eventId: EventId?, mediaSource: MediaSource, mimeType: String?, - body: String?, + filename: String?, ): VoiceMessagePlayer } @@ -113,7 +113,7 @@ class DefaultVoiceMessagePlayer( private val eventId: EventId?, mediaSource: MediaSource, mimeType: String?, - body: String?, + filename: String?, ) : VoiceMessagePlayer { @ContributesBinding(RoomScope::class) // Scoped types can't use @AssistedInject. class Factory @Inject constructor( @@ -124,21 +124,21 @@ class DefaultVoiceMessagePlayer( eventId: EventId?, mediaSource: MediaSource, mimeType: String?, - body: String?, + filename: String?, ): DefaultVoiceMessagePlayer = DefaultVoiceMessagePlayer( mediaPlayer = mediaPlayer, voiceMessageMediaRepoFactory = voiceMessageMediaRepoFactory, eventId = eventId, mediaSource = mediaSource, mimeType = mimeType, - body = body, + filename = filename, ) } private val repo = voiceMessageMediaRepoFactory.create( mediaSource = mediaSource, mimeType = mimeType, - body = body + filename = filename, ) private var internalState = MutableStateFlow( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt index 155b47728a..8eb21e1528 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt @@ -59,7 +59,7 @@ class VoiceMessagePresenter @AssistedInject constructor( eventId = content.eventId, mediaSource = content.mediaSource, mimeType = content.mimeType, - body = content.caption, + filename = content.caption, ) private val play = mutableStateOf>(AsyncData.Uninitialized) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt index 462535d3c0..fcf1998097 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt @@ -137,7 +137,7 @@ private fun createDefaultVoiceMessageMediaRepo( json = null ), mimeType = MimeTypes.Ogg, - body = "someBody.ogg" + filename = "someBody.ogg" ) private const val MXC_URI = "mxc://matrix.org/1234567890abcdefg" diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt index b5a11ba160..9a82b46776 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt @@ -279,7 +279,7 @@ private fun createDefaultVoiceMessagePlayer( json = null ), mimeType = MimeTypes.Ogg, - body = "someBody.ogg" + filename = "someBody.ogg" ) private const val MXC_URI = "mxc://matrix.org/1234567890abcdefg" diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt index a4770539be..ceedf0948f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenterTest.kt @@ -226,14 +226,14 @@ fun TestScope.createVoiceMessagePresenter( analyticsService: AnalyticsService = FakeAnalyticsService(), content: TimelineItemVoiceContent = aTimelineItemVoiceContent(), ) = VoiceMessagePresenter( - voiceMessagePlayerFactory = { eventId, mediaSource, mimeType, body -> + voiceMessagePlayerFactory = { eventId, mediaSource, mimeType, filename -> DefaultVoiceMessagePlayer( mediaPlayer = mediaPlayer, voiceMessageMediaRepoFactory = { _, _, _ -> voiceMessageMediaRepo }, eventId = eventId, mediaSource = mediaSource, mimeType = mimeType, - body = body + filename = filename ) }, analyticsService = analyticsService, 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 06f6350b64..349e651cf1 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 @@ -25,14 +25,14 @@ interface MatrixMediaLoader { /** * @param source to fetch the data for. * @param mimeType: optional mime type. - * @param body: optional body which will be used to name the file. + * @param filename: optional String which will be used to name the file. * @param useCache: if true, the rust sdk will cache the media in its store. * @return a [Result] of [MediaFile] */ suspend fun downloadMediaFile( source: MediaSource, mimeType: String?, - body: String?, + filename: String?, useCache: Boolean = true, ): Result } 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 eb4d016b6c..9604d6af6d 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 @@ -63,7 +63,7 @@ class RustMediaLoader( override suspend fun downloadMediaFile( source: MediaSource, mimeType: String?, - body: String?, + filename: String?, useCache: Boolean, ): Result = withContext(mediaDispatcher) { @@ -71,7 +71,7 @@ class RustMediaLoader( source.toRustMediaSource().use { mediaSource -> val mediaFile = innerClient.getMediaFile( mediaSource = mediaSource, - body = body, + filename = filename, mimeType = mimeType?.takeIf { MimeTypes.hasSubtype(it) } ?: MimeTypes.OctetStream, useCache = useCache, tempDir = cacheDirectory.path, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMatrixMediaLoader.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMatrixMediaLoader.kt index 68bc3ed764..6a53d8658b 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMatrixMediaLoader.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMatrixMediaLoader.kt @@ -35,7 +35,7 @@ class FakeMatrixMediaLoader : MatrixMediaLoader { override suspend fun downloadMediaFile( source: MediaSource, mimeType: String?, - body: String?, + filename: String?, useCache: Boolean, ): Result = simulateLongTask { if (shouldFail) { diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt index 3d51096dd3..3cef900040 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt @@ -92,7 +92,7 @@ class MediaViewerPresenter @AssistedInject constructor( mediaLoader.downloadMediaFile( source = inputs.mediaSource, mimeType = inputs.mediaInfo.mimeType, - body = inputs.mediaInfo.filename + filename = inputs.mediaInfo.filename ) .onSuccess { mediaFile.value = it 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 1d6e916124..2fb6186bd4 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 @@ -299,7 +299,7 @@ class DefaultNotifiableEventResolver @Inject constructor( .getMediaFile( mediaSource = messageType.source, mimeType = messageType.info?.mimetype, - body = messageType.filename, + filename = messageType.filename, ) is VideoMessageType -> null // Use the thumbnail here? else -> null diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt index f8818cf341..c53bbcd347 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt @@ -47,13 +47,13 @@ interface NotificationMediaRepo { * * @param mediaSource the media source of the media. * @param mimeType the mime type of the media. - * @param body optional body which will be used to name the file. + * @param filename optional String which will be used to name the file. * @return A [Result] holding either the media [File] from the cache directory or an [Exception]. */ suspend fun getMediaFile( mediaSource: MediaSource, mimeType: String?, - body: String?, + filename: String?, ): Result } @@ -75,7 +75,7 @@ class DefaultNotificationMediaRepo @AssistedInject constructor( override suspend fun getMediaFile( mediaSource: MediaSource, mimeType: String?, - body: String?, + filename: String?, ): Result { val cachedFile = mediaSource.cachedFile() return when { @@ -84,7 +84,7 @@ class DefaultNotificationMediaRepo @AssistedInject constructor( else -> matrixMediaLoader.downloadMediaFile( source = mediaSource, mimeType = mimeType, - body = body, + filename = filename, ).mapCatching { it.use { mediaFile -> val dest = cachedFile.apply { parentFile?.mkdirs() } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt index 8ab7bf7d9c..68ce164284 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationMediaRepo.kt @@ -15,7 +15,7 @@ class FakeNotificationMediaRepo : NotificationMediaRepo { override suspend fun getMediaFile( mediaSource: MediaSource, mimeType: String?, - body: String?, + filename: String?, ): Result { return Result.failure(IllegalStateException("Fake class")) } From 5027fb5fe3987237024cdb69d7dc7a4d22454a81 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 15:02:28 +0200 Subject: [PATCH 44/67] Fix other API change: map new value `Membership.KNOCKED` --- .../android/libraries/matrix/api/room/CurrentUserMembership.kt | 3 ++- .../android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/CurrentUserMembership.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/CurrentUserMembership.kt index 1558ea4af5..b1bc3648d7 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/CurrentUserMembership.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/CurrentUserMembership.kt @@ -10,5 +10,6 @@ package io.element.android.libraries.matrix.api.room enum class CurrentUserMembership { INVITED, JOINED, - LEFT + LEFT, + KNOCKED, } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt index c6d60bdca5..607c316b25 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt @@ -19,6 +19,7 @@ import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toPersistentMap +import org.matrix.rustcomponents.sdk.Membership import org.matrix.rustcomponents.sdk.RoomHero import org.matrix.rustcomponents.sdk.Membership as RustMembership import org.matrix.rustcomponents.sdk.RoomInfo as RustRoomInfo @@ -65,6 +66,7 @@ fun RustMembership.map(): CurrentUserMembership = when (this) { RustMembership.INVITED -> CurrentUserMembership.INVITED RustMembership.JOINED -> CurrentUserMembership.JOINED RustMembership.LEFT -> CurrentUserMembership.LEFT + Membership.KNOCKED -> CurrentUserMembership.KNOCKED } fun RustRoomNotificationMode.map(): RoomNotificationMode = when (this) { From 83de862fc11860b7509b85c0cc30058d55afded3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 15:48:14 +0200 Subject: [PATCH 45/67] Fix other API change: `EventShieldsProvider` and `EventTimelineItemDebugInfoProvider` have been replaced by `LazyTimelineItemProvider` --- .../actionlist/ActionListStateProvider.kt | 68 ++++++++++--------- .../impl/timeline/TimelineStateProvider.kt | 7 +- .../TimelineViewMessageShieldPreview.kt | 8 ++- ...melineItemEventForTimestampViewProvider.kt | 4 +- .../event/TimelineItemEventFactory.kt | 3 +- .../impl/timeline/model/TimelineItem.kt | 12 ++-- .../impl/fixtures/MessageEventFixtures.kt | 7 +- .../groups/TimelineItemGrouperTest.kt | 7 +- .../RedactedVoiceMessageManagerTest.kt | 10 +-- .../timeline/item/event/EventTimelineItem.kt | 10 +-- .../item/event/EventTimelineItemMapper.kt | 22 +++--- .../fixtures/factories/EventTimelineItem.kt | 11 ++- .../fakes/FakeRustEventShieldsProvider.kt | 18 ----- ...kt => FakeRustLazyTimelineItemProvider.kt} | 11 +-- .../matrix/test/timeline/TimelineFixture.kt | 7 +- 15 files changed, 105 insertions(+), 100 deletions(-) delete mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventShieldsProvider.kt rename libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/{FakeRustEventTimelineItemDebugInfoProvider.kt => FakeRustLazyTimelineItemProvider.kt} (59%) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt index 4f92bc72ba..78e65c839a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt @@ -31,103 +31,109 @@ open class ActionListStateProvider : PreviewParameterProvider { return sequenceOf( anActionListState(), anActionListState().copy(target = ActionListState.Target.Loading(aTimelineItemEvent())), - anActionListState().copy( + anActionListState( target = ActionListState.Target.Success( - event = aTimelineItemEvent().copy( - reactionsState = reactionsState + event = aTimelineItemEvent( + timelineItemReactions = reactionsState ), displayEmojiReactions = true, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), - anActionListState().copy( + anActionListState( target = ActionListState.Target.Success( event = aTimelineItemEvent( content = aTimelineItemImageContent(), displayNameAmbiguous = true, - ).copy( - reactionsState = reactionsState, + timelineItemReactions = reactionsState, ), displayEmojiReactions = true, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), - anActionListState().copy( + anActionListState( target = ActionListState.Target.Success( - event = aTimelineItemEvent(content = aTimelineItemVideoContent()).copy( - reactionsState = reactionsState + event = aTimelineItemEvent( + content = aTimelineItemVideoContent(), + timelineItemReactions = reactionsState ), displayEmojiReactions = true, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), - anActionListState().copy( + anActionListState( target = ActionListState.Target.Success( - event = aTimelineItemEvent(content = aTimelineItemFileContent()).copy( - reactionsState = reactionsState + event = aTimelineItemEvent( + content = aTimelineItemFileContent(), + timelineItemReactions = reactionsState ), displayEmojiReactions = true, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), - anActionListState().copy( + anActionListState( target = ActionListState.Target.Success( - event = aTimelineItemEvent(content = aTimelineItemAudioContent()).copy( - reactionsState = reactionsState + event = aTimelineItemEvent( + content = aTimelineItemAudioContent(), + timelineItemReactions = reactionsState ), displayEmojiReactions = true, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), - anActionListState().copy( + anActionListState( target = ActionListState.Target.Success( - event = aTimelineItemEvent(content = aTimelineItemVoiceContent()).copy( - reactionsState = reactionsState + event = aTimelineItemEvent( + content = aTimelineItemVoiceContent(), + timelineItemReactions = reactionsState ), displayEmojiReactions = true, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), - anActionListState().copy( + anActionListState( target = ActionListState.Target.Success( - event = aTimelineItemEvent(content = aTimelineItemLocationContent()).copy( - reactionsState = reactionsState + event = aTimelineItemEvent( + content = aTimelineItemLocationContent(), + timelineItemReactions = reactionsState ), displayEmojiReactions = true, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ) ), - anActionListState().copy( + anActionListState( target = ActionListState.Target.Success( - event = aTimelineItemEvent(content = aTimelineItemLocationContent()).copy( - reactionsState = reactionsState + event = aTimelineItemEvent( + content = aTimelineItemLocationContent(), + timelineItemReactions = reactionsState ), displayEmojiReactions = false, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), ), ), - anActionListState().copy( + anActionListState( target = ActionListState.Target.Success( - event = aTimelineItemEvent(content = aTimelineItemPollContent()).copy( - reactionsState = reactionsState + event = aTimelineItemEvent( + content = aTimelineItemPollContent(), + timelineItemReactions = reactionsState ), displayEmojiReactions = false, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemPollActionList(), ), ), - anActionListState().copy( + anActionListState( target = ActionListState.Target.Success( - event = aTimelineItemEvent().copy( - reactionsState = reactionsState, + event = aTimelineItemEvent( + timelineItemReactions = reactionsState, messageShield = MessageShield.UnknownDevice(isCritical = true) ), displayEmojiReactions = true, @@ -135,7 +141,7 @@ open class ActionListStateProvider : PreviewParameterProvider { actions = aTimelineItemActionList(), ) ), - anActionListState().copy( + anActionListState( target = ActionListState.Target.Success( event = aTimelineItemEvent(), displayEmojiReactions = true, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index fb96e75e28..cecc528cee 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails @@ -164,10 +165,12 @@ internal fun aTimelineItemEvent( groupPosition = groupPosition, localSendState = sendState, inReplyTo = inReplyTo, - debugInfoProvider = { debugInfo }, isThreaded = isThreaded, origin = null, - messageShield = messageShield, + lazyTimelineItemProvider = object : LazyTimelineItemProvider { + override fun getTimelineItemDebugInfo(): TimelineItemDebugInfo = debugInfo + override fun getShield(strict: Boolean): MessageShield? = messageShield + } ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt index 806a81b7fe..ddbc98c638 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt @@ -17,6 +17,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import kotlinx.collections.immutable.toImmutableList @PreviewsDayNight @@ -26,7 +27,12 @@ internal fun TimelineViewMessageShieldPreview() = ElementPreview { // For consistency, ensure that there is a message in the timeline (the last one) with an error. val messageShield = aCriticalShield() val items = listOf( - (timelineItems.first() as TimelineItem.Event).copy(messageShield = messageShield) + (timelineItems.first() as TimelineItem.Event).copy( + lazyTimelineItemProvider = object : LazyTimelineItemProvider { + override fun getTimelineItemDebugInfo() = aTimelineItemDebugInfo() + override fun getShield(strict: Boolean) = messageShield + } + ) ) + timelineItems.drop(1) CompositionLocalProvider( LocalTimelineItemPresenterFactories provides aFakeTimelineItemPresenterFactories(), 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/TimelineItemEventForTimestampViewProvider.kt index 3fe24fbef0..df0dfb41bf 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/TimelineItemEventForTimestampViewProvider.kt @@ -27,10 +27,10 @@ class TimelineItemEventForTimestampViewProvider : PreviewParameterProvider().toImmutableList()), localSendState = sendState, inReplyTo = inReplyTo, - debugInfoProvider = { debugInfo }, isThreaded = isThreaded, origin = null, - messageShield = messageShield, + lazyTimelineItemProvider = object : LazyTimelineItemProvider { + override fun getTimelineItemDebugInfo() = debugInfo + override fun getShield(strict: Boolean) = messageShield + }, ) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt index 2e37145ff8..2b76ab6a90 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt @@ -18,6 +18,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.matrix.api.core.UniqueId +import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_USER_ID @@ -41,9 +42,11 @@ class TimelineItemGrouperTest { canBeRepliedTo = false, inReplyTo = null, isThreaded = false, - debugInfoProvider = { aTimelineItemDebugInfo() }, origin = null, - messageShield = null, + lazyTimelineItemProvider = object : LazyTimelineItemProvider { + override fun getTimelineItemDebugInfo() = aTimelineItemDebugInfo() + override fun getShield(strict: Boolean) = null + }, ) private val aNonGroupableItem = aMessageEvent() private val aNonGroupableItemNoEvent = TimelineItem.Virtual(UniqueId("virtual"), aTimelineItemDaySeparatorModel("Today")) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt index b9b08ab6d4..d778b488c9 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt @@ -14,6 +14,7 @@ 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.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -88,15 +89,16 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf( senderProfile = ProfileTimelineDetails.Unavailable, timestamp = 9442, content = RedactedContent, - debugInfoProvider = { - TimelineItemDebugInfo( + origin = null, + lazyTimelineItemProvider = object : LazyTimelineItemProvider { + override fun getTimelineItemDebugInfo() = TimelineItemDebugInfo( model = "enim", originalJson = null, latestEditedJson = null ) + + override fun getShield(strict: Boolean) = null }, - origin = null, - messageShieldProvider = { null }, ), ) ) 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 b311b4e6bf..7b7d876bb7 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 @@ -28,9 +28,8 @@ data class EventTimelineItem( val senderProfile: ProfileTimelineDetails, val timestamp: Long, val content: EventContent, - val debugInfoProvider: EventDebugInfoProvider, val origin: TimelineItemEventOrigin?, - val messageShieldProvider: EventShieldsProvider, + val lazyTimelineItemProvider: LazyTimelineItemProvider, ) { fun inReplyTo(): InReplyTo? { return (content as? MessageContent)?.inReplyTo @@ -46,10 +45,7 @@ data class EventTimelineItem( } } -fun interface EventDebugInfoProvider { - fun get(): TimelineItemDebugInfo -} - -fun interface EventShieldsProvider { +interface LazyTimelineItemProvider { + fun getTimelineItemDebugInfo(): TimelineItemDebugInfo fun getShield(strict: Boolean): MessageShield? } 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 1291c9e6b8..6be638faae 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 @@ -12,10 +12,9 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo -import io.element.android.libraries.matrix.api.timeline.item.event.EventDebugInfoProvider import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction -import io.element.android.libraries.matrix.api.timeline.item.event.EventShieldsProvider import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails @@ -27,14 +26,13 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import org.matrix.rustcomponents.sdk.EventOrTransactionId import org.matrix.rustcomponents.sdk.EventSendState -import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfoProvider import org.matrix.rustcomponents.sdk.Reaction import org.matrix.rustcomponents.sdk.ShieldState import uniffi.matrix_sdk_common.ShieldStateCode import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState -import org.matrix.rustcomponents.sdk.EventShieldsProvider as RustEventShieldsProvider import org.matrix.rustcomponents.sdk.EventTimelineItem as RustEventTimelineItem import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo as RustEventTimelineItemDebugInfo +import org.matrix.rustcomponents.sdk.LazyTimelineItemProvider as RustLazyTimelineItemProvider import org.matrix.rustcomponents.sdk.ProfileDetails as RustProfileDetails import org.matrix.rustcomponents.sdk.Receipt as RustReceipt import uniffi.matrix_sdk_ui.EventItemOrigin as RustEventItemOrigin @@ -48,7 +46,8 @@ class EventTimelineItemMapper( transactionId = eventOrTransactionId.transactionId(), isEditable = isEditable, canBeRepliedTo = canBeRepliedTo, - isLocal = isLocal, + // TODO Remove this field + isLocal = !isRemote, isOwn = isOwn, isRemote = isRemote, localSendState = localSendState?.map(), @@ -58,9 +57,8 @@ class EventTimelineItemMapper( senderProfile = senderProfile.map(), timestamp = timestamp.toLong(), content = contentMapper.map(content), - debugInfoProvider = RustEventDebugInfoProvider(debugInfoProvider), origin = origin?.map(), - messageShieldProvider = RustEventShieldsProvider(shieldsProvider) + lazyTimelineItemProvider = LazyTimelineItemProviderWrapper(lazyProvider) ) } } @@ -168,15 +166,13 @@ private fun ShieldState?.map(): MessageShield? { } } -class RustEventDebugInfoProvider(private val debugInfoProvider: EventTimelineItemDebugInfoProvider) : EventDebugInfoProvider { - override fun get(): TimelineItemDebugInfo { - return debugInfoProvider.get().map() +class LazyTimelineItemProviderWrapper(private val provider: RustLazyTimelineItemProvider) : LazyTimelineItemProvider { + override fun getTimelineItemDebugInfo(): TimelineItemDebugInfo { + return provider.debugInfo().map() } -} -class RustEventShieldsProvider(private val shieldsProvider: RustEventShieldsProvider) : EventShieldsProvider { override fun getShield(strict: Boolean): MessageShield? { - return shieldsProvider.getShields(strict)?.map() + return provider.getShields(strict)?.map() } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/EventTimelineItem.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/EventTimelineItem.kt index 1b8c7152ff..42e710efc2 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/EventTimelineItem.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/EventTimelineItem.kt @@ -7,8 +7,7 @@ package io.element.android.libraries.matrix.impl.fixtures.factories -import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustEventShieldsProvider -import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustEventTimelineItemDebugInfoProvider +import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustLazyTimelineItemProvider import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_USER_ID import org.matrix.rustcomponents.sdk.EventOrTransactionId @@ -23,7 +22,6 @@ import org.matrix.rustcomponents.sdk.TimelineItemContent import uniffi.matrix_sdk_ui.EventItemOrigin fun aRustEventTimelineItem( - isLocal: Boolean = false, isRemote: Boolean = true, eventOrTransactionId: EventOrTransactionId = EventOrTransactionId.EventId(AN_EVENT_ID.value), sender: String = A_USER_ID.value, @@ -40,7 +38,6 @@ fun aRustEventTimelineItem( canBeRepliedTo: Boolean = true, shieldsState: ShieldState? = null, ) = EventTimelineItem( - isLocal = isLocal, isRemote = isRemote, eventOrTransactionId = eventOrTransactionId, sender = sender, @@ -50,10 +47,12 @@ fun aRustEventTimelineItem( isEditable = isEditable, canBeRepliedTo = canBeRepliedTo, content = content, - debugInfoProvider = FakeRustEventTimelineItemDebugInfoProvider(debugInfo), - shieldsProvider = FakeRustEventShieldsProvider(shieldsState), localSendState = localSendState, reactions = reactions, readReceipts = readReceipts, origin = origin, + lazyProvider = FakeRustLazyTimelineItemProvider( + debugInfo = debugInfo, + shieldsState = shieldsState, + ) ) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventShieldsProvider.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventShieldsProvider.kt deleted file mode 100644 index 93f1e5162b..0000000000 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventShieldsProvider.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only - * Please see LICENSE in the repository root for full details. - */ - -package io.element.android.libraries.matrix.impl.fixtures.fakes - -import org.matrix.rustcomponents.sdk.EventShieldsProvider -import org.matrix.rustcomponents.sdk.NoPointer -import org.matrix.rustcomponents.sdk.ShieldState - -class FakeRustEventShieldsProvider( - private val shieldsState: ShieldState? = null, -) : EventShieldsProvider(NoPointer) { - override fun getShields(strict: Boolean): ShieldState? = shieldsState -} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventTimelineItemDebugInfoProvider.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustLazyTimelineItemProvider.kt similarity index 59% rename from libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventTimelineItemDebugInfoProvider.kt rename to libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustLazyTimelineItemProvider.kt index 93ac66318b..b133ddd0b1 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustEventTimelineItemDebugInfoProvider.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustLazyTimelineItemProvider.kt @@ -9,11 +9,14 @@ package io.element.android.libraries.matrix.impl.fixtures.fakes import io.element.android.libraries.matrix.impl.fixtures.factories.anEventTimelineItemDebugInfo import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo -import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfoProvider +import org.matrix.rustcomponents.sdk.LazyTimelineItemProvider import org.matrix.rustcomponents.sdk.NoPointer +import org.matrix.rustcomponents.sdk.ShieldState -class FakeRustEventTimelineItemDebugInfoProvider( +class FakeRustLazyTimelineItemProvider( private val debugInfo: EventTimelineItemDebugInfo = anEventTimelineItemDebugInfo(), -) : EventTimelineItemDebugInfoProvider(NoPointer) { - override fun get(): EventTimelineItemDebugInfo = debugInfo + private val shieldsState: ShieldState? = null, +) : LazyTimelineItemProvider(NoPointer) { + override fun getShields(strict: Boolean) = shieldsState + override fun debugInfo() = debugInfo } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt index 85833f183a..459987a983 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt @@ -19,6 +19,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventContent import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo +import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield @@ -69,9 +70,11 @@ fun anEventTimelineItem( senderProfile = senderProfile, timestamp = timestamp, content = content, - debugInfoProvider = { debugInfo }, origin = null, - messageShieldProvider = { messageShield }, + lazyTimelineItemProvider = object : LazyTimelineItemProvider { + override fun getTimelineItemDebugInfo() = debugInfo + override fun getShield(strict: Boolean) = messageShield + }, ) fun aProfileTimelineDetails( From 58f0cbc5cf08c0f7b5f3cb0cfda60dc6e1f9097a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 16:01:59 +0200 Subject: [PATCH 46/67] Fix wrong mapping --- .../impl/voicemessages/timeline/VoiceMessagePresenter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt index 8eb21e1528..038de6730d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt @@ -59,7 +59,7 @@ class VoiceMessagePresenter @AssistedInject constructor( eventId = content.eventId, mediaSource = content.mediaSource, mimeType = content.mimeType, - filename = content.caption, + filename = content.filename, ) private val play = mutableStateOf>(AsyncData.Uninitialized) From 383cfd323a5424dd8c9c056d74c6b177865d66e2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 14:42:30 +0200 Subject: [PATCH 47/67] Use Create prompt when user already said they wanted to create an account #3627. --- .../confirmaccountprovider/ConfirmAccountProviderPresenter.kt | 3 ++- 1 file changed, 2 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 63720a613b..6cc4cdc5e4 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 @@ -93,7 +93,8 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor( val matrixHomeServerDetails = authenticationService.getHomeserverDetails().value!! if (matrixHomeServerDetails.supportsOidcLogin) { // Retrieve the details right now - LoginFlow.OidcFlow(authenticationService.getOidcUrl(OidcPrompt.Consent).getOrThrow()) + val oidcPrompt = if (params.isAccountCreation) OidcPrompt.Create else OidcPrompt.Consent + LoginFlow.OidcFlow(authenticationService.getOidcUrl(oidcPrompt).getOrThrow()) } else if (params.isAccountCreation) { val url = webClientUrlForAuthenticationRetriever.retrieve(homeserverUrl) LoginFlow.AccountCreationFlow(url) From 62f6791865563bfc22c2f80e655140ef85d73dd6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 16:19:04 +0200 Subject: [PATCH 48/67] Remove unused isLocal field. --- .../voicemessages/timeline/RedactedVoiceMessageManagerTest.kt | 1 - .../matrix/api/timeline/item/event/EventTimelineItem.kt | 1 - .../matrix/impl/timeline/item/event/EventTimelineItemMapper.kt | 2 -- .../android/libraries/matrix/test/timeline/TimelineFixture.kt | 2 -- 4 files changed, 6 deletions(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt index d778b488c9..284254045a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt @@ -79,7 +79,6 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf( transactionId = null, isEditable = false, canBeRepliedTo = false, - isLocal = false, isOwn = false, isRemote = false, localSendState = null, 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 7b7d876bb7..e9c27cf022 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 @@ -18,7 +18,6 @@ data class EventTimelineItem( val transactionId: TransactionId?, val isEditable: Boolean, val canBeRepliedTo: Boolean, - val isLocal: Boolean, val isOwn: Boolean, val isRemote: Boolean, val localSendState: LocalEventSendState?, 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 6be638faae..c423d976dd 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 @@ -46,8 +46,6 @@ class EventTimelineItemMapper( transactionId = eventOrTransactionId.transactionId(), isEditable = isEditable, canBeRepliedTo = canBeRepliedTo, - // TODO Remove this field - isLocal = !isRemote, isOwn = isOwn, isRemote = isRemote, localSendState = localSendState?.map(), diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt index 459987a983..09c3d52969 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt @@ -43,7 +43,6 @@ fun anEventTimelineItem( transactionId: TransactionId? = null, isEditable: Boolean = false, canBeRepliedTo: Boolean = false, - isLocal: Boolean = false, isOwn: Boolean = false, isRemote: Boolean = false, localSendState: LocalEventSendState? = null, @@ -60,7 +59,6 @@ fun anEventTimelineItem( transactionId = transactionId, isEditable = isEditable, canBeRepliedTo = canBeRepliedTo, - isLocal = isLocal, isOwn = isOwn, isRemote = isRemote, localSendState = localSendState, From d6407d699562e1f4ecdbab3c6da31e137a1d4d7d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Oct 2024 17:12:46 +0200 Subject: [PATCH 49/67] Restore fun interface for a sweeter code. --- .../impl/timeline/TimelineStateProvider.kt | 7 ++----- .../timeline/TimelineViewMessageShieldPreview.kt | 6 +----- .../factories/event/TimelineItemEventFactory.kt | 3 ++- .../messages/impl/timeline/model/TimelineItem.kt | 10 ++++++---- .../impl/fixtures/MessageEventFixtures.kt | 7 ++----- .../timeline/groups/TimelineItemGrouperTest.kt | 7 ++----- .../timeline/RedactedVoiceMessageManagerTest.kt | 10 ++++------ .../api/timeline/item/event/EventTimelineItem.kt | 12 ++++++++---- .../item/event/EventTimelineItemMapper.kt | 15 ++------------- .../matrix/test/timeline/TimelineFixture.kt | 7 ++----- 10 files changed, 31 insertions(+), 53 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index cecc528cee..6c790d7b1d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -30,7 +30,6 @@ import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo -import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails @@ -167,10 +166,8 @@ internal fun aTimelineItemEvent( inReplyTo = inReplyTo, isThreaded = isThreaded, origin = null, - lazyTimelineItemProvider = object : LazyTimelineItemProvider { - override fun getTimelineItemDebugInfo(): TimelineItemDebugInfo = debugInfo - override fun getShield(strict: Boolean): MessageShield? = messageShield - } + timelineItemDebugInfoProvider = { debugInfo }, + messageShieldProvider = { messageShield }, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt index ddbc98c638..99f69675fa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt @@ -17,7 +17,6 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import kotlinx.collections.immutable.toImmutableList @PreviewsDayNight @@ -28,10 +27,7 @@ internal fun TimelineViewMessageShieldPreview() = ElementPreview { val messageShield = aCriticalShield() val items = listOf( (timelineItems.first() as TimelineItem.Event).copy( - lazyTimelineItemProvider = object : LazyTimelineItemProvider { - override fun getTimelineItemDebugInfo() = aTimelineItemDebugInfo() - override fun getShield(strict: Boolean) = messageShield - } + messageShieldProvider = { messageShield }, ) ) + timelineItems.drop(1) CompositionLocalProvider( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index d276dbd5d5..21004fb4ab 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -86,7 +86,8 @@ class TimelineItemEventFactory @AssistedInject constructor( inReplyTo = currentTimelineItem.event.inReplyTo()?.map(permalinkParser = permalinkParser), isThreaded = currentTimelineItem.event.isThreaded(), origin = currentTimelineItem.event.origin, - lazyTimelineItemProvider = currentTimelineItem.event.lazyTimelineItemProvider, + timelineItemDebugInfoProvider = currentTimelineItem.event.timelineItemDebugInfoProvider, + messageShieldProvider = currentTimelineItem.event.messageShieldProvider, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt index 7748f46c06..d13fd992bf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt @@ -18,10 +18,11 @@ import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo -import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield +import io.element.android.libraries.matrix.api.timeline.item.event.MessageShieldProvider import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails +import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemDebugInfoProvider import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails @@ -76,7 +77,8 @@ sealed interface TimelineItem { val inReplyTo: InReplyToDetails?, val isThreaded: Boolean, val origin: TimelineItemEventOrigin?, - val lazyTimelineItemProvider: LazyTimelineItemProvider, + val timelineItemDebugInfoProvider: TimelineItemDebugInfoProvider, + val messageShieldProvider: MessageShieldProvider, ) : TimelineItem { val showSenderInformation = groupPosition.isNew() && !isMine @@ -91,10 +93,10 @@ sealed interface TimelineItem { val isRemote = eventId != null // No need to be lazy here? - val messageShield: MessageShield? = lazyTimelineItemProvider.getShield(strict = false) + val messageShield: MessageShield? = messageShieldProvider(strict = false) val debugInfo: TimelineItemDebugInfo - get() = lazyTimelineItemProvider.getTimelineItemDebugInfo() + get() = timelineItemDebugInfoProvider() } @Immutable diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MessageEventFixtures.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MessageEventFixtures.kt index 7f197f93f1..3a4d343203 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MessageEventFixtures.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MessageEventFixtures.kt @@ -20,7 +20,6 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo -import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -61,8 +60,6 @@ internal fun aMessageEvent( inReplyTo = inReplyTo, isThreaded = isThreaded, origin = null, - lazyTimelineItemProvider = object : LazyTimelineItemProvider { - override fun getTimelineItemDebugInfo() = debugInfo - override fun getShield(strict: Boolean) = messageShield - }, + timelineItemDebugInfoProvider = { debugInfo }, + messageShieldProvider = { messageShield }, ) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt index 2b76ab6a90..a5a806fe5b 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt @@ -18,7 +18,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.matrix.api.core.UniqueId -import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_USER_ID @@ -43,10 +42,8 @@ class TimelineItemGrouperTest { inReplyTo = null, isThreaded = false, origin = null, - lazyTimelineItemProvider = object : LazyTimelineItemProvider { - override fun getTimelineItemDebugInfo() = aTimelineItemDebugInfo() - override fun getShield(strict: Boolean) = null - }, + timelineItemDebugInfoProvider = { aTimelineItemDebugInfo() }, + messageShieldProvider = { null }, ) private val aNonGroupableItem = aMessageEvent() private val aNonGroupableItemNoEvent = TimelineItem.Virtual(UniqueId("virtual"), aTimelineItemDaySeparatorModel("Today")) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt index 284254045a..a980467add 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt @@ -14,7 +14,6 @@ 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.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem -import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -89,15 +88,14 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf( timestamp = 9442, content = RedactedContent, origin = null, - lazyTimelineItemProvider = object : LazyTimelineItemProvider { - override fun getTimelineItemDebugInfo() = TimelineItemDebugInfo( + timelineItemDebugInfoProvider = { + TimelineItemDebugInfo( model = "enim", originalJson = null, - latestEditedJson = null + latestEditedJson = null, ) - - override fun getShield(strict: Boolean) = null }, + messageShieldProvider = { null }, ), ) ) 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 e9c27cf022..eb8923d1d6 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 @@ -28,7 +28,8 @@ data class EventTimelineItem( val timestamp: Long, val content: EventContent, val origin: TimelineItemEventOrigin?, - val lazyTimelineItemProvider: LazyTimelineItemProvider, + val timelineItemDebugInfoProvider: TimelineItemDebugInfoProvider, + val messageShieldProvider: MessageShieldProvider, ) { fun inReplyTo(): InReplyTo? { return (content as? MessageContent)?.inReplyTo @@ -44,7 +45,10 @@ data class EventTimelineItem( } } -interface LazyTimelineItemProvider { - fun getTimelineItemDebugInfo(): TimelineItemDebugInfo - fun getShield(strict: Boolean): MessageShield? +fun interface TimelineItemDebugInfoProvider { + operator fun invoke(): TimelineItemDebugInfo +} + +fun interface MessageShieldProvider { + operator fun invoke(strict: Boolean): MessageShield? } 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 c423d976dd..95b11e10c1 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 @@ -14,7 +14,6 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem -import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails @@ -32,7 +31,6 @@ import uniffi.matrix_sdk_common.ShieldStateCode import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState import org.matrix.rustcomponents.sdk.EventTimelineItem as RustEventTimelineItem import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo as RustEventTimelineItemDebugInfo -import org.matrix.rustcomponents.sdk.LazyTimelineItemProvider as RustLazyTimelineItemProvider import org.matrix.rustcomponents.sdk.ProfileDetails as RustProfileDetails import org.matrix.rustcomponents.sdk.Receipt as RustReceipt import uniffi.matrix_sdk_ui.EventItemOrigin as RustEventItemOrigin @@ -56,7 +54,8 @@ class EventTimelineItemMapper( timestamp = timestamp.toLong(), content = contentMapper.map(content), origin = origin?.map(), - lazyTimelineItemProvider = LazyTimelineItemProviderWrapper(lazyProvider) + timelineItemDebugInfoProvider = { lazyProvider.debugInfo().map() }, + messageShieldProvider = { strict -> lazyProvider.getShields(strict)?.map() }, ) } } @@ -164,16 +163,6 @@ private fun ShieldState?.map(): MessageShield? { } } -class LazyTimelineItemProviderWrapper(private val provider: RustLazyTimelineItemProvider) : LazyTimelineItemProvider { - override fun getTimelineItemDebugInfo(): TimelineItemDebugInfo { - return provider.debugInfo().map() - } - - override fun getShield(strict: Boolean): MessageShield? { - return provider.getShields(strict)?.map() - } -} - private fun EventOrTransactionId.eventId(): EventId? { return (this as? EventOrTransactionId.EventId)?.let { EventId(it.eventId) } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt index 09c3d52969..e1c6d9829e 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt @@ -19,7 +19,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventContent import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo -import io.element.android.libraries.matrix.api.timeline.item.event.LazyTimelineItemProvider import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield @@ -69,10 +68,8 @@ fun anEventTimelineItem( timestamp = timestamp, content = content, origin = null, - lazyTimelineItemProvider = object : LazyTimelineItemProvider { - override fun getTimelineItemDebugInfo() = debugInfo - override fun getShield(strict: Boolean) = messageShield - }, + timelineItemDebugInfoProvider = { debugInfo }, + messageShieldProvider = { messageShield }, ) fun aProfileTimelineDetails( From 26c0205112479f88f75204c16c24a83affcd31ca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 15:58:44 +0000 Subject: [PATCH 50/67] Update dependency app.cash.turbine:turbine to v1.2.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 13733a101d..309f204954 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -146,7 +146,7 @@ test_junit = "junit:junit:4.13.2" test_runner = "androidx.test:runner:1.6.2" test_mockk = "io.mockk:mockk:1.13.13" test_konsist = "com.lemonappdev:konsist:0.16.1" -test_turbine = "app.cash.turbine:turbine:1.1.0" +test_turbine = "app.cash.turbine:turbine:1.2.0" test_truth = "com.google.truth:truth:1.4.4" test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.18" test_robolectric = "org.robolectric:robolectric:4.13" From 9a13fe52b1be32f1b04731ff950b0c19e8d3e6df Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 22:32:22 +0000 Subject: [PATCH 51/67] Update dependency androidx.compose:compose-bom to v2024.10.00 --- 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 309f204954..6a8dc38bde 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,7 +25,7 @@ media3 = "1.4.1" camera = "1.3.4" # Compose -compose_bom = "2024.09.03" +compose_bom = "2024.10.00" composecompiler = "1.5.15" # Coroutines From 2d16a4db6aaffc878fe4c4531d4f3029ddad50ed Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 08:05:32 +0000 Subject: [PATCH 52/67] Update activity to v1.9.3 (#3697) 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 9a522467a3..78d2c9f927 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,7 +20,7 @@ datastore = "1.0.0" constraintlayout = "2.1.4" constraintlayout_compose = "1.0.1" lifecycle = "2.8.6" -activity = "1.9.2" +activity = "1.9.3" media3 = "1.4.1" camera = "1.3.4" From 9b9fef1aa8071da95208f29b8b16ab928d0b2552 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 17 Oct 2024 12:21:15 +0200 Subject: [PATCH 53/67] Rework: use @AssistedFactory and comment out the UserProfileModule. --- .../userprofile/impl/di/UserProfileModule.kt | 33 ------------------- .../impl/root/UserProfilePresenter.kt | 2 ++ 2 files changed, 2 insertions(+), 33 deletions(-) delete mode 100644 features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/di/UserProfileModule.kt diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/di/UserProfileModule.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/di/UserProfileModule.kt deleted file mode 100644 index ae30b10175..0000000000 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/di/UserProfileModule.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only - * Please see LICENSE in the repository root for full details. - */ - -package io.element.android.features.userprofile.impl.di - -import com.squareup.anvil.annotations.ContributesTo -import dagger.Module -import dagger.Provides -import io.element.android.features.createroom.api.StartDMAction -import io.element.android.features.userprofile.impl.root.UserProfilePresenter -import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.core.UserId - -@Module -@ContributesTo(SessionScope::class) -object UserProfileModule { - @Provides - fun provideUserProfilePresenterFactory( - matrixClient: MatrixClient, - startDMAction: StartDMAction, - ): UserProfilePresenter.Factory { - return object : UserProfilePresenter.Factory { - override fun create(userId: UserId): UserProfilePresenter { - return UserProfilePresenter(userId, matrixClient, startDMAction) - } - } - } -} diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt index 136ffcb206..fb696c3257 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt @@ -16,6 +16,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.userprofile.shared.UserProfileEvents @@ -41,6 +42,7 @@ class UserProfilePresenter @AssistedInject constructor( private val client: MatrixClient, private val startDMAction: StartDMAction, ) : Presenter { + @AssistedFactory interface Factory { fun create(userId: UserId): UserProfilePresenter } From 9b82c6df1cb19e1a30d865fb809ee95afb076b41 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 17 Oct 2024 13:10:59 +0200 Subject: [PATCH 54/67] Let RoomMemberDetailsPresenter use UserProfilePresenter to reduce code duplication. --- .../roomdetails/impl/RoomDetailsState.kt | 2 +- .../impl/RoomDetailsStateProvider.kt | 2 +- .../roomdetails/impl/di/RoomMemberModule.kt | 8 +- .../details/RoomMemberDetailsPresenter.kt | 131 ++------ .../roomdetails/RoomDetailsPresenterTest.kt | 10 +- .../details/RoomMemberDetailsPresenterTest.kt | 279 ++++-------------- .../userprofile/api}/UserProfileEvents.kt | 2 +- .../userprofile/api}/UserProfileState.kt | 2 +- .../api/UserProfileStatePresenterFactory.kt | 15 + ...DefaultUserProfileStatePresenterFactory.kt | 24 ++ .../impl/root/UserProfilePresenter.kt | 9 +- .../impl/UserProfilePresenterTest.kt | 4 +- .../shared/UserProfileStateProvider.kt | 2 + .../userprofile/shared/UserProfileView.kt | 2 + .../shared/blockuser/BlockUserDialogs.kt | 4 +- .../shared/blockuser/BlockUserSection.kt | 17 +- .../userprofile/UserProfileViewTest.kt | 4 +- .../shared/blockuser/BlockUserDialogsTest.kt | 4 +- 18 files changed, 153 insertions(+), 368 deletions(-) rename features/userprofile/{shared/src/main/kotlin/io/element/android/features/userprofile/shared => api/src/main/kotlin/io/element/android/features/userprofile/api}/UserProfileEvents.kt (91%) rename features/userprofile/{shared/src/main/kotlin/io/element/android/features/userprofile/shared => api/src/main/kotlin/io/element/android/features/userprofile/api}/UserProfileState.kt (93%) create mode 100644 features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileStatePresenterFactory.kt create mode 100644 features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileStatePresenterFactory.kt diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt index f33b647a13..1abaa59d93 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt @@ -9,7 +9,7 @@ package io.element.android.features.roomdetails.impl import androidx.compose.runtime.Immutable import io.element.android.features.leaveroom.api.LeaveRoomState -import io.element.android.features.userprofile.shared.UserProfileState +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomMember diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt index 3746407fbf..d9b0c22c65 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.aLeaveRoomState import io.element.android.features.roomdetails.impl.members.aRoomMember -import io.element.android.features.userprofile.shared.UserProfileState +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt index cf59f0db1c..46406f3751 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt @@ -10,10 +10,9 @@ package io.element.android.features.roomdetails.impl.di import com.squareup.anvil.annotations.ContributesTo import dagger.Module import dagger.Provides -import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter +import io.element.android.features.userprofile.api.UserProfileStatePresenterFactory import io.element.android.libraries.di.RoomScope -import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom @@ -22,13 +21,12 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom object RoomMemberModule { @Provides fun provideRoomMemberDetailsPresenterFactory( - matrixClient: MatrixClient, room: MatrixRoom, - startDMAction: StartDMAction, + userProfileStatePresenterFactory: UserProfileStatePresenterFactory, ): RoomMemberDetailsPresenter.Factory { return object : RoomMemberDetailsPresenter.Factory { override fun create(roomMemberId: UserId): RoomMemberDetailsPresenter { - return RoomMemberDetailsPresenter(roomMemberId, matrixClient, room, startDMAction) + return RoomMemberDetailsPresenter(roomMemberId, room, userProfileStatePresenterFactory) } } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt index 768c717e73..fce5de5e21 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt @@ -9,153 +9,60 @@ package io.element.android.features.roomdetails.impl.members.details 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.produceState -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedInject -import io.element.android.features.createroom.api.StartDMAction -import io.element.android.features.userprofile.shared.UserProfileEvents -import io.element.android.features.userprofile.shared.UserProfilePresenterHelper -import io.element.android.features.userprofile.shared.UserProfileState -import io.element.android.features.userprofile.shared.UserProfileState.ConfirmationDialog -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.architecture.AsyncData +import io.element.android.features.userprofile.api.UserProfileState +import io.element.android.features.userprofile.api.UserProfileStatePresenterFactory import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.core.bool.orFalse -import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +/** + * Presenter for room member details screen. + * Rely on UserProfileStatePresenter, but override some fields with room member info when available. + */ class RoomMemberDetailsPresenter @AssistedInject constructor( @Assisted private val roomMemberId: UserId, - private val client: MatrixClient, private val room: MatrixRoom, - private val startDMAction: StartDMAction, + userProfileStatePresenterFactory: UserProfileStatePresenterFactory, ) : Presenter { interface Factory { fun create(roomMemberId: UserId): RoomMemberDetailsPresenter } - private val userProfilePresenterHelper = UserProfilePresenterHelper( - userId = roomMemberId, - client = client, - ) + private val userProfilePresenter = userProfileStatePresenterFactory.create(roomMemberId) @Composable override fun present(): UserProfileState { - val coroutineScope = rememberCoroutineScope() - var confirmationDialog by remember { mutableStateOf(null) } val roomMember by room.getRoomMemberAsState(roomMemberId) - var userProfile by remember { mutableStateOf(null) } - val startDmActionState: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - val isBlocked: MutableState> = remember { mutableStateOf(AsyncData.Uninitialized) } - val isCurrentUser = remember { client.isMe(roomMemberId) } - val dmRoomId by userProfilePresenterHelper.getDmRoomId() - val canCall by userProfilePresenterHelper.getCanCall(dmRoomId) - LaunchedEffect(Unit) { - client.ignoredUsersFlow - .map { ignoredUsers -> roomMemberId in ignoredUsers } - .distinctUntilChanged() - .onEach { isBlocked.value = AsyncData.Success(it) } - .launchIn(this) - } LaunchedEffect(Unit) { // Update room member info when opening this screen // We don't need to assign the result as it will be automatically propagated by `room.getRoomMemberAsState` room.getUpdatedMember(roomMemberId) - .onFailure { - // Not a member of the room, try to get the user profile - userProfile = client.getProfile(roomMemberId).getOrNull() - } } - fun handleEvents(event: UserProfileEvents) { - when (event) { - is UserProfileEvents.BlockUser -> { - if (event.needsConfirmation) { - confirmationDialog = ConfirmationDialog.Block - } else { - confirmationDialog = null - userProfilePresenterHelper.blockUser(coroutineScope, isBlocked) - } - } - is UserProfileEvents.UnblockUser -> { - if (event.needsConfirmation) { - confirmationDialog = ConfirmationDialog.Unblock - } else { - confirmationDialog = null - userProfilePresenterHelper.unblockUser(coroutineScope, isBlocked) - } - } - UserProfileEvents.ClearConfirmationDialog -> confirmationDialog = null - UserProfileEvents.ClearBlockUserError -> { - isBlocked.value = AsyncData.Success(isBlocked.value.dataOrNull().orFalse()) - } - UserProfileEvents.StartDM -> { - coroutineScope.launch { - startDMAction.execute(roomMemberId, startDmActionState) - } - } - UserProfileEvents.ClearStartDMState -> { - startDmActionState.value = AsyncAction.Uninitialized - } - } - } - - val userName: String? by produceState( - initialValue = roomMember?.displayName ?: userProfile?.displayName, + val roomUserName: String? by produceState( + initialValue = roomMember?.displayName, key1 = roomMember, - key2 = userProfile, ) { - value = room.userDisplayName(roomMemberId) - .fold( - onSuccess = { it }, - onFailure = { - // Fallback to user profile - userProfile?.displayName - } - ) + value = room.userDisplayName(roomMemberId).getOrNull() ?: roomMember?.displayName } - val userAvatar: String? by produceState( - initialValue = roomMember?.avatarUrl ?: userProfile?.avatarUrl, + val roomUserAvatar: String? by produceState( + initialValue = roomMember?.avatarUrl, key1 = roomMember, - key2 = userProfile, ) { - value = room.userAvatarUrl(roomMemberId) - .fold( - onSuccess = { it }, - onFailure = { - // Fallback to user profile - userProfile?.avatarUrl - } - ) + value = room.userAvatarUrl(roomMemberId).getOrNull() ?: roomMember?.avatarUrl } - return UserProfileState( - userId = roomMemberId, - userName = userName, - avatarUrl = userAvatar, - isBlocked = isBlocked.value, - startDmActionState = startDmActionState.value, - displayConfirmationDialog = confirmationDialog, - isCurrentUser = isCurrentUser, - dmRoomId = dmRoomId, - canCall = canCall, - eventSink = ::handleEvents + val userProfileState = userProfilePresenter.present() + + return userProfileState.copy( + userName = roomUserName ?: userProfileState.userName, + avatarUrl = roomUserAvatar ?: userProfileState.avatarUrl, ) } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt index 73eee2092e..841a2284ce 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt @@ -14,7 +14,6 @@ import app.cash.turbine.TurbineTestContext import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Interaction -import io.element.android.features.createroom.test.FakeStartDMAction import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.aLeaveRoomState @@ -25,6 +24,8 @@ import io.element.android.features.roomdetails.impl.RoomDetailsType import io.element.android.features.roomdetails.impl.RoomTopicState import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter +import io.element.android.features.userprofile.shared.aUserProfileState +import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService @@ -82,7 +83,12 @@ class RoomDetailsPresenterTest { val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService) val roomMemberDetailsPresenterFactory = object : RoomMemberDetailsPresenter.Factory { override fun create(roomMemberId: UserId): RoomMemberDetailsPresenter { - return RoomMemberDetailsPresenter(roomMemberId, matrixClient, room, FakeStartDMAction()) + return RoomMemberDetailsPresenter( + roomMemberId = roomMemberId, + room = room, + userProfileStatePresenterFactory = { + Presenter { aUserProfileState() } + }) } } val featureFlagService = FakeFeatureFlagService( diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTest.kt index c987e308ab..1d72610e28 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTest.kt @@ -9,27 +9,18 @@ package io.element.android.features.roomdetails.members.details import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow -import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.createroom.api.StartDMAction -import io.element.android.features.createroom.test.FakeStartDMAction import io.element.android.features.roomdetails.aMatrixRoom import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter -import io.element.android.features.userprofile.shared.UserProfileEvents -import io.element.android.features.userprofile.shared.UserProfileState -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.features.userprofile.api.UserProfileStatePresenterFactory +import io.element.android.features.userprofile.shared.aUserProfileState +import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.test.AN_EXCEPTION -import io.element.android.libraries.matrix.test.A_ROOM_ID -import io.element.android.libraries.matrix.test.A_THROWABLE -import io.element.android.libraries.matrix.test.FakeMatrixClient -import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.tests.testutils.WarmUpRule import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -54,28 +45,26 @@ class RoomMemberDetailsPresenterTest { } val presenter = createRoomMemberDetailsPresenter( room = room, - roomMemberId = roomMember.userId ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitFirstItem() - assertThat(initialState.userId).isEqualTo(roomMember.userId) - assertThat(initialState.userName).isEqualTo(roomMember.displayName) - assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl) - assertThat(initialState.isBlocked).isEqualTo(AsyncData.Success(roomMember.isIgnored)) - assertThat(initialState.dmRoomId).isEqualTo(A_ROOM_ID) - assertThat(initialState.canCall).isFalse() + val initialState = awaitItem() + assertThat(initialState.userName).isEqualTo("Alice") + assertThat(initialState.avatarUrl).isEqualTo("Profile avatar url") skipItems(1) - val loadedState = awaitItem() - assertThat(loadedState.userName).isEqualTo("A custom name") - assertThat(loadedState.avatarUrl).isEqualTo("A custom avatar") + val nextState = awaitItem() + assertThat(nextState.userName).isEqualTo("A custom name") + assertThat(nextState.avatarUrl).isEqualTo("A custom avatar") } } @Test fun `present - will recover when retrieving room member details fails`() = runTest { - val roomMember = aRoomMember(displayName = "Alice") + val roomMember = aRoomMember( + displayName = "Alice", + avatarUrl = "Alice Avatar url", + ) val room = aMatrixRoom( userDisplayNameResult = { Result.failure(Throwable()) }, userAvatarUrlResult = { Result.failure(Throwable()) }, @@ -86,16 +75,13 @@ class RoomMemberDetailsPresenterTest { val presenter = createRoomMemberDetailsPresenter( room = room, - roomMemberId = roomMember.userId ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitFirstItem() - assertThat(initialState.userName).isEqualTo(roomMember.displayName) - assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl) - - ensureAllEventsConsumed() + val initialState = awaitItem() + assertThat(initialState.userName).isEqualTo("Alice") + assertThat(initialState.avatarUrl).isEqualTo("Alice Avatar url") } } @@ -111,238 +97,81 @@ class RoomMemberDetailsPresenterTest { } val presenter = createRoomMemberDetailsPresenter( room = room, - roomMemberId = roomMember.userId ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitFirstItem() - assertThat(initialState.userName).isEqualTo(roomMember.displayName) - assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl) - - ensureAllEventsConsumed() + val initialState = awaitItem() + assertThat(initialState.userName).isEqualTo("Alice") + assertThat(initialState.avatarUrl).isEqualTo("Profile avatar url") } } @Test fun `present - will fallback to user profile if user is not a member of the room`() = runTest { - val bobProfile = aMatrixUser("@bob:server.org", "Bob", avatarUrl = "anAvatarUrl") val room = aMatrixRoom( userDisplayNameResult = { Result.failure(Exception("Not a member!")) }, userAvatarUrlResult = { Result.failure(Exception("Not a member!")) }, getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) }, ) - val client = FakeMatrixClient().apply { - givenGetProfileResult(bobProfile.userId, Result.success(bobProfile)) - } val presenter = createRoomMemberDetailsPresenter( - client = client, room = room, - roomMemberId = UserId("@bob:server.org") ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(2) - val initialState = awaitFirstItem() - assertThat(initialState.userName).isEqualTo("Bob") - assertThat(initialState.avatarUrl).isEqualTo("anAvatarUrl") - - ensureAllEventsConsumed() + val initialState = awaitItem() + assertThat(initialState.userName).isEqualTo("Profile user name") + assertThat(initialState.avatarUrl).isEqualTo("Profile avatar url") } } @Test - fun `present - BlockUser needing confirmation displays confirmation dialog`() = runTest { + fun `present - null cases`() = runTest { + val roomMember = aRoomMember( + displayName = null, + avatarUrl = null, + ) + val room = aMatrixRoom( + userDisplayNameResult = { Result.success(null) }, + userAvatarUrlResult = { Result.success(null) }, + getUpdatedMemberResult = { Result.success(roomMember) }, + ) val presenter = createRoomMemberDetailsPresenter( - room = aMatrixRoom( - getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) }, - userDisplayNameResult = { Result.success("Alice") }, - userAvatarUrlResult = { Result.success("anAvatarUrl") }, - ) + room = room, + userProfileStatePresenterFactory = { + Presenter { + aUserProfileState( + userName = null, + avatarUrl = null, + ) + } + }, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitFirstItem() - initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = true)) - - val dialogState = awaitItem() - assertThat(dialogState.displayConfirmationDialog).isEqualTo(UserProfileState.ConfirmationDialog.Block) - - dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog) - assertThat(awaitItem().displayConfirmationDialog).isNull() - - ensureAllEventsConsumed() + val initialState = awaitItem() + assertThat(initialState.userName).isNull() + assertThat(initialState.avatarUrl).isNull() } } - @Test - fun `present - BlockUser and UnblockUser without confirmation change the 'blocked' state`() = runTest { - val client = FakeMatrixClient() - val roomMember = aRoomMember() - val presenter = createRoomMemberDetailsPresenter( - room = aMatrixRoom( - getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) }, - userDisplayNameResult = { Result.success("Alice") }, - userAvatarUrlResult = { Result.success("anAvatarUrl") }, - ), - client = client, - roomMemberId = roomMember.userId - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitFirstItem() - initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false)) - assertThat(awaitItem().isBlocked.isLoading()).isTrue() - client.emitIgnoreUserList(listOf(roomMember.userId)) - assertThat(awaitItem().isBlocked.dataOrNull()).isTrue() - - initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false)) - assertThat(awaitItem().isBlocked.isLoading()).isTrue() - client.emitIgnoreUserList(listOf()) - assertThat(awaitItem().isBlocked.dataOrNull()).isFalse() - } - } - - @Test - fun `present - BlockUser with error`() = runTest { - val matrixClient = FakeMatrixClient() - matrixClient.givenIgnoreUserResult(Result.failure(A_THROWABLE)) - val presenter = createRoomMemberDetailsPresenter( - client = matrixClient, - room = aMatrixRoom( - getUpdatedMemberResult = { Result.success(aRoomMember(displayName = "Alice")) }, - userDisplayNameResult = { Result.success("Alice") }, - userAvatarUrlResult = { Result.success("anAvatarUrl") }, - ), - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitFirstItem() - initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false)) - assertThat(awaitItem().isBlocked.isLoading()).isTrue() - skipItems(2) - val errorState = awaitItem() - assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE) - // Clear error - initialState.eventSink(UserProfileEvents.ClearBlockUserError) - assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(false)) - } - } - - @Test - fun `present - UnblockUser with error`() = runTest { - val matrixClient = FakeMatrixClient() - matrixClient.givenUnignoreUserResult(Result.failure(A_THROWABLE)) - val presenter = createRoomMemberDetailsPresenter( - room = aMatrixRoom( - getUpdatedMemberResult = { Result.success(aRoomMember(displayName = "Alice")) }, - userDisplayNameResult = { Result.success("Alice") }, - userAvatarUrlResult = { Result.success("anAvatarUrl") }, - ), - client = matrixClient, - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitFirstItem() - initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false)) - assertThat(awaitItem().isBlocked.isLoading()).isTrue() - skipItems(2) - val errorState = awaitItem() - assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE) - // Clear error - initialState.eventSink(UserProfileEvents.ClearBlockUserError) - assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(true)) - } - } - - @Test - fun `present - UnblockUser needing confirmation displays confirmation dialog`() = runTest { - val presenter = createRoomMemberDetailsPresenter( - room = aMatrixRoom( - getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) }, - userDisplayNameResult = { Result.success("Alice") }, - userAvatarUrlResult = { Result.success("anAvatarUrl") }, - ), - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitFirstItem() - initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = true)) - - val dialogState = awaitItem() - assertThat(dialogState.displayConfirmationDialog).isEqualTo(UserProfileState.ConfirmationDialog.Unblock) - - dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog) - assertThat(awaitItem().displayConfirmationDialog).isNull() - - ensureAllEventsConsumed() - } - } - - @Test - fun `present - start DM action complete scenario`() = runTest { - val startDMAction = FakeStartDMAction() - val presenter = createRoomMemberDetailsPresenter( - room = aMatrixRoom( - getUpdatedMemberResult = { Result.success(aRoomMember(displayName = "Alice")) }, - userDisplayNameResult = { Result.success("Alice") }, - userAvatarUrlResult = { Result.success("anAvatarUrl") }, - ), - startDMAction = startDMAction, - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitFirstItem() - assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java) - val startDMSuccessResult = AsyncAction.Success(A_ROOM_ID) - val startDMFailureResult = AsyncAction.Failure(A_THROWABLE) - - // Failure - startDMAction.givenExecuteResult(startDMFailureResult) - initialState.eventSink(UserProfileEvents.StartDM) - assertThat(awaitItem().startDmActionState).isInstanceOf(AsyncAction.Loading::class.java) - skipItems(2) - awaitItem().also { state -> - assertThat(state.startDmActionState).isEqualTo(startDMFailureResult) - state.eventSink(UserProfileEvents.ClearStartDMState) - } - - // Success - startDMAction.givenExecuteResult(startDMSuccessResult) - awaitItem().also { state -> - assertThat(state.startDmActionState).isEqualTo(AsyncAction.Uninitialized) - state.eventSink(UserProfileEvents.StartDM) - } - assertThat(awaitItem().startDmActionState).isInstanceOf(AsyncAction.Loading::class.java) - awaitItem().also { state -> - assertThat(state.startDmActionState).isEqualTo(startDMSuccessResult) - } - } - } - - private suspend fun ReceiveTurbine.awaitFirstItem(): T { - skipItems(1) - return awaitItem() - } - private fun createRoomMemberDetailsPresenter( room: MatrixRoom, - client: MatrixClient = FakeMatrixClient(), - roomMemberId: UserId = UserId("@alice:server.org"), - startDMAction: StartDMAction = FakeStartDMAction() + userProfileStatePresenterFactory: UserProfileStatePresenterFactory = UserProfileStatePresenterFactory { + Presenter { + aUserProfileState( + userName = "Profile user name", + avatarUrl = "Profile avatar url", + ) + } + }, ): RoomMemberDetailsPresenter { return RoomMemberDetailsPresenter( - roomMemberId = roomMemberId, - client = client, + roomMemberId = UserId("@alice:server.org"), room = room, - startDMAction = startDMAction + userProfileStatePresenterFactory = userProfileStatePresenterFactory ) } } diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileEvents.kt b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvents.kt similarity index 91% rename from features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileEvents.kt rename to features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvents.kt index da096f6288..a277eb5733 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileEvents.kt +++ b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvents.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.features.userprofile.shared +package io.element.android.features.userprofile.api sealed interface UserProfileEvents { data object StartDM : UserProfileEvents diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileState.kt b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileState.kt similarity index 93% rename from features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileState.kt rename to features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileState.kt index ceb3cd7952..4bc3dc06d7 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileState.kt +++ b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileState.kt @@ -5,7 +5,7 @@ * Please see LICENSE in the repository root for full details. */ -package io.element.android.features.userprofile.shared +package io.element.android.features.userprofile.api import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData diff --git a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileStatePresenterFactory.kt b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileStatePresenterFactory.kt new file mode 100644 index 0000000000..f176a4791f --- /dev/null +++ b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileStatePresenterFactory.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.userprofile.api + +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.core.UserId + +fun interface UserProfileStatePresenterFactory { + fun create(userId: UserId): Presenter +} diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileStatePresenterFactory.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileStatePresenterFactory.kt new file mode 100644 index 0000000000..6d76097911 --- /dev/null +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileStatePresenterFactory.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.userprofile.impl + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.userprofile.api.UserProfileState +import io.element.android.features.userprofile.api.UserProfileStatePresenterFactory +import io.element.android.features.userprofile.impl.root.UserProfilePresenter +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.core.UserId +import javax.inject.Inject + +@ContributesBinding(SessionScope::class) +class DefaultUserProfileStatePresenterFactory @Inject constructor( + private val factory: UserProfilePresenter.Factory, +) : UserProfileStatePresenterFactory { + override fun create(userId: UserId): Presenter = factory.create(userId) +} diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt index fb696c3257..de58bf2682 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt @@ -19,10 +19,10 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.features.createroom.api.StartDMAction -import io.element.android.features.userprofile.shared.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileState +import io.element.android.features.userprofile.api.UserProfileState.ConfirmationDialog import io.element.android.features.userprofile.shared.UserProfilePresenterHelper -import io.element.android.features.userprofile.shared.UserProfileState -import io.element.android.features.userprofile.shared.UserProfileState.ConfirmationDialog import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -55,6 +55,7 @@ class UserProfilePresenter @AssistedInject constructor( @Composable override fun present(): UserProfileState { val coroutineScope = rememberCoroutineScope() + val isCurrentUser = remember { client.isMe(userId) } var confirmationDialog by remember { mutableStateOf(null) } var userProfile by remember { mutableStateOf(null) } val startDmActionState: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } @@ -112,7 +113,7 @@ class UserProfilePresenter @AssistedInject constructor( isBlocked = isBlocked.value, startDmActionState = startDmActionState.value, displayConfirmationDialog = confirmationDialog, - isCurrentUser = client.isMe(userId), + isCurrentUser = isCurrentUser, dmRoomId = dmRoomId, canCall = canCall, eventSink = ::handleEvents diff --git a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt index 043ea267d7..6dedaa70e5 100644 --- a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt +++ b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt @@ -14,9 +14,9 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.createroom.test.FakeStartDMAction +import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.impl.root.UserProfilePresenter -import io.element.android.features.userprofile.shared.UserProfileEvents -import io.element.android.features.userprofile.shared.UserProfileState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.MatrixClient 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/UserProfileStateProvider.kt index 9126ae49ad..9fa1eca2dc 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/UserProfileStateProvider.kt @@ -8,6 +8,8 @@ package io.element.android.features.userprofile.shared import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.RoomId 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 250ec0c86c..12aafb2731 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 @@ -21,6 +21,8 @@ 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.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs import io.element.android.features.userprofile.shared.blockuser.BlockUserSection import io.element.android.libraries.designsystem.components.async.AsyncActionView diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogs.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogs.kt index b2e7ec259c..d4e8ad952d 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogs.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogs.kt @@ -9,9 +9,9 @@ package io.element.android.features.userprofile.shared.blockuser import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource +import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.shared.R -import io.element.android.features.userprofile.shared.UserProfileEvents -import io.element.android.features.userprofile.shared.UserProfileState import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog @Composable diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt index 92c979b8bf..0ec3d5ee91 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt @@ -14,9 +14,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.shared.R -import io.element.android.features.userprofile.shared.UserProfileEvents -import io.element.android.features.userprofile.shared.UserProfileState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.components.dialogs.RetryDialog @@ -34,23 +34,24 @@ fun BlockUserSection( state: UserProfileState, modifier: Modifier = Modifier, ) { + val isBlocked = state.isBlocked PreferenceCategory( modifier = modifier, showTopDivider = false, ) { - when (state.isBlocked) { - is AsyncData.Failure -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = false, eventSink = state.eventSink) - is AsyncData.Loading -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = true, eventSink = state.eventSink) - is AsyncData.Success -> PreferenceBlockUser(isBlocked = state.isBlocked.data, isLoading = false, eventSink = state.eventSink) + when (isBlocked) { + is AsyncData.Failure -> PreferenceBlockUser(isBlocked = isBlocked.prevData, isLoading = false, eventSink = state.eventSink) + is AsyncData.Loading -> PreferenceBlockUser(isBlocked = isBlocked.prevData, isLoading = true, eventSink = state.eventSink) + is AsyncData.Success -> PreferenceBlockUser(isBlocked = isBlocked.data, isLoading = false, eventSink = state.eventSink) AsyncData.Uninitialized -> PreferenceBlockUser(isBlocked = null, isLoading = true, eventSink = state.eventSink) } } - if (state.isBlocked is AsyncData.Failure) { + if (isBlocked is AsyncData.Failure) { RetryDialog( content = stringResource(CommonStrings.error_unknown), onDismiss = { state.eventSink(UserProfileEvents.ClearBlockUserError) }, onRetry = { - val event = when (state.isBlocked.prevData) { + val event = when (isBlocked.prevData) { true -> UserProfileEvents.UnblockUser(needsConfirmation = false) false -> UserProfileEvents.BlockUser(needsConfirmation = false) // null case Should not happen diff --git a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt index 2971904d89..38ebfc7960 100644 --- a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt +++ b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt @@ -13,9 +13,9 @@ import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.shared.R -import io.element.android.features.userprofile.shared.UserProfileEvents -import io.element.android.features.userprofile.shared.UserProfileState import io.element.android.features.userprofile.shared.UserProfileView import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.libraries.architecture.AsyncData diff --git a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt index 584713bc27..934258974e 100644 --- a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt +++ b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt @@ -10,9 +10,9 @@ package io.element.android.features.userprofile.shared.blockuser import androidx.activity.ComponentActivity import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.shared.R -import io.element.android.features.userprofile.shared.UserProfileEvents -import io.element.android.features.userprofile.shared.UserProfileState import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EventsRecorder From 2c94df3647aef2644af0ed6b83366c2fbabf7fee Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 17 Oct 2024 14:07:13 +0200 Subject: [PATCH 55/67] No need for UserProfilePresenterHelper anymore. --- .../impl/root/UserProfilePresenter.kt | 55 ++++++++++++--- .../shared/UserProfilePresenterHelper.kt | 67 ------------------- 2 files changed, 46 insertions(+), 76 deletions(-) delete mode 100644 features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfilePresenterHelper.kt diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt index de58bf2682..30209d49e6 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt @@ -10,8 +10,10 @@ package io.element.android.features.userprofile.impl.root import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue @@ -22,7 +24,6 @@ import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState.ConfirmationDialog -import io.element.android.features.userprofile.shared.UserProfilePresenterHelper import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -31,6 +32,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -47,10 +49,23 @@ class UserProfilePresenter @AssistedInject constructor( fun create(userId: UserId): UserProfilePresenter } - private val userProfilePresenterHelper = UserProfilePresenterHelper( - userId = userId, - client = client, - ) + @Composable + fun getDmRoomId(): State { + return produceState(initialValue = null) { + value = client.findDM(userId) + } + } + + @Composable + fun getCanCall(roomId: RoomId?): State { + return produceState(initialValue = false, roomId) { + value = if (client.isMe(userId)) { + false + } else { + roomId?.let { client.getRoom(it)?.canUserJoinCall(client.sessionId)?.getOrNull() == true }.orFalse() + } + } + } @Composable override fun present(): UserProfileState { @@ -60,8 +75,8 @@ class UserProfilePresenter @AssistedInject constructor( var userProfile by remember { mutableStateOf(null) } val startDmActionState: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val isBlocked: MutableState> = remember { mutableStateOf(AsyncData.Uninitialized) } - val dmRoomId by userProfilePresenterHelper.getDmRoomId() - val canCall by userProfilePresenterHelper.getCanCall(dmRoomId) + val dmRoomId by getDmRoomId() + val canCall by getCanCall(dmRoomId) LaunchedEffect(Unit) { client.ignoredUsersFlow .map { ignoredUsers -> userId in ignoredUsers } @@ -80,7 +95,7 @@ class UserProfilePresenter @AssistedInject constructor( confirmationDialog = ConfirmationDialog.Block } else { confirmationDialog = null - userProfilePresenterHelper.blockUser(coroutineScope, isBlocked) + coroutineScope.blockUser(isBlocked) } } is UserProfileEvents.UnblockUser -> { @@ -88,7 +103,7 @@ class UserProfilePresenter @AssistedInject constructor( confirmationDialog = ConfirmationDialog.Unblock } else { confirmationDialog = null - userProfilePresenterHelper.unblockUser(coroutineScope, isBlocked) + coroutineScope.unblockUser(isBlocked) } } UserProfileEvents.ClearConfirmationDialog -> confirmationDialog = null @@ -119,4 +134,26 @@ class UserProfilePresenter @AssistedInject constructor( eventSink = ::handleEvents ) } + + private fun CoroutineScope.blockUser( + isBlockedState: MutableState>, + ) = launch { + isBlockedState.value = AsyncData.Loading(false) + client.ignoreUser(userId) + .onFailure { + isBlockedState.value = AsyncData.Failure(it, false) + } + // Note: on success, ignoredUsersFlow will emit new item. + } + + private fun CoroutineScope.unblockUser( + isBlockedState: MutableState>, + ) = launch { + isBlockedState.value = AsyncData.Loading(true) + client.unignoreUser(userId) + .onFailure { + isBlockedState.value = AsyncData.Failure(it, true) + } + // Note: on success, ignoredUsersFlow will emit new item. + } } diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfilePresenterHelper.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfilePresenterHelper.kt deleted file mode 100644 index 3b206900b2..0000000000 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfilePresenterHelper.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only - * Please see LICENSE in the repository root for full details. - */ - -package io.element.android.features.userprofile.shared - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.State -import androidx.compose.runtime.produceState -import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.core.bool.orFalse -import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.UserId -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -class UserProfilePresenterHelper( - private val userId: UserId, - private val client: MatrixClient, -) { - @Composable - fun getDmRoomId(): State { - return produceState(initialValue = null) { - value = client.findDM(userId) - } - } - - @Composable - fun getCanCall(roomId: RoomId?): State { - return produceState(initialValue = false, roomId) { - value = if (client.isMe(userId)) { - false - } else { - roomId?.let { client.getRoom(it)?.canUserJoinCall(client.sessionId)?.getOrNull() == true }.orFalse() - } - } - } - - fun blockUser( - scope: CoroutineScope, - isBlockedState: MutableState>, - ) = scope.launch { - isBlockedState.value = AsyncData.Loading(false) - client.ignoreUser(userId) - .onFailure { - isBlockedState.value = AsyncData.Failure(it, false) - } - // Note: on success, ignoredUserList will be updated. - } - - fun unblockUser( - scope: CoroutineScope, - isBlockedState: MutableState>, - ) = scope.launch { - isBlockedState.value = AsyncData.Loading(true) - client.unignoreUser(userId) - .onFailure { - isBlockedState.value = AsyncData.Failure(it, true) - } - // Note: on success, ignoredUserList will be updated. - } -} From fbd7e33bf0ba70d3def95358a011b47ad6bb7945 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 17 Oct 2024 14:16:29 +0200 Subject: [PATCH 56/67] Format --- .../android/features/roomdetails/RoomDetailsPresenterTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt index 841a2284ce..abb7d9bdb5 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt @@ -88,7 +88,8 @@ class RoomDetailsPresenterTest { room = room, userProfileStatePresenterFactory = { Presenter { aUserProfileState() } - }) + }, + ) } } val featureFlagService = FakeFeatureFlagService( From feeddaceb9411ec7f4deddd80fa2105586f2c257 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 17 Oct 2024 14:24:08 +0200 Subject: [PATCH 57/67] Renaming. --- .../features/roomdetails/impl/di/RoomMemberModule.kt | 10 +++++++--- .../impl/members/details/RoomMemberDetailsPresenter.kt | 8 ++++---- .../features/roomdetails/RoomDetailsPresenterTest.kt | 2 +- .../members/details/RoomMemberDetailsPresenterTest.kt | 8 ++++---- ...senterFactory.kt => UserProfilePresenterFactory.kt} | 2 +- ...actory.kt => DefaultUserProfilePresenterFactory.kt} | 6 +++--- 6 files changed, 20 insertions(+), 16 deletions(-) rename features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/{UserProfileStatePresenterFactory.kt => UserProfilePresenterFactory.kt} (88%) rename features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/{DefaultUserProfileStatePresenterFactory.kt => DefaultUserProfilePresenterFactory.kt} (80%) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt index 46406f3751..c1e4369aaa 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt @@ -11,7 +11,7 @@ import com.squareup.anvil.annotations.ContributesTo import dagger.Module import dagger.Provides import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter -import io.element.android.features.userprofile.api.UserProfileStatePresenterFactory +import io.element.android.features.userprofile.api.UserProfilePresenterFactory import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom @@ -22,11 +22,15 @@ object RoomMemberModule { @Provides fun provideRoomMemberDetailsPresenterFactory( room: MatrixRoom, - userProfileStatePresenterFactory: UserProfileStatePresenterFactory, + userProfilePresenterFactory: UserProfilePresenterFactory, ): RoomMemberDetailsPresenter.Factory { return object : RoomMemberDetailsPresenter.Factory { override fun create(roomMemberId: UserId): RoomMemberDetailsPresenter { - return RoomMemberDetailsPresenter(roomMemberId, room, userProfileStatePresenterFactory) + return RoomMemberDetailsPresenter( + roomMemberId = roomMemberId, + room = room, + userProfilePresenterFactory = userProfilePresenterFactory, + ) } } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt index fce5de5e21..e2e2d160b9 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt @@ -14,7 +14,7 @@ import androidx.compose.runtime.produceState import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.features.userprofile.api.UserProfileState -import io.element.android.features.userprofile.api.UserProfileStatePresenterFactory +import io.element.android.features.userprofile.api.UserProfilePresenterFactory import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom @@ -22,18 +22,18 @@ import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState /** * Presenter for room member details screen. - * Rely on UserProfileStatePresenter, but override some fields with room member info when available. + * Rely on UserProfilePresenter, but override some fields with room member info when available. */ class RoomMemberDetailsPresenter @AssistedInject constructor( @Assisted private val roomMemberId: UserId, private val room: MatrixRoom, - userProfileStatePresenterFactory: UserProfileStatePresenterFactory, + userProfilePresenterFactory: UserProfilePresenterFactory, ) : Presenter { interface Factory { fun create(roomMemberId: UserId): RoomMemberDetailsPresenter } - private val userProfilePresenter = userProfileStatePresenterFactory.create(roomMemberId) + private val userProfilePresenter = userProfilePresenterFactory.create(roomMemberId) @Composable override fun present(): UserProfileState { diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt index abb7d9bdb5..9a893b671e 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt @@ -86,7 +86,7 @@ class RoomDetailsPresenterTest { return RoomMemberDetailsPresenter( roomMemberId = roomMemberId, room = room, - userProfileStatePresenterFactory = { + userProfilePresenterFactory = { Presenter { aUserProfileState() } }, ) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTest.kt index 1d72610e28..1ea63f6cce 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTest.kt @@ -14,7 +14,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.roomdetails.aMatrixRoom import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter -import io.element.android.features.userprofile.api.UserProfileStatePresenterFactory +import io.element.android.features.userprofile.api.UserProfilePresenterFactory import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.UserId @@ -139,7 +139,7 @@ class RoomMemberDetailsPresenterTest { ) val presenter = createRoomMemberDetailsPresenter( room = room, - userProfileStatePresenterFactory = { + userProfilePresenterFactory = { Presenter { aUserProfileState( userName = null, @@ -159,7 +159,7 @@ class RoomMemberDetailsPresenterTest { private fun createRoomMemberDetailsPresenter( room: MatrixRoom, - userProfileStatePresenterFactory: UserProfileStatePresenterFactory = UserProfileStatePresenterFactory { + userProfilePresenterFactory: UserProfilePresenterFactory = UserProfilePresenterFactory { Presenter { aUserProfileState( userName = "Profile user name", @@ -171,7 +171,7 @@ class RoomMemberDetailsPresenterTest { return RoomMemberDetailsPresenter( roomMemberId = UserId("@alice:server.org"), room = room, - userProfileStatePresenterFactory = userProfileStatePresenterFactory + userProfilePresenterFactory = userProfilePresenterFactory ) } } diff --git a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileStatePresenterFactory.kt b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfilePresenterFactory.kt similarity index 88% rename from features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileStatePresenterFactory.kt rename to features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfilePresenterFactory.kt index f176a4791f..6142d5f225 100644 --- a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileStatePresenterFactory.kt +++ b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfilePresenterFactory.kt @@ -10,6 +10,6 @@ package io.element.android.features.userprofile.api import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.UserId -fun interface UserProfileStatePresenterFactory { +fun interface UserProfilePresenterFactory { fun create(userId: UserId): Presenter } diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileStatePresenterFactory.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfilePresenterFactory.kt similarity index 80% rename from features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileStatePresenterFactory.kt rename to features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfilePresenterFactory.kt index 6d76097911..c0ec155e26 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileStatePresenterFactory.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfilePresenterFactory.kt @@ -9,7 +9,7 @@ package io.element.android.features.userprofile.impl import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.userprofile.api.UserProfileState -import io.element.android.features.userprofile.api.UserProfileStatePresenterFactory +import io.element.android.features.userprofile.api.UserProfilePresenterFactory import io.element.android.features.userprofile.impl.root.UserProfilePresenter import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.di.SessionScope @@ -17,8 +17,8 @@ import io.element.android.libraries.matrix.api.core.UserId import javax.inject.Inject @ContributesBinding(SessionScope::class) -class DefaultUserProfileStatePresenterFactory @Inject constructor( +class DefaultUserProfilePresenterFactory @Inject constructor( private val factory: UserProfilePresenter.Factory, -) : UserProfileStatePresenterFactory { +) : UserProfilePresenterFactory { override fun create(userId: UserId): Presenter = factory.create(userId) } From 4f3065d1c667b5c470544354825a023a9ad8d173 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 17 Oct 2024 15:55:45 +0200 Subject: [PATCH 58/67] Cover getCanCall with tests. --- .../impl/root/UserProfilePresenter.kt | 4 +- .../impl/UserProfilePresenterTest.kt | 70 +++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt index 30209d49e6..e73eef6aa0 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt @@ -50,14 +50,14 @@ class UserProfilePresenter @AssistedInject constructor( } @Composable - fun getDmRoomId(): State { + private fun getDmRoomId(): State { return produceState(initialValue = null) { value = client.findDM(userId) } } @Composable - fun getCanCall(roomId: RoomId?): State { + private fun getCanCall(roomId: RoomId?): State { return produceState(initialValue = false, roomId) { value = if (client.isMe(userId)) { false diff --git a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt index 6dedaa70e5..485ac268a5 100644 --- a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt +++ b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt @@ -20,14 +20,18 @@ import io.element.android.features.userprofile.impl.root.UserProfilePresenter import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.awaitLastSequentialItem import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -60,6 +64,72 @@ class UserProfilePresenterTest { } } + @Test + fun `present - canCall is true when all the conditions are met`() { + testCanCall( + expectedResult = true, + ) + } + + @Test + fun `present - canCall is false when canUserJoinCall returns false`() { + testCanCall( + canUserJoinCallResult = Result.success(false), + expectedResult = false, + ) + } + + @Test + fun `present - canCall is false when canUserJoinCall fails`() { + testCanCall( + canUserJoinCallResult = Result.failure(AN_EXCEPTION), + expectedResult = false, + ) + } + + @Test + fun `present - canCall is false when there is no DM`() { + testCanCall( + dmRoom = null, + expectedResult = false, + ) + } + + @Test + fun `present - canCall is false when room is not found`() { + testCanCall( + canFindRoom = false, + expectedResult = false, + ) + } + + private fun testCanCall( + canUserJoinCallResult: Result = Result.success(true), + dmRoom: RoomId? = A_ROOM_ID, + canFindRoom: Boolean = true, + expectedResult: Boolean, + ) = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { canUserJoinCallResult }, + ) + val client = FakeMatrixClient().apply { + if (canFindRoom) { + givenGetRoomResult(A_ROOM_ID, room) + } + givenFindDmResult(dmRoom) + } + val presenter = createUserProfilePresenter( + userId = A_USER_ID_2, + client = client, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitLastSequentialItem() + assertThat(initialState.canCall).isEqualTo(expectedResult) + } + } + @Test fun `present - returns empty data in case of failure`() = runTest { val client = FakeMatrixClient().apply { From 56c564c49934bbf4e7156a7b0d81d318c8bec497 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 17 Oct 2024 15:57:30 +0200 Subject: [PATCH 59/67] Use test extension on presenter. --- .../impl/UserProfilePresenterTest.kt | 40 +++++-------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt index 485ac268a5..1da38187e1 100644 --- a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt +++ b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt @@ -7,10 +7,7 @@ package io.element.android.features.userprofile.impl -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow import app.cash.turbine.ReceiveTurbine -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.createroom.test.FakeStartDMAction @@ -32,6 +29,7 @@ import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.awaitLastSequentialItem +import io.element.android.tests.testutils.test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -51,9 +49,7 @@ class UserProfilePresenterTest { val presenter = createUserProfilePresenter( client = client, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitFirstItem() assertThat(initialState.userId).isEqualTo(matrixUser.userId) assertThat(initialState.userName).isEqualTo(matrixUser.displayName) @@ -122,9 +118,7 @@ class UserProfilePresenterTest { userId = A_USER_ID_2, client = client, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitLastSequentialItem() assertThat(initialState.canCall).isEqualTo(expectedResult) } @@ -138,9 +132,7 @@ class UserProfilePresenterTest { val presenter = createUserProfilePresenter( client = client, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitFirstItem() assertThat(initialState.userId).isEqualTo(A_USER_ID) assertThat(initialState.userName).isNull() @@ -152,9 +144,7 @@ class UserProfilePresenterTest { @Test fun `present - BlockUser needing confirmation displays confirmation dialog`() = runTest { val presenter = createUserProfilePresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitFirstItem() initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = true)) @@ -175,9 +165,7 @@ class UserProfilePresenterTest { client = client, userId = A_USER_ID ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitFirstItem() initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false)) assertThat(awaitItem().isBlocked.isLoading()).isTrue() @@ -196,9 +184,7 @@ class UserProfilePresenterTest { val matrixClient = FakeMatrixClient() matrixClient.givenIgnoreUserResult(Result.failure(A_THROWABLE)) val presenter = createUserProfilePresenter(client = matrixClient) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitFirstItem() initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false)) assertThat(awaitItem().isBlocked.isLoading()).isTrue() @@ -215,9 +201,7 @@ class UserProfilePresenterTest { val matrixClient = FakeMatrixClient() matrixClient.givenUnignoreUserResult(Result.failure(A_THROWABLE)) val presenter = createUserProfilePresenter(client = matrixClient) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitFirstItem() initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false)) assertThat(awaitItem().isBlocked.isLoading()).isTrue() @@ -232,9 +216,7 @@ class UserProfilePresenterTest { @Test fun `present - UnblockUser needing confirmation displays confirmation dialog`() = runTest { val presenter = createUserProfilePresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitFirstItem() initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = true)) @@ -252,9 +234,7 @@ class UserProfilePresenterTest { fun `present - start DM action complete scenario`() = runTest { val startDMAction = FakeStartDMAction() val presenter = createUserProfilePresenter(startDMAction = startDMAction) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitFirstItem() assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java) val startDMSuccessResult = AsyncAction.Success(A_ROOM_ID) From e68d917b4723eed3db6b354d0091d1197105daab Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 14:32:08 +0000 Subject: [PATCH 60/67] Update dependency org.matrix.rustcomponents:sdk-android to v0.2.55 --- 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 aced112ac8..4f6dd3bc93 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -168,7 +168,7 @@ jsoup = "org.jsoup:jsoup:1.18.1" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.54" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.55" matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } From 252308f591acffb57559ca826f8f33ef8bcaa7ad Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 17 Oct 2024 16:34:10 +0200 Subject: [PATCH 61/67] Reorder imports. --- .../impl/members/details/RoomMemberDetailsPresenter.kt | 2 +- .../userprofile/impl/DefaultUserProfilePresenterFactory.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt index e2e2d160b9..0e6d9052f0 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt @@ -13,8 +13,8 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import dagger.assisted.Assisted import dagger.assisted.AssistedInject -import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfilePresenterFactory +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfilePresenterFactory.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfilePresenterFactory.kt index c0ec155e26..c0f32b5df7 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfilePresenterFactory.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfilePresenterFactory.kt @@ -8,8 +8,8 @@ package io.element.android.features.userprofile.impl import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfilePresenterFactory +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.impl.root.UserProfilePresenter import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.di.SessionScope From 9852ce682109ec4c5c891e53344dc72561f73349 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 18 Oct 2024 08:49:02 +0200 Subject: [PATCH 62/67] Fix API Break: introduce EventOrTransactionId --- .../impl/send/SendLocationPresenterTest.kt | 8 +-- .../features/messages/impl/MessagesEvents.kt | 4 +- .../messages/impl/MessagesPresenter.kt | 13 ++--- .../features/messages/impl/MessagesView.kt | 2 +- .../MessageComposerPresenter.kt | 11 ++-- .../CustomReactionBottomSheet.kt | 6 +- .../impl/timeline/model/TimelineItem.kt | 4 ++ .../messages/impl/MessagesPresenterTest.kt | 35 ++++++------ .../messages/impl/MessagesViewTest.kt | 4 +- .../MessageComposerPresenterTest.kt | 26 ++++----- .../features/poll/impl/data/PollRepository.kt | 4 +- .../impl/create/CreatePollPresenterTest.kt | 11 ++-- .../libraries/matrix/api/room/MatrixRoom.kt | 4 +- .../libraries/matrix/api/timeline/Timeline.kt | 13 +++-- .../item/event/EventOrTransactionId.kt | 37 +++++++++++++ .../matrix/impl/room/RustMatrixRoom.kt | 6 +- .../impl/timeline/EventOrTransactionId.kt | 16 ++++++ .../impl/timeline/MatrixTimelineItemMapper.kt | 2 +- .../matrix/impl/timeline/RustTimeline.kt | 55 +++++-------------- .../fixtures/fakes/FakeRustTimelineItem.kt | 3 +- .../matrix/test/room/FakeMatrixRoom.kt | 8 +-- .../matrix/test/timeline/FakeTimeline.kt | 30 +++++----- .../libraries/textcomposer/TextComposer.kt | 9 ++- .../textcomposer/components/SendButton.kt | 4 +- .../textcomposer/model/MessageComposerMode.kt | 7 +-- 25 files changed, 175 insertions(+), 147 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventOrTransactionId.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/EventOrTransactionId.kt diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt index 1edac5af9f..1da4723f8b 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt @@ -22,6 +22,8 @@ import io.element.android.features.location.impl.common.permissions.PermissionsS import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.location.AssetType +import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId +import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.textcomposer.model.MessageComposerMode @@ -397,8 +399,7 @@ class SendLocationPresenterTest { ) fakeMessageComposerContext.apply { composerMode = MessageComposerMode.Edit( - eventId = null, - transactionId = null, + eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(), content = "" ) } @@ -446,8 +447,7 @@ class SendLocationPresenterTest { ) fakeMessageComposerContext.apply { composerMode = MessageComposerMode.Edit( - eventId = null, - transactionId = null, + eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(), content = "" ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvents.kt index 943040ce49..c8699524bd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvents.kt @@ -9,11 +9,11 @@ package io.element.android.features.messages.impl 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.UniqueId +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId sealed interface MessagesEvents { data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : MessagesEvents - data class ToggleReaction(val emoji: String, val uniqueId: UniqueId) : MessagesEvents + data class ToggleReaction(val emoji: String, val eventOrTransactionId: EventOrTransactionId) : MessagesEvents data class InviteDialogDismissed(val action: InviteDialogAction) : MessagesEvents data object Dismiss : MessagesEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index a42a09fcd6..b215fc49fd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -62,7 +62,6 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo @@ -73,6 +72,7 @@ import io.element.android.libraries.matrix.api.room.powerlevels.canPinUnpin import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.ui.messages.reply.map import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.room.canCall @@ -191,7 +191,7 @@ class MessagesPresenter @AssistedInject constructor( ) } is MessagesEvents.ToggleReaction -> { - localCoroutineScope.toggleReaction(event.emoji, event.uniqueId) + localCoroutineScope.toggleReaction(event.emoji, event.eventOrTransactionId) } is MessagesEvents.InviteDialogDismissed -> { hasDismissedInviteDialog = true @@ -327,10 +327,10 @@ class MessagesPresenter @AssistedInject constructor( private fun CoroutineScope.toggleReaction( emoji: String, - uniqueId: UniqueId, + eventOrTransactionId: EventOrTransactionId, ) = launch(dispatchers.io) { timelineController.invokeOnCurrentTimeline { - toggleReaction(emoji, uniqueId) + toggleReaction(emoji, eventOrTransactionId) .onFailure { Timber.e(it) } } } @@ -360,7 +360,7 @@ class MessagesPresenter @AssistedInject constructor( private suspend fun handleActionRedact(event: TimelineItem.Event) { timelineController.invokeOnCurrentTimeline { - redactEvent(eventId = event.eventId, transactionId = event.transactionId, reason = null) + redactEvent(eventOrTransactionId = event.eventOrTransactionId, reason = null) .onFailure { Timber.e(it) } } } @@ -377,8 +377,7 @@ class MessagesPresenter @AssistedInject constructor( } else -> { val composerMode = MessageComposerMode.Edit( - targetEvent.eventId, - targetEvent.transactionId, + targetEvent.eventOrTransactionId, (targetEvent.content as? TimelineItemTextBasedContent)?.let { if (enableTextFormatting) { it.htmlBody ?: it.body 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 e56d37005b..bd8ae98b2f 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 @@ -168,7 +168,7 @@ fun MessagesView( } fun onEmojiReactionClick(emoji: String, event: TimelineItem.Event) { - state.eventSink(MessagesEvents.ToggleReaction(emoji, event.id)) + state.eventSink(MessagesEvents.ToggleReaction(emoji, event.eventOrTransactionId)) } fun onEmojiReactionLongClick(emoji: String, event: TimelineItem.Event) { 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 d0daa9bcb9..648be160d1 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 @@ -53,6 +53,7 @@ import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.timeline.TimelineException +import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import io.element.android.libraries.matrix.ui.messages.reply.map @@ -442,12 +443,11 @@ class MessageComposerPresenter @Inject constructor( intentionalMentions = message.intentionalMentions ) is MessageComposerMode.Edit -> { - val eventId = capturedMode.eventId - val transactionId = capturedMode.transactionId timelineController.invokeOnCurrentTimeline { // First try to edit the message in the current timeline - editMessage(eventId, transactionId, message.markdown, message.html, message.intentionalMentions) + editMessage(capturedMode.eventOrTransactionId, message.markdown, message.html, message.intentionalMentions) .onFailure { cause -> + val eventId = capturedMode.eventOrTransactionId.eventId if (cause is TimelineException.EventNotFound && eventId != null) { // if the event is not found in the timeline, try to edit the message directly room.editMessage(eventId, message.markdown, message.html, message.intentionalMentions) @@ -581,8 +581,7 @@ class MessageComposerPresenter @Inject constructor( when (val draftType = draft.draftType) { ComposerDraftType.NewMessage -> messageComposerContext.composerMode = MessageComposerMode.Normal is ComposerDraftType.Edit -> messageComposerContext.composerMode = MessageComposerMode.Edit( - eventId = draftType.eventId, - transactionId = null, + eventOrTransactionId = draftType.eventId.toEventOrTransactionId(), content = htmlText ?: markdownText ) is ComposerDraftType.Reply -> { @@ -611,7 +610,7 @@ class MessageComposerPresenter @Inject constructor( val draftType = when (val mode = messageComposerContext.composerMode) { is MessageComposerMode.Normal -> ComposerDraftType.NewMessage is MessageComposerMode.Edit -> { - mode.eventId?.let { eventId -> ComposerDraftType.Edit(eventId) } + mode.eventOrTransactionId.eventId?.let { eventId -> ComposerDraftType.Edit(eventId) } } is MessageComposerMode.Reply -> ComposerDraftType.Reply(mode.eventId) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt index 751cfdd050..c1478ca8a6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt @@ -16,13 +16,13 @@ import androidx.compose.ui.Modifier import io.element.android.emojibasebindings.Emoji import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.hide -import io.element.android.libraries.matrix.api.core.UniqueId +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId @OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomReactionBottomSheet( state: CustomReactionState, - onSelectEmoji: (UniqueId, Emoji) -> Unit, + onSelectEmoji: (EventOrTransactionId, Emoji) -> Unit, modifier: Modifier = Modifier, ) { val sheetState = rememberModalBottomSheetState() @@ -37,7 +37,7 @@ fun CustomReactionBottomSheet( if (target?.event == null) return sheetState.hide(coroutineScope) { state.eventSink(CustomReactionEvents.DismissCustomReactionSheet) - onSelectEmoji(target.event.id, emoji) + onSelectEmoji(target.event.eventOrTransactionId, emoji) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt index d13fd992bf..dcf0e16aa8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt @@ -18,6 +18,7 @@ import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import io.element.android.libraries.matrix.api.timeline.item.event.MessageShieldProvider @@ -92,6 +93,9 @@ sealed interface TimelineItem { val isRemote = eventId != null + val eventOrTransactionId: EventOrTransactionId + get() = EventOrTransactionId.from(eventId = eventId, transactionId = transactionId) + // No need to be lazy here? val messageShield: MessageShield? = messageShieldProvider(strict = false) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 9fb83f6471..7664379207 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -47,8 +47,6 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.core.TransactionId -import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.permalink.PermalinkParser @@ -56,13 +54,14 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.RoomMembershipState +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId +import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID_2 import io.element.android.libraries.matrix.test.A_THROWABLE -import io.element.android.libraries.matrix.test.A_UNIQUE_ID import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.room.FakeMatrixRoom @@ -164,8 +163,9 @@ class MessagesPresenterTest { @Test fun `present - handle toggling a reaction`() = runTest { val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) - val toggleReactionSuccess = lambdaRecorder { _: String, _: UniqueId -> Result.success(Unit) } - val toggleReactionFailure = lambdaRecorder { _: String, _: UniqueId -> Result.failure(IllegalStateException("Failed to send reaction")) } + val toggleReactionSuccess = lambdaRecorder { _: String, _: EventOrTransactionId -> Result.success(Unit) } + val toggleReactionFailure = + lambdaRecorder { _: String, _: EventOrTransactionId -> Result.failure(IllegalStateException("Failed to send reaction")) } val timeline = FakeTimeline().apply { this.toggleReactionLambda = toggleReactionSuccess @@ -185,23 +185,23 @@ class MessagesPresenterTest { }.test { skipItems(1) val initialState = awaitItem() - initialState.eventSink(MessagesEvents.ToggleReaction("👍", A_UNIQUE_ID)) + initialState.eventSink(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId())) assert(toggleReactionSuccess) .isCalledOnce() - .with(value("👍"), value(A_UNIQUE_ID)) + .with(value("👍"), value(AN_EVENT_ID.toEventOrTransactionId())) // No crashes when sending a reaction failed timeline.apply { toggleReactionLambda = toggleReactionFailure } - initialState.eventSink(MessagesEvents.ToggleReaction("👍", A_UNIQUE_ID)) + initialState.eventSink(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId())) assert(toggleReactionFailure) .isCalledOnce() - .with(value("👍"), value(A_UNIQUE_ID)) + .with(value("👍"), value(AN_EVENT_ID.toEventOrTransactionId())) } } @Test fun `present - handle toggling a reaction twice`() = runTest { val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) - val toggleReactionSuccess = lambdaRecorder { _: String, _: UniqueId -> Result.success(Unit) } + val toggleReactionSuccess = lambdaRecorder { _: String, _: EventOrTransactionId -> Result.success(Unit) } val timeline = FakeTimeline().apply { this.toggleReactionLambda = toggleReactionSuccess @@ -220,13 +220,13 @@ class MessagesPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(MessagesEvents.ToggleReaction("👍", A_UNIQUE_ID)) - initialState.eventSink(MessagesEvents.ToggleReaction("👍", A_UNIQUE_ID)) + initialState.eventSink(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId())) + initialState.eventSink(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId())) assert(toggleReactionSuccess) .isCalledExactly(2) .withSequence( - listOf(value("👍"), value(A_UNIQUE_ID)), - listOf(value("👍"), value(A_UNIQUE_ID)), + listOf(value("👍"), value(AN_EVENT_ID.toEventOrTransactionId())), + listOf(value("👍"), value(AN_EVENT_ID.toEventOrTransactionId())), ) skipItems(1) } @@ -452,8 +452,7 @@ class MessagesPresenterTest { composerRecorder.assertSingle( MessageComposerEvents.SetMode( composerMode = MessageComposerMode.Edit( - eventId = AN_EVENT_ID, - transactionId = null, + eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(), content = (aMessageEvent().content as TimelineItemTextContent).body ) ) @@ -506,7 +505,7 @@ class MessagesPresenterTest { canUserPinUnpinResult = { Result.success(true) }, ) - val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(Unit) } + val redactEventLambda = lambdaRecorder { _: EventOrTransactionId, _: String? -> Result.success(Unit) } liveTimeline.redactEventLambda = redactEventLambda val presenter = createMessagesPresenter( matrixRoom = matrixRoom, @@ -521,7 +520,7 @@ class MessagesPresenterTest { awaitItem() assert(redactEventLambda) .isCalledOnce() - .with(value(messageEvent.eventId), value(messageEvent.transactionId), value(null)) + .with(value(messageEvent.eventOrTransactionId), value(null)) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt index 210ffb4fe6..a20acaa5df 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt @@ -355,7 +355,7 @@ class MessagesViewTest { state = state, ) rule.onAllNodesWithText("👍️").onFirst().performClick() - eventsRecorder.assertSingle(MessagesEvents.ToggleReaction("👍️", timelineItem.id)) + eventsRecorder.assertSingle(MessagesEvents.ToggleReaction("👍️", timelineItem.eventOrTransactionId)) } @Test @@ -484,7 +484,7 @@ class MessagesViewTest { // Give time for the close animation to complete rule.mainClock.advanceTimeBy(milliseconds = 1_000) customReactionStateEventsRecorder.assertSingle(CustomReactionEvents.DismissCustomReactionSheet) - eventsRecorder.assertSingle(MessagesEvents.ToggleReaction(aUnicode, timelineItem.id)) + eventsRecorder.assertSingle(MessagesEvents.ToggleReaction(aUnicode, timelineItem.eventOrTransactionId)) } @Test 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 df0cc1bdc2..c66a2b56ad 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt @@ -32,7 +32,6 @@ import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder @@ -44,7 +43,9 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType import io.element.android.libraries.matrix.api.timeline.TimelineException +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo +import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.test.ANOTHER_MESSAGE import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_MESSAGE @@ -355,7 +356,7 @@ class MessageComposerPresenterTest { @Test fun `present - edit sent message`() = runTest { - val editMessageLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String, _: String?, _: List -> + val editMessageLambda = lambdaRecorder { _: EventOrTransactionId, _: String, _: String?, _: List -> Result.success(Unit) } val timeline = FakeTimeline().apply { @@ -392,7 +393,7 @@ class MessageComposerPresenterTest { assert(editMessageLambda) .isCalledOnce() - .with(value(AN_EVENT_ID), value(null), value(ANOTHER_MESSAGE), value(ANOTHER_MESSAGE), any()) + .with(value(AN_EVENT_ID.toEventOrTransactionId()), value(ANOTHER_MESSAGE), value(ANOTHER_MESSAGE), any()) assertThat(analyticsService.capturedEvents).containsExactly( Composer( @@ -407,7 +408,7 @@ class MessageComposerPresenterTest { @Test fun `present - edit sent message event not found`() = runTest { - val timelineEditMessageLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String, _: String?, _: List -> + val timelineEditMessageLambda = lambdaRecorder { _: EventOrTransactionId, _: String, _: String?, _: List -> Result.failure(TimelineException.EventNotFound) } val timeline = FakeTimeline().apply { @@ -448,7 +449,7 @@ class MessageComposerPresenterTest { assert(timelineEditMessageLambda) .isCalledOnce() - .with(value(AN_EVENT_ID), value(null), value(ANOTHER_MESSAGE), value(ANOTHER_MESSAGE), any()) + .with(value(AN_EVENT_ID.toEventOrTransactionId()), value(ANOTHER_MESSAGE), value(ANOTHER_MESSAGE), any()) assert(roomEditMessageLambda) .isCalledOnce() @@ -467,7 +468,7 @@ class MessageComposerPresenterTest { @Test fun `present - edit not sent message`() = runTest { - val editMessageLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String, _: String?, _: List -> + val editMessageLambda = lambdaRecorder { _: EventOrTransactionId, _: String, _: String?, _: List -> Result.success(Unit) } val timeline = FakeTimeline().apply { @@ -487,7 +488,7 @@ class MessageComposerPresenterTest { }.test { val initialState = awaitFirstItem() assertThat(initialState.textEditorState.messageHtml()).isEqualTo("") - val mode = anEditMode(eventId = null, transactionId = A_TRANSACTION_ID) + val mode = anEditMode(eventOrTransactionId = A_TRANSACTION_ID.toEventOrTransactionId()) initialState.eventSink.invoke(MessageComposerEvents.SetMode(mode)) val withMessageState = awaitItem() assertThat(withMessageState.mode).isEqualTo(mode) @@ -504,7 +505,7 @@ class MessageComposerPresenterTest { assert(editMessageLambda) .isCalledOnce() - .with(value(null), value(A_TRANSACTION_ID), value(ANOTHER_MESSAGE), value(ANOTHER_MESSAGE), any()) + .with(value(A_TRANSACTION_ID.toEventOrTransactionId()), value(ANOTHER_MESSAGE), value(ANOTHER_MESSAGE), any()) assertThat(analyticsService.capturedEvents).containsExactly( Composer( @@ -1058,7 +1059,7 @@ class MessageComposerPresenterTest { val replyMessageLambda = lambdaRecorder { _: EventId, _: String, _: String?, _: List, _: Boolean -> Result.success(Unit) } - val editMessageLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String, _: String?, _: List -> + val editMessageLambda = lambdaRecorder { _: EventOrTransactionId, _: String, _: String?, _: List -> Result.success(Unit) } val timeline = FakeTimeline().apply { @@ -1128,7 +1129,7 @@ class MessageComposerPresenterTest { assert(editMessageLambda) .isCalledOnce() - .with(any(), any(), any(), any(), value(listOf(IntentionalMention.User(A_USER_ID_3)))) + .with(any(), any(), any(), value(listOf(IntentionalMention.User(A_USER_ID_3)))) skipItems(1) } @@ -1516,10 +1517,9 @@ class MessageComposerPresenterTest { } fun anEditMode( - eventId: EventId? = AN_EVENT_ID, + eventOrTransactionId: EventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(), message: String = A_MESSAGE, - transactionId: TransactionId? = null, -) = MessageComposerMode.Edit(eventId, transactionId, message) +) = MessageComposerMode.Edit(eventOrTransactionId, message) fun aReplyMode() = MessageComposerMode.Reply( replyToDetails = InReplyToDetails.Loading(AN_EVENT_ID), diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/data/PollRepository.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/data/PollRepository.kt index 839adc492f..d40ccfa680 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/data/PollRepository.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/data/PollRepository.kt @@ -14,6 +14,7 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.TimelineProvider import io.element.android.libraries.matrix.api.timeline.getActiveTimeline import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import kotlinx.coroutines.flow.first import javax.inject.Inject @@ -63,8 +64,7 @@ class PollRepository @Inject constructor( timelineProvider .getActiveTimeline() .redactEvent( - eventId = pollStartId, - transactionId = null, + eventOrTransactionId = pollStartId.toEventOrTransactionId(), reason = null, ) } diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt index d3abb3d10d..f25707694a 100644 --- a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt @@ -20,10 +20,11 @@ import io.element.android.features.poll.impl.aPollTimelineItems import io.element.android.features.poll.impl.anOngoingPollContent import io.element.android.features.poll.impl.data.PollRepository import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.timeline.FakeTimeline @@ -466,7 +467,7 @@ class CreatePollPresenterTest { @Test fun `delete confirms`() = runTest { val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId)) - val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(Unit) } + val redactEventLambda = lambdaRecorder { _: EventOrTransactionId, _: String? -> Result.success(Unit) } timeline.redactEventLambda = redactEventLambda moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -481,7 +482,7 @@ class CreatePollPresenterTest { @Test fun `delete can be cancelled`() = runTest { val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId)) - val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(Unit) } + val redactEventLambda = lambdaRecorder { _: EventOrTransactionId, _: String? -> Result.success(Unit) } timeline.redactEventLambda = redactEventLambda moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -499,7 +500,7 @@ class CreatePollPresenterTest { @Test fun `delete can be confirmed`() = runTest { val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId)) - val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(Unit) } + val redactEventLambda = lambdaRecorder { _: EventOrTransactionId, _: String? -> Result.success(Unit) } timeline.redactEventLambda = redactEventLambda moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -512,7 +513,7 @@ class CreatePollPresenterTest { } assert(redactEventLambda) .isCalledOnce() - .with(value(pollEventId), value(null), any()) + .with(value(pollEventId.toEventOrTransactionId()), any()) } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index b8d6d66043..fcc1fd1812 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -14,7 +14,6 @@ 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.SessionId import io.element.android.libraries.matrix.api.core.TransactionId -import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange import io.element.android.libraries.matrix.api.media.AudioInfo @@ -29,6 +28,7 @@ import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerL import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.Timeline +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings import kotlinx.coroutines.flow.Flow @@ -150,7 +150,7 @@ interface MatrixRoom : Closeable { suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result - suspend fun toggleReaction(emoji: String, uniqueId: UniqueId): Result + suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result suspend fun forwardEvent(eventId: EventId, roomIds: List): Result 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 70d5a5ab57..085c4d49ea 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 @@ -11,7 +11,6 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.TransactionId -import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.api.media.ImageInfo @@ -20,7 +19,9 @@ import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.room.IntentionalMention import io.element.android.libraries.matrix.api.room.location.AssetType +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo +import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import java.io.File @@ -57,8 +58,7 @@ interface Timeline : AutoCloseable { ): Result suspend fun editMessage( - originalEventId: EventId?, - transactionId: TransactionId?, + eventOrTransactionId: EventOrTransactionId, body: String, htmlBody: String?, intentionalMentions: List, ): Result @@ -89,17 +89,18 @@ interface Timeline : AutoCloseable { progressCallback: ProgressCallback? ): Result - suspend fun redactEvent(eventId: EventId?, transactionId: TransactionId?, reason: String?): Result + suspend fun redactEvent(eventOrTransactionId: EventOrTransactionId, reason: String?): Result suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result - suspend fun toggleReaction(emoji: String, uniqueId: UniqueId): Result + suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result suspend fun forwardEvent(eventId: EventId, roomIds: List): Result - suspend fun cancelSend(transactionId: TransactionId): Result + suspend fun cancelSend(transactionId: TransactionId): Result = + redactEvent(transactionId.toEventOrTransactionId(), reason = null) /** * Share a location message in the room. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventOrTransactionId.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventOrTransactionId.kt new file mode 100644 index 0000000000..42f9c9198e --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventOrTransactionId.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api.timeline.item.event + +import androidx.compose.runtime.Immutable +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.TransactionId + +@Immutable +sealed interface EventOrTransactionId { + @JvmInline + value class Event(val id: EventId) : EventOrTransactionId + + @JvmInline + value class Transaction(val id: TransactionId) : EventOrTransactionId + + val eventId: EventId? + get() = (this as? Event)?.id + + companion object { + fun from(eventId: EventId?, transactionId: TransactionId?): EventOrTransactionId { + return when { + eventId != null -> Event(eventId) + transactionId != null -> Transaction(transactionId) + else -> throw IllegalArgumentException("EventId and TransactionId are both null") + } + } + } +} + +fun EventId.toEventOrTransactionId() = EventOrTransactionId.Event(this) +fun TransactionId.toEventOrTransactionId() = EventOrTransactionId.Transaction(this) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 7bc95471c3..31d8ae1f43 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -17,7 +17,6 @@ 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.SessionId import io.element.android.libraries.matrix.api.core.TransactionId -import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange import io.element.android.libraries.matrix.api.media.AudioInfo @@ -42,6 +41,7 @@ import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange import io.element.android.libraries.matrix.api.room.roomNotificationSettings import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.Timeline +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId 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.mapper.map @@ -471,8 +471,8 @@ class RustMatrixRoom( return liveTimeline.sendFile(file, fileInfo, progressCallback) } - override suspend fun toggleReaction(emoji: String, uniqueId: UniqueId): Result { - return liveTimeline.toggleReaction(emoji, uniqueId) + override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result { + return liveTimeline.toggleReaction(emoji, eventOrTransactionId) } override suspend fun forwardEvent(eventId: EventId, roomIds: List): Result { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/EventOrTransactionId.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/EventOrTransactionId.kt new file mode 100644 index 0000000000..85684faed0 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/EventOrTransactionId.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.timeline + +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId +import org.matrix.rustcomponents.sdk.EventOrTransactionId as RustEventOrTransactionId + +fun EventOrTransactionId.toRustEventOrTransactionId() = when (this) { + is EventOrTransactionId.Event -> RustEventOrTransactionId.EventId(id.value) + is EventOrTransactionId.Transaction -> RustEventOrTransactionId.TransactionId(id.value) +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt index 4096b87ba4..97cfc27457 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt @@ -23,7 +23,7 @@ class MatrixTimelineItemMapper( private val eventTimelineItemMapper: EventTimelineItemMapper, ) { fun map(timelineItem: TimelineItem): MatrixTimelineItem = timelineItem.use { - val uniqueId = UniqueId(timelineItem.uniqueId()) + val uniqueId = UniqueId(timelineItem.uniqueId().id) val asEvent = it.asEvent() if (asEvent != null) { val eventTimelineItem = eventTimelineItemMapper.map(asEvent) 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 517d83ff80..56c9cf4d85 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 @@ -10,8 +10,6 @@ package io.element.android.libraries.matrix.impl.timeline import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.TransactionId -import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.api.media.ImageInfo @@ -26,6 +24,7 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.TimelineException +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo import io.element.android.libraries.matrix.impl.core.toProgressWatcher import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl @@ -65,8 +64,6 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.EditedContent -import org.matrix.rustcomponents.sdk.EventOrTransactionId -import org.matrix.rustcomponents.sdk.EventTimelineItem import org.matrix.rustcomponents.sdk.FormattedBody import org.matrix.rustcomponents.sdk.MessageFormat import org.matrix.rustcomponents.sdk.PollData @@ -75,6 +72,7 @@ import org.matrix.rustcomponents.sdk.use import timber.log.Timber import uniffi.matrix_sdk_ui.LiveBackPaginationStatus import java.io.File +import org.matrix.rustcomponents.sdk.EventOrTransactionId as RustEventOrTransactionId import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline private const val PAGINATION_SIZE = 50 @@ -280,31 +278,23 @@ class RustTimeline( } } - override suspend fun redactEvent(eventId: EventId?, transactionId: TransactionId?, reason: String?): Result = withContext(dispatcher) { + override suspend fun redactEvent(eventOrTransactionId: EventOrTransactionId, reason: String?): Result = withContext(dispatcher) { runCatching { - val eventOrTransactionId = if (eventId != null) { - EventOrTransactionId.EventId(eventId.value) - } else { - EventOrTransactionId.TransactionId(transactionId!!.value) - } - inner.redactEvent(eventOrTransactionId = eventOrTransactionId, reason = reason) + inner.redactEvent( + eventOrTransactionId = eventOrTransactionId.toRustEventOrTransactionId(), + reason = reason, + ) } } override suspend fun editMessage( - originalEventId: EventId?, - transactionId: TransactionId?, + eventOrTransactionId: EventOrTransactionId, body: String, htmlBody: String?, intentionalMentions: List, ): Result = withContext(dispatcher) { runCatching { - val eventOrTransactionId = if (originalEventId != null) { - EventOrTransactionId.EventId(originalEventId.value) - } else { - EventOrTransactionId.TransactionId(transactionId!!.value) - } val editedContent = EditedContent.RoomMessage( content = MessageEventContent.from( body = body, @@ -314,7 +304,7 @@ class RustTimeline( ) inner.edit( newContent = editedContent, - eventOrTransactionId = eventOrTransactionId, + eventOrTransactionId = eventOrTransactionId.toRustEventOrTransactionId(), ) } } @@ -354,21 +344,6 @@ class RustTimeline( } } - @Throws - @Suppress("UnusedPrivateMember") - private suspend fun getEventTimelineItem(eventId: EventId?, transactionId: TransactionId?): EventTimelineItem { - return try { - when { - eventId != null -> inner.getEventTimelineItemByEventId(eventId.value) - transactionId != null -> inner.getEventTimelineItemByTransactionId(transactionId.value) - else -> error("Either eventId or transactionId must be non-null") - } - } catch (e: Exception) { - Timber.e(e, "Failed to get event timeline item") - throw TimelineException.EventNotFound - } - } - override suspend fun sendVideo( file: File, thumbnailFile: File?, @@ -410,9 +385,12 @@ class RustTimeline( } } - override suspend fun toggleReaction(emoji: String, uniqueId: UniqueId): Result = withContext(dispatcher) { + override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result = withContext(dispatcher) { runCatching { - inner.toggleReaction(key = emoji, uniqueId = uniqueId.value) + inner.toggleReaction( + key = emoji, + itemId = eventOrTransactionId.toRustEventOrTransactionId(), + ) } } @@ -424,9 +402,6 @@ class RustTimeline( } } - override suspend fun cancelSend(transactionId: TransactionId): Result = - redactEvent(eventId = null, transactionId = transactionId, reason = null) - override suspend fun sendLocation( body: String, geoUri: String, @@ -479,7 +454,7 @@ class RustTimeline( ) inner.edit( newContent = editedContent, - eventOrTransactionId = EventOrTransactionId.EventId(pollStartId.value), + eventOrTransactionId = RustEventOrTransactionId.EventId(pollStartId.value), ) }.map { } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustTimelineItem.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustTimelineItem.kt index f6d46799c3..4ce2c2e063 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustTimelineItem.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustTimelineItem.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.fixtures.fakes import org.matrix.rustcomponents.sdk.EventTimelineItem import org.matrix.rustcomponents.sdk.NoPointer import org.matrix.rustcomponents.sdk.TimelineItem +import org.matrix.rustcomponents.sdk.TimelineUniqueId import org.matrix.rustcomponents.sdk.VirtualTimelineItem class FakeRustTimelineItem( @@ -18,5 +19,5 @@ class FakeRustTimelineItem( override fun asEvent(): EventTimelineItem? = asEventResult override fun asVirtual(): VirtualTimelineItem? = null override fun fmtDebug(): String = "fmtDebug" - override fun uniqueId(): String = "uniqueId" + override fun uniqueId(): TimelineUniqueId = TimelineUniqueId("uniqueId") } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 2190f89d13..b46b45f20e 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -14,7 +14,6 @@ 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.SessionId import io.element.android.libraries.matrix.api.core.TransactionId -import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange import io.element.android.libraries.matrix.api.media.AudioInfo @@ -38,6 +37,7 @@ import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerL import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.Timeline +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -95,7 +95,7 @@ class FakeMatrixRoom( private val editMessageLambda: (EventId, String, String?, List) -> Result = { _, _, _, _ -> lambdaError() }, private val sendMessageResult: (String, String?, List) -> Result = { _, _, _ -> lambdaError() }, private val updateUserRoleResult: () -> Result = { lambdaError() }, - private val toggleReactionResult: (String, UniqueId) -> Result = { _, _ -> lambdaError() }, + private val toggleReactionResult: (String, EventOrTransactionId) -> Result = { _, _ -> lambdaError() }, private val retrySendMessageResult: (TransactionId) -> Result = { lambdaError() }, private val cancelSendResult: (TransactionId) -> Result = { lambdaError() }, private val forwardEventResult: (EventId, List) -> Result = { _, _ -> lambdaError() }, @@ -236,8 +236,8 @@ class FakeMatrixRoom( sendMessageResult(body, htmlBody, intentionalMentions) } - override suspend fun toggleReaction(emoji: String, uniqueId: UniqueId): Result { - return toggleReactionResult(emoji, uniqueId) + override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result { + return toggleReactionResult(emoji, eventOrTransactionId) } override suspend fun retrySendMessage(transactionId: TransactionId): Result = simulateLongTask { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt index d2a99df97b..0395bc2328 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt @@ -10,8 +10,6 @@ package io.element.android.libraries.matrix.test.timeline import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.TransactionId -import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.api.media.ImageInfo @@ -23,6 +21,7 @@ import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.Timeline +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.tests.testutils.lambda.lambdaError @@ -63,35 +62,31 @@ class FakeTimeline( intentionalMentions: List, ): Result = sendMessageLambda(body, htmlBody, intentionalMentions) - var redactEventLambda: (eventId: EventId?, transactionId: TransactionId?, reason: String?) -> Result = { _, _, _ -> + var redactEventLambda: (eventOrTransactionId: EventOrTransactionId, reason: String?) -> Result = { _, _ -> Result.success(Unit) } override suspend fun redactEvent( - eventId: EventId?, - transactionId: TransactionId?, + eventOrTransactionId: EventOrTransactionId, reason: String? - ): Result = redactEventLambda(eventId, transactionId, reason) + ): Result = redactEventLambda(eventOrTransactionId, reason) var editMessageLambda: ( - originalEventId: EventId?, - transactionId: TransactionId?, + eventOrTransactionId: EventOrTransactionId, body: String, htmlBody: String?, intentionalMentions: List, - ) -> Result = { _, _, _, _, _ -> + ) -> Result = { _, _, _, _ -> Result.success(Unit) } override suspend fun editMessage( - originalEventId: EventId?, - transactionId: TransactionId?, + eventOrTransactionId: EventOrTransactionId, body: String, htmlBody: String?, intentionalMentions: List, ): Result = editMessageLambda( - originalEventId, - transactionId, + eventOrTransactionId, body, htmlBody, intentionalMentions @@ -211,14 +206,15 @@ class FakeTimeline( progressCallback ) - var toggleReactionLambda: (emoji: String, uniqueId: UniqueId) -> Result = { _, _ -> Result.success(Unit) } - override suspend fun toggleReaction(emoji: String, uniqueId: UniqueId): Result = toggleReactionLambda(emoji, uniqueId) + var toggleReactionLambda: (emoji: String, eventOrTransactionId: EventOrTransactionId) -> Result = { _, _ -> Result.success(Unit) } + override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result = toggleReactionLambda( + emoji, + eventOrTransactionId + ) var forwardEventLambda: (eventId: EventId, roomIds: List) -> Result = { _, _ -> Result.success(Unit) } override suspend fun forwardEvent(eventId: EventId, roomIds: List): Result = forwardEventLambda(eventId, roomIds) - override suspend fun cancelSend(transactionId: TransactionId): Result = redactEvent(null, transactionId, null) - var sendLocationLambda: ( body: String, geoUri: String, 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 57cc810a7c..f7b19f9d9a 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 @@ -42,7 +42,8 @@ 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.Text import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.core.TransactionId +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId +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.testtags.TestTags @@ -719,12 +720,10 @@ fun aRichTextEditorState( ) fun aMessageComposerModeEdit( - eventId: EventId? = EventId("$1234"), - transactionId: TransactionId? = TransactionId("1234"), + eventOrTransactionId: EventOrTransactionId = EventId("$1234").toEventOrTransactionId(), content: String = "Some text", ) = MessageComposerMode.Edit( - eventId = eventId, - transactionId = transactionId, + eventOrTransactionId = eventOrTransactionId, content = content ) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt index 9c3d47453a..7f1efe55d2 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt @@ -26,6 +26,8 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.ui.strings.CommonStrings @@ -77,7 +79,7 @@ internal fun SendButton( @Composable internal fun SendButtonPreview() = ElementPreview { val normalMode = MessageComposerMode.Normal - val editMode = MessageComposerMode.Edit(null, null, "") + val editMode = MessageComposerMode.Edit(EventId("\$id").toEventOrTransactionId(), "") Row { SendButton(canSendMessage = true, onClick = {}, composerMode = normalMode) SendButton(canSendMessage = false, onClick = {}, composerMode = normalMode) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt index fafa65e64f..ef96000d2b 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt @@ -9,7 +9,7 @@ package io.element.android.libraries.textcomposer.model import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.core.TransactionId +import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import io.element.android.libraries.matrix.ui.messages.reply.eventId @@ -21,8 +21,7 @@ sealed interface MessageComposerMode { sealed interface Special : MessageComposerMode data class Edit( - val eventId: EventId?, - val transactionId: TransactionId?, + val eventOrTransactionId: EventOrTransactionId, val content: String ) : Special @@ -36,7 +35,7 @@ sealed interface MessageComposerMode { val relatedEventId: EventId? get() = when (this) { is Normal -> null - is Edit -> eventId + is Edit -> eventOrTransactionId.eventId is Reply -> eventId } From cced765581cd1192d865101f5ca55b7924982e7e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 18 Oct 2024 15:44:46 +0200 Subject: [PATCH 63/67] Add a check for screenshot name duplication. --- tools/test/generateAllScreenshots.py | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tools/test/generateAllScreenshots.py b/tools/test/generateAllScreenshots.py index 3c3850140a..5f7ffcf576 100755 --- a/tools/test/generateAllScreenshots.py +++ b/tools/test/generateAllScreenshots.py @@ -10,6 +10,7 @@ import os import re import sys import time + from util import compare @@ -100,6 +101,34 @@ def computeDarkFileName(lightFileName): return match.group(1) + "_Night_" + match.group(2) + "_" + match.group(3) return "" + +def checkForScreenshotNameDuplication(): + __doc__ = "Check for screenshots name duplication" + print("Check for screenshots name duplication...") + files = os.listdir("tests/uitests/src/test/snapshots/images/") + dict = {} + for file in files: + start = file.find("_") + 1 + end = file.find("_", start) + screenshotName = file[start:end] + if screenshotName in dict: + dict[screenshotName].append(file[:end]) + else: + dict[screenshotName] = [file[:end]] + error = 0 + for key in dict: + if key in ["Icon", "RoundIcon"]: + continue + values = set(dict[key]) + if len(values) > 1: + print("Duplicated screenshot name: %s" % key) + for value in values: + print(" - %s" % value) + error += 1 + if error: + print("Warning: %d duplicated screenshot name(s) found" % error) + + def generateJavascriptFile(): __doc__ = "Generate a javascript file to load the screenshots" print("Generating javascript file...") @@ -151,6 +180,7 @@ def generateJavascriptFile(): def main(): + checkForScreenshotNameDuplication() generateAllScreenshots(readArguments()) lang = detectLanguages() for l in lang: @@ -158,4 +188,5 @@ def main(): moveScreenshots(l) generateJavascriptFile() + main() From 7d6b859366cc2ca82843706b943c10ce82c2841e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 18 Oct 2024 15:47:59 +0200 Subject: [PATCH 64/67] Rename View and Preview to fix a name clash in the Gallery. --- .../features/messages/impl/actionlist/ActionListView.kt | 8 ++++---- .../components/reactionsummary/ReactionSummaryView.kt | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) 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 0b9377ae90..7d30edd116 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 @@ -146,7 +146,7 @@ fun ActionListView( onDismissRequest = ::onDismiss, modifier = modifier, ) { - SheetContent( + ActionListViewContent( state = state, onActionClick = ::onItemActionClick, onEmojiReactionClick = ::onEmojiReactionClick, @@ -161,7 +161,7 @@ fun ActionListView( } @Composable -private fun SheetContent( +private fun ActionListViewContent( state: ActionListState, onActionClick: (TimelineItemAction) -> Unit, onEmojiReactionClick: (String) -> Unit, @@ -442,10 +442,10 @@ private fun EmojiButton( @PreviewsDayNight @Composable -internal fun SheetContentPreview( +internal fun ActionListViewContentPreview( @PreviewParameter(ActionListStateProvider::class) state: ActionListState ) = ElementPreview { - SheetContent( + ActionListViewContent( state = state, onActionClick = {}, onEmojiReactionClick = {}, 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 ba3d7c4348..ee440e707d 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 @@ -89,14 +89,14 @@ fun ReactionSummaryView( sheetState = sheetState, modifier = modifier ) { - SheetContent(summary = state.target) + ReactionSummaryViewContent(summary = state.target) } } } @OptIn(ExperimentalFoundationApi::class) @Composable -private fun SheetContent( +private fun ReactionSummaryViewContent( summary: ReactionSummaryState.Summary, ) { val animationScope = rememberCoroutineScope() @@ -274,8 +274,8 @@ private fun SenderRow( @PreviewsDayNight @Composable -internal fun SheetContentPreview( +internal fun ReactionSummaryViewContentPreview( @PreviewParameter(ReactionSummaryStateProvider::class) state: ReactionSummaryState ) = ElementPreview { - SheetContent(summary = state.target as ReactionSummaryState.Summary) + ReactionSummaryViewContent(summary = state.target as ReactionSummaryState.Summary) } From c5ebbffdfcd2fc723df0d57e417417053d6826ad Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 18 Oct 2024 14:03:19 +0000 Subject: [PATCH 65/67] Update screenshots --- ...s.messages.impl.actionlist_ActionListViewContent_Day_0_en.png} | 0 ....messages.impl.actionlist_ActionListViewContent_Day_10_en.png} | 0 ....messages.impl.actionlist_ActionListViewContent_Day_11_en.png} | 0 ....messages.impl.actionlist_ActionListViewContent_Day_12_en.png} | 0 ...s.messages.impl.actionlist_ActionListViewContent_Day_1_en.png} | 0 ...s.messages.impl.actionlist_ActionListViewContent_Day_2_en.png} | 0 ...s.messages.impl.actionlist_ActionListViewContent_Day_3_en.png} | 0 ...s.messages.impl.actionlist_ActionListViewContent_Day_4_en.png} | 0 ...s.messages.impl.actionlist_ActionListViewContent_Day_5_en.png} | 0 ...s.messages.impl.actionlist_ActionListViewContent_Day_6_en.png} | 0 ...s.messages.impl.actionlist_ActionListViewContent_Day_7_en.png} | 0 ...s.messages.impl.actionlist_ActionListViewContent_Day_8_en.png} | 0 ...s.messages.impl.actionlist_ActionListViewContent_Day_9_en.png} | 0 ...messages.impl.actionlist_ActionListViewContent_Night_0_en.png} | 0 ...essages.impl.actionlist_ActionListViewContent_Night_10_en.png} | 0 ...essages.impl.actionlist_ActionListViewContent_Night_11_en.png} | 0 ...essages.impl.actionlist_ActionListViewContent_Night_12_en.png} | 0 ...messages.impl.actionlist_ActionListViewContent_Night_1_en.png} | 0 ...messages.impl.actionlist_ActionListViewContent_Night_2_en.png} | 0 ...messages.impl.actionlist_ActionListViewContent_Night_3_en.png} | 0 ...messages.impl.actionlist_ActionListViewContent_Night_4_en.png} | 0 ...messages.impl.actionlist_ActionListViewContent_Night_5_en.png} | 0 ...messages.impl.actionlist_ActionListViewContent_Night_6_en.png} | 0 ...messages.impl.actionlist_ActionListViewContent_Night_7_en.png} | 0 ...messages.impl.actionlist_ActionListViewContent_Night_8_en.png} | 0 ...messages.impl.actionlist_ActionListViewContent_Night_9_en.png} | 0 ...nents.reactionsummary_ReactionSummaryViewContent_Day_0_en.png} | 0 ...nts.reactionsummary_ReactionSummaryViewContent_Night_0_en.png} | 0 28 files changed, 0 insertions(+), 0 deletions(-) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_0_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_10_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_10_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_11_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_11_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_12_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_12_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_1_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_1_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_2_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_2_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_3_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_3_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_4_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_4_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_5_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_5_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_6_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_6_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_7_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_7_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_8_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_8_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Day_9_en.png => features.messages.impl.actionlist_ActionListViewContent_Day_9_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_0_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_10_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_10_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_11_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_11_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_12_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_12_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_1_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_1_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_2_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_2_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_3_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_3_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_4_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_4_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_5_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_5_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_6_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_6_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_7_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_7_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_8_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_8_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.actionlist_SheetContent_Night_9_en.png => features.messages.impl.actionlist_ActionListViewContent_Night_9_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.timeline.components.reactionsummary_SheetContent_Day_0_en.png => features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Day_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.messages.impl.timeline.components.reactionsummary_SheetContent_Night_0_en.png => features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Night_0_en.png} (100%) diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_0_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_10_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_10_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_10_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_11_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_11_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_11_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_12_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_12_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_12_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_1_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_1_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_1_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_2_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_2_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_3_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_3_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_3_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_4_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_4_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_4_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_5_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_5_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_5_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_6_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_6_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_6_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_7_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_7_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_7_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_8_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_8_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_8_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_9_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_9_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_9_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_0_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_10_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_10_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_10_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_11_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_11_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_11_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_12_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_12_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_12_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_1_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_1_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_1_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_2_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_2_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_3_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_3_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_3_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_4_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_4_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_4_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_5_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_5_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_5_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_6_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_6_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_6_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_7_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_7_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_7_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_8_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_8_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_8_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_9_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_9_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_9_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.reactionsummary_SheetContent_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Day_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.reactionsummary_SheetContent_Day_0_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Day_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.reactionsummary_SheetContent_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Night_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.reactionsummary_SheetContent_Night_0_en.png rename to tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Night_0_en.png From b0f4d14e6f68c9f7e29429b61f078471cf59a9ff Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 18 Oct 2024 19:44:22 +0000 Subject: [PATCH 66/67] Update dependencyAnalysis to v2.2.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 4f6dd3bc93..57268bf95c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -40,7 +40,7 @@ test_core = "1.6.1" #other coil = "2.7.0" datetime = "0.6.0" -dependencyAnalysis = "2.1.4" +dependencyAnalysis = "2.2.0" serialization_json = "1.6.3" showkase = "1.0.3" appyx = "1.4.0" From f19ea02390d90fc4668a7f4043cd9dfce3711789 Mon Sep 17 00:00:00 2001 From: bmarty <3940906+bmarty@users.noreply.github.com> Date: Mon, 21 Oct 2024 00:27:20 +0000 Subject: [PATCH 67/67] Sync Strings from Localazy --- .../src/main/res/values-el/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 4 +- .../src/main/res/values-pt/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 12 +- .../src/main/res/values-be/translations.xml | 7 + .../src/main/res/values-ru/translations.xml | 2 +- .../src/main/res/values-pt/translations.xml | 6 +- .../impl/src/main/res/values/localazy.xml | 2 +- .../src/main/res/values-ru/translations.xml | 4 +- .../src/main/res/values-ru/translations.xml | 16 +- .../src/main/res/values-be/translations.xml | 1 + .../src/main/res/values-el/translations.xml | 1 + .../src/main/res/values-pt/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 15 +- .../src/main/res/values-ru/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 10 +- .../src/main/res/values-ru/translations.xml | 8 +- .../src/main/res/values-ru/translations.xml | 14 +- .../src/main/res/values-pt/translations.xml | 4 +- .../src/main/res/values-ru/translations.xml | 40 +- .../src/main/res/values-el/translations.xml | 2 +- .../src/main/res/values-pt/translations.xml | 6 +- .../src/main/res/values-ru/translations.xml | 12 +- .../src/main/res/values-ru/translations.xml | 4 +- .../src/main/res/values-ru/translations.xml | 2 +- .../src/main/res/values-cs/translations.xml | 5 + .../src/main/res/values-et/translations.xml | 5 + .../src/main/res/values-fr/translations.xml | 3 + .../src/main/res/values-hu/translations.xml | 5 + .../src/main/res/values-pt/translations.xml | 4 + .../src/main/res/values-ru/translations.xml | 5 + .../impl/src/main/res/values/localazy.xml | 5 + .../src/main/res/values-ru/translations.xml | 8 +- .../src/main/res/values-be/translations.xml | 6 + .../src/main/res/values-cs/translations.xml | 15 + .../src/main/res/values-el/translations.xml | 9 + .../src/main/res/values-et/translations.xml | 15 + .../src/main/res/values-fr/translations.xml | 11 + .../src/main/res/values-hu/translations.xml | 15 + .../src/main/res/values-pt/translations.xml | 20 +- .../src/main/res/values-ru/translations.xml | 66 +- .../src/main/res/values/localazy.xml | 13 + ...nlist_ActionListViewContent_Day_10_de.png} | 0 ...nlist_ActionListViewContent_Day_11_de.png} | 0 ...nlist_ActionListViewContent_Day_12_de.png} | 0 ...onlist_ActionListViewContent_Day_2_de.png} | 0 ...ionlist_ActionListViewContent_Day_3_de.png | 3 + ...onlist_ActionListViewContent_Day_4_de.png} | 0 ...onlist_ActionListViewContent_Day_5_de.png} | 0 ...onlist_ActionListViewContent_Day_6_de.png} | 0 ...onlist_ActionListViewContent_Day_7_de.png} | 0 ...onlist_ActionListViewContent_Day_8_de.png} | 0 ...onlist_ActionListViewContent_Day_9_de.png} | 0 ....impl.actionlist_SheetContent_Day_3_de.png | 3 - ...ntity_IdentityChangeStateView_Day_1_de.png | 4 +- ...ntity_IdentityChangeStateView_Day_2_de.png | 3 + ...essagesViewWithIdentityChange_Day_1_de.png | 4 +- ...essagesViewWithIdentityChange_Day_2_de.png | 3 + ...ion_RoomMembersModerationView_Day_5_de.png | 3 - ...textcomposer_ComposerModeView_Day_0_de.png | 3 + ...mposer_TextComposerFormatting_Day_0_de.png | 4 +- ...xtcomposer_TextComposerReply_Day_11_de.png | 4 +- ...extcomposer_TextComposerReply_Day_1_de.png | 4 +- ...xtcomposer_TextComposerSimple_Day_0_de.png | 4 +- screenshots/html/data.js | 1244 +++++++++-------- ...es.joinroom.impl_JoinRoomView_Day_4_en.png | 4 +- ....joinroom.impl_JoinRoomView_Night_4_en.png | 4 +- 69 files changed, 933 insertions(+), 754 deletions(-) rename screenshots/de/{features.messages.impl.actionlist_SheetContent_Day_10_de.png => features.messages.impl.actionlist_ActionListViewContent_Day_10_de.png} (100%) rename screenshots/de/{features.messages.impl.actionlist_SheetContent_Day_11_de.png => features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png} (100%) rename screenshots/de/{features.messages.impl.actionlist_SheetContent_Day_12_de.png => features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png} (100%) rename screenshots/de/{features.messages.impl.actionlist_SheetContent_Day_2_de.png => features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png} (100%) create mode 100644 screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png rename screenshots/de/{features.messages.impl.actionlist_SheetContent_Day_4_de.png => features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png} (100%) rename screenshots/de/{features.messages.impl.actionlist_SheetContent_Day_5_de.png => features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png} (100%) rename screenshots/de/{features.messages.impl.actionlist_SheetContent_Day_6_de.png => features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png} (100%) rename screenshots/de/{features.messages.impl.actionlist_SheetContent_Day_7_de.png => features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png} (100%) rename screenshots/de/{features.messages.impl.actionlist_SheetContent_Day_8_de.png => features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png} (100%) rename screenshots/de/{features.messages.impl.actionlist_SheetContent_Day_9_de.png => features.messages.impl.actionlist_ActionListViewContent_Day_9_de.png} (100%) delete mode 100644 screenshots/de/features.messages.impl.actionlist_SheetContent_Day_3_de.png create mode 100644 screenshots/de/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_de.png create mode 100644 screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_de.png delete mode 100644 screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_de.png create mode 100644 screenshots/de/libraries.textcomposer_ComposerModeView_Day_0_de.png diff --git a/appnav/src/main/res/values-el/translations.xml b/appnav/src/main/res/values-el/translations.xml index 42925c0b6f..ef9f8d3c7d 100644 --- a/appnav/src/main/res/values-el/translations.xml +++ b/appnav/src/main/res/values-el/translations.xml @@ -1,5 +1,5 @@ - "Αποσύνδεση &amp; Αναβάθμιση" + "Αποσύνδεση & Αναβάθμιση" "Ο οικιακός διακομιστής σου δεν υποστηρίζει πλέον το παλιό πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για να συνεχίσεις να χρησιμοποιείς την εφαρμογή." diff --git a/appnav/src/main/res/values-ru/translations.xml b/appnav/src/main/res/values-ru/translations.xml index df600ea045..ac03f18098 100644 --- a/appnav/src/main/res/values-ru/translations.xml +++ b/appnav/src/main/res/values-ru/translations.xml @@ -1,5 +1,5 @@ "Выйти и обновить" - "Ваш homeserver больше не поддерживает старый протокол. Пожалуйста, выйдите из системы и войдите снова, чтобы продолжить использование приложения." + "Ваш домашний сервер больше не поддерживает старый протокол. Пожалуйста, выйдите и войдите в свою учётную запись снова, чтобы продолжить использование приложения." diff --git a/features/analytics/api/src/main/res/values-ru/translations.xml b/features/analytics/api/src/main/res/values-ru/translations.xml index 305d98f498..b97c069a85 100644 --- a/features/analytics/api/src/main/res/values-ru/translations.xml +++ b/features/analytics/api/src/main/res/values-ru/translations.xml @@ -1,7 +1,7 @@ - "Предоставлять анонимные данные об использовании, чтобы помочь нам выявить проблемы." + "Предоставьте разработчикам анонимные данные об использовании, чтобы помочь им выявлять проблемы эффективнее." "Вы можете ознакомиться со всеми нашими условиями %1$s." "здесь" - "Делитесь данными аналитики" + "Отправлять аналитические данные" diff --git a/features/analytics/impl/src/main/res/values-pt/translations.xml b/features/analytics/impl/src/main/res/values-pt/translations.xml index 1a837d6313..d98f020799 100644 --- a/features/analytics/impl/src/main/res/values-pt/translations.xml +++ b/features/analytics/impl/src/main/res/values-pt/translations.xml @@ -4,7 +4,7 @@ "Partilhe dados de utilização anónimos para nos ajudar a identificar problemas." "Podes ler todos os nossos termos %1$s." "aqui" - "Podes desligar qualquer momento" + "Pode desactivar a qualquer momento" "Não partilharemos os teus dados com terceiros" "Ajude a melhorar a %1$s" diff --git a/features/analytics/impl/src/main/res/values-ru/translations.xml b/features/analytics/impl/src/main/res/values-ru/translations.xml index 805cba6bf2..4212f5fd0f 100644 --- a/features/analytics/impl/src/main/res/values-ru/translations.xml +++ b/features/analytics/impl/src/main/res/values-ru/translations.xml @@ -1,7 +1,7 @@ "Мы не будем записывать или профилировать какие-либо персональные данные" - "Предоставлять анонимные данные об использовании, чтобы помочь нам выявить проблемы." + "Предоставьте разработчикам анонимные данные об использовании, чтобы помочь им выявлять проблемы эффективнее." "Вы можете ознакомиться со всеми нашими условиями %1$s." "здесь" "Вы можете отключить эту функцию в любое время" diff --git a/features/createroom/impl/src/main/res/values-ru/translations.xml b/features/createroom/impl/src/main/res/values-ru/translations.xml index 42028b98f7..1bb1206aad 100644 --- a/features/createroom/impl/src/main/res/values-ru/translations.xml +++ b/features/createroom/impl/src/main/res/values-ru/translations.xml @@ -1,14 +1,14 @@ - "Новая комната" + "Создать новую комнату" "Пригласить в комнату" "Произошла ошибка при создании комнаты" - "Сообщения в этой комнате зашифрованы. Отключить шифрование позже будет невозможно." - "Приватная комната (только по приглашению)" - "Сообщения не зашифрованы, каждый может их прочитать. Вы можете включить шифрование позже." - "Публичная комната (любой)" + "Сообщения в этой комнате будут зашифрованы. Отключить шифрование позже будет невозможно." + "Частная комната (только по приглашениям)" + "Сообщения не будут зашифрованы и каждый сможет их прочитать. Шифрование можно будет включить позже." + "Общедоступная комната (для всех)" "Название комнаты" "Создать комнату" "Тема (необязательно)" - "Произошла ошибка при попытке открытия комнаты" + "Произошла ошибка при запуске чата" diff --git a/features/deactivation/impl/src/main/res/values-be/translations.xml b/features/deactivation/impl/src/main/res/values-be/translations.xml index 38037dce6c..f950e4eed6 100644 --- a/features/deactivation/impl/src/main/res/values-be/translations.xml +++ b/features/deactivation/impl/src/main/res/values-be/translations.xml @@ -1,4 +1,11 @@ + "Калі ласка, пацвердзіце, што вы хочаце дэактываваць свой уліковы запіс. Гэта дзеянне нельга адмяніць." + "Выдаліць усе мае паведамленні" + "Увага: будучыя карыстальнікі могуць бачыць няпоўныя размовы." + "незваротны" + "Назаўсёды адключыць" + "Выдаліць вас з усіх чатаў." + "Выдаліце інфармацыю аб сваім уліковым запісе з нашага сервера ідэнтыфікацыі." "Дэактываваць уліковы запіс" diff --git a/features/ftue/impl/src/main/res/values-ru/translations.xml b/features/ftue/impl/src/main/res/values-ru/translations.xml index 6ddceef57f..7a5ed89a21 100644 --- a/features/ftue/impl/src/main/res/values-ru/translations.xml +++ b/features/ftue/impl/src/main/res/values-ru/translations.xml @@ -1,7 +1,7 @@ "Вы можете изменить настройки позже." - "Разрешите уведомления и никогда не пропустите сообщение" + "Разрешите отправку уведомлений и ни одно сообщение не будет пропущено" "Звонки, опросы, поиск и многое другое будут добавлены позже в этом году." "История сообщений для зашифрованных комнат в этом обновлении будет недоступна." "Мы будем рады услышать ваше мнение, сообщите нам об этом через страницу настроек." diff --git a/features/invite/impl/src/main/res/values-pt/translations.xml b/features/invite/impl/src/main/res/values-pt/translations.xml index 1bdde3eaee..a7f257261a 100644 --- a/features/invite/impl/src/main/res/values-pt/translations.xml +++ b/features/invite/impl/src/main/res/values-pt/translations.xml @@ -1,8 +1,8 @@ - "Tens a certeza que queres rejeitar o convite para %1$s?" - "Rejeitar conite" - "Tens a certeza que queres rejeitar esta conversa privada com %1$s?" + "Tens a certeza que queres rejeitar o convite para entra em %1$s?" + "Rejeitar convite" + "Tem a certeza que queres rejeitar esta conversa privada com %1$s?" "Rejeitar conversa" "Sem convites" "%1$s (%2$s) convidou-te" diff --git a/features/joinroom/impl/src/main/res/values/localazy.xml b/features/joinroom/impl/src/main/res/values/localazy.xml index 103d512970..86ed3c0cc3 100644 --- a/features/joinroom/impl/src/main/res/values/localazy.xml +++ b/features/joinroom/impl/src/main/res/values/localazy.xml @@ -1,7 +1,7 @@ "Join room" - "Knock to join" + "Send request to join" "%1$s does not support spaces yet. You can access spaces on web." "Spaces are not supported yet" "Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved." diff --git a/features/leaveroom/api/src/main/res/values-ru/translations.xml b/features/leaveroom/api/src/main/res/values-ru/translations.xml index c6faa299a2..ebf7ac82cd 100644 --- a/features/leaveroom/api/src/main/res/values-ru/translations.xml +++ b/features/leaveroom/api/src/main/res/values-ru/translations.xml @@ -1,7 +1,7 @@ - "Вы уверены, что хотите покинуть беседу?" + "Вы уверены, что хотите покинуть беседу? Эта беседа не является общедоступной, и Вы не сможете присоединиться к ней без приглашения." "Вы уверены, что хотите покинуть эту комнату? Вы здесь единственный человек. Если вы уйдете, никто не сможет присоединиться в будущем, включая вас." - "Вы уверены, что хотите покинуть эту комнату? Эта комната не является публичной, и Вы не сможете присоединиться к ней без приглашения." + "Вы уверены, что хотите покинуть эту комнату? Эта комната не является общедоступной, и Вы не сможете присоединиться к ней без приглашения." "Вы уверены, что хотите покинуть комнату?" diff --git a/features/lockscreen/impl/src/main/res/values-ru/translations.xml b/features/lockscreen/impl/src/main/res/values-ru/translations.xml index dd9deb0ee1..a70ff5015c 100644 --- a/features/lockscreen/impl/src/main/res/values-ru/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-ru/translations.xml @@ -4,7 +4,7 @@ "биометрическая разблокировка" "Разблокировать с помощью биометрии" "Забыли PIN-код?" - "Измените PIN-код" + "Изменить PIN-код" "Разрешить биометрическую разблокировку" "Удалить PIN-код" "Вы действительно хотите удалить PIN-код?" @@ -22,16 +22,16 @@ "Повторите PIN-код" "PIN-коды не совпадают" "Чтобы продолжить, вам необходимо повторно войти в систему и создать новый PIN-код" - "Вы выходите из системы" + "Выполняется выход из системы" - "Вы попытались разблокировать %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/login/impl/src/main/res/values-be/translations.xml b/features/login/impl/src/main/res/values-be/translations.xml index 16efe0b3b9..e4276c99b8 100644 --- a/features/login/impl/src/main/res/values-be/translations.xml +++ b/features/login/impl/src/main/res/values-be/translations.xml @@ -60,6 +60,7 @@ "Выберыце %1$s" "“Звязаць новую прыладу”" "Адсканіруйце QR-код з дапамогай гэтай прылады" + "Даступна толькі ў тым выпадку, калі ваш правайдар уліковага запісу гэта падтрымлівае." "Адкрыйце %1$s на іншай прыладзе, каб атрымаць QR-код" "Выкарыстоўвайце QR-код, паказаны на іншай прыладзе." "Паўтарыць спробу" diff --git a/features/login/impl/src/main/res/values-el/translations.xml b/features/login/impl/src/main/res/values-el/translations.xml index b0ad469732..532ee17023 100644 --- a/features/login/impl/src/main/res/values-el/translations.xml +++ b/features/login/impl/src/main/res/values-el/translations.xml @@ -60,6 +60,7 @@ "Επιλογή %1$s" "«Σύνδεση νέας συσκευής»" "Σάρωσε τον κωδικό QR με αυτήν τη συσκευή" + "Διατίθεται μόνο εάν ο πάροχος του λογαριασμού σου το υποστηρίζει." "Άνοιγμα %1$s σε άλλη συσκευή για να λήψη κωδικού QR" "Χρησιμοποίησε τον κωδικό QR που εμφανίζεται στην άλλη συσκευή." "Προσπάθησε ξανά" diff --git a/features/login/impl/src/main/res/values-pt/translations.xml b/features/login/impl/src/main/res/values-pt/translations.xml index 101117c588..d5489c90f7 100644 --- a/features/login/impl/src/main/res/values-pt/translations.xml +++ b/features/login/impl/src/main/res/values-pt/translations.xml @@ -60,6 +60,7 @@ Tenta iniciar a sessão manualmente ou digitaliza o código QR com outro disposi "Seleciona %1$s" "“Ligar novo dispositivo”" "Lê o código QR com este dispositivo" + "Disponível apenas se o seu fornecedor de conta o suportar." "Abre a %1$s noutro dispositivo para obteres o código QR" "Lê o código QR apresentado no outro dispositivo." "Tentar novamente" diff --git a/features/login/impl/src/main/res/values-ru/translations.xml b/features/login/impl/src/main/res/values-ru/translations.xml index dad21faf5f..4dc20339e1 100644 --- a/features/login/impl/src/main/res/values-ru/translations.xml +++ b/features/login/impl/src/main/res/values-ru/translations.xml @@ -5,9 +5,9 @@ "Введите поисковый запрос или адрес домена." "Поиск компании, сообщества или частного сервера." "Поиск сервера учетной записи" - "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." + "Здесь будут храниться ваши разговоры — точно так же, как если бы вы использовали почтового провайдера для хранения своих писем." "Вы собираетесь войти в %s" - "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." + "Здесь будут храниться ваши разговоры — точно так же, как если бы вы использовали почтового провайдера для хранения своих писем." "Вы собираетесь создать учетную запись на %s" "Matrix.org — это большой бесплатный сервер в общедоступной сети Matrix для безопасной децентрализованной связи, управляемый Matrix.org Foundation." "Другое" @@ -27,14 +27,14 @@ "Это не корректный идентификатор пользователя. Ожидаемый формат: \'@user:homeserver.org\'" "Этот сервер настроен на использование токенов обновления. Они не поддерживаются при использовании входа на основе пароля." "Выбранный домашний сервер не поддерживает пароль или логин OIDC. Пожалуйста, свяжитесь с администратором или выберите другой домашний сервер." - "Введите сведения о себе" + "Введите свои данные" "Matrix — это открытая сеть для безопасной децентрализованной связи." "Рады видеть вас снова!" "Войти в %1$s" "Установление безопасного соединения" "Не удалось установить безопасное соединение с новым устройством. Существующие устройства по-прежнему в безопасности, и вам не нужно беспокоиться о них." "Что теперь?" - "Попробуйте снова войти в систему с помощью QR-кода, если это была сетевая проблема" + "Попробуйте снова войти в систему с помощью QR-кода, если это была проблема с соединением" "Если вы столкнулись с той же проблемой, попробуйте сменить точку доступа Wi-Fi или используйте мобильные данные" "Если это не помогло, войдите вручную" "Соединение не защищено" @@ -55,11 +55,12 @@ "Поставщик учетной записи не поддерживает %1$s." "%1$s не поддерживается" "Готово к сканированию" - "Откройте %1$s на настольном устройстве" + "Откройте %1$s на компьютере" "Нажмите на свое изображение" - "Выбрать %1$s" + "Выберите %1$s" "\"Привязать новое устройство\"" "Отсканируйте QR-код с помощью этого устройства" + "Доступно только в том случае, если ваш поставщик учетной записи поддерживает это." "Откройте %1$s на другом устройстве, чтобы получить QR-код" "Используйте QR-код, показанный на другом устройстве." "Повторить попытку" @@ -76,7 +77,7 @@ "Сменить поставщика учетной записи" "Частный сервер для сотрудников Element." "Matrix — это открытая сеть для безопасной децентрализованной связи." - "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." + "Здесь будут храниться ваши разговоры — точно так же, как если бы вы использовали почтового провайдера для хранения своих писем." "Вы собираетесь войти в %1$s" "Вы собираетесь создать учетную запись на %1$s" diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml index 28ca6271d9..7845607e15 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -25,7 +25,7 @@ "Хотите пригласить их снова?" "Вы одни в этой комнате" "Уведомить всю комнату" - "Для всех" + "Все" "Отправить снова" "Не удалось отправить ваше сообщение" "Добавить эмодзи" diff --git a/features/onboarding/impl/src/main/res/values-ru/translations.xml b/features/onboarding/impl/src/main/res/values-ru/translations.xml index 201687b0cf..80b05a158f 100644 --- a/features/onboarding/impl/src/main/res/values-ru/translations.xml +++ b/features/onboarding/impl/src/main/res/values-ru/translations.xml @@ -1,9 +1,9 @@ - "Вход в систему вручную" - "Войти с помощью QR-кода" + "Войти вручную" + "Войти QR-кодом" "Создать учетную запись" - "Добро пожаловать в самый быстрый %1$s. Сверхзаряженность на скорость и простоту." - "Добро пожаловать в %1$s. Сверхзаряжен для скорости и простоты." - "Будьте в своем element" + "Добро пожаловать в самый быстрый клиент %1$s. Ориентирован на скорость и простоту." + "Добро пожаловать в %1$s. Ориентирован на скорость и простоту." + "Чувствуйте себя как дома с Element" diff --git a/features/poll/impl/src/main/res/values-ru/translations.xml b/features/poll/impl/src/main/res/values-ru/translations.xml index 2ed319bbeb..dbc7a49e5e 100644 --- a/features/poll/impl/src/main/res/values-ru/translations.xml +++ b/features/poll/impl/src/main/res/values-ru/translations.xml @@ -2,17 +2,17 @@ "Добавить вариант" "Показывать результаты только после окончания опроса" - "Анонимный опрос" - "Настройка %1$d" + "Скрыть голоса" + "Вариант %1$d" "Изменения не сохранены. Вы действительно хотите вернуться?" "Вопрос или тема" - "Тема опроса?" + "О чём будет опрос?" "Создать опрос" "Вы уверены, что хотите удалить этот опрос?" "Удалить опрос" "Редактировать опрос" "Не найдено текущих опросов." - "Не найдено предыдущих опросов." + "Не найдено прошлых опросов." "Текущие" "Прошлые" "Опросы" diff --git a/features/preferences/impl/src/main/res/values-ru/translations.xml b/features/preferences/impl/src/main/res/values-ru/translations.xml index fdd2b54652..ffe52769bf 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -1,6 +1,6 @@ - "Чтобы никогда не пропустить важный звонок, измените настройки, чтобы разрешить полноэкранные уведомления, когда ваш телефон заблокирован." + "Чтобы больше не пропускать важные звонки, разрешите приложению показывать полноэкранные уведомления на заблокированном экране телефона." "Улучшите качество звонков" "Выберите способ получения уведомлений" "Режим разработчика" @@ -32,23 +32,23 @@ "Мы упростили настройки уведомлений, чтобы упростить поиск опций. Некоторые пользовательские настройки, выбранные вами ранее, не отображаются в данном меню, но они все еще активны. Если вы продолжите, некоторые настройки могут быть изменены." - "Прямые чаты" + "В личных чатах" "Персональные настройки для каждого чата" - "При обновлении настроек уведомления произошла ошибка." - "Все сообщения" + "Произошла ошибка при обновлении настройки уведомления." + "О всех сообщениях" "Только упоминания и ключевые слова" "Уведомлять меня в личных чатах" "Уведомлять меня в групповых чатах" "Включить уведомления на данном устройстве" "Конфигурация не была исправлена, попробуйте еще раз." - "Групповые чаты" + "В групповых чатах" "Приглашения" "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, в некоторых комнатах вы можете не получать уведомления." "Упоминания" "Все" "Упоминания" - "Уведомить меня" - "Уведомить меня в @room" + "Уведомлять меня" + "Уведомлять меня при упоминании @room" "Чтобы получать уведомления, измените свой %1$s." "настройки системы" "Системные уведомления выключены" 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 f7b3adf14f..5dde97ec98 100644 --- a/features/roomdetails/impl/src/main/res/values-pt/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pt/translations.xml @@ -17,7 +17,7 @@ "Altera o nome da sala" "Alterar a descrição da sala" "Enviar mensagens" - "Editar administradores" + "Editar Administradores" "Não poderás desfazer esta ação. Estás a promover o utilizador para ter o mesmo nível de poder que tu." "Adicionar administrador?" "Despromover" @@ -26,7 +26,7 @@ "%1$s (pendente)" "(pendente)" "Os administradores têm automaticamente privilégios de moderador" - "Editar moderadores" + "Editar Moderadores" "Administradores" "Moderadores" "Participantes" diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml index f9f9a2d06b..574223a6ec 100644 --- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml @@ -1,22 +1,22 @@ - "При обновлении настроек уведомления произошла ошибка." + "Произошла ошибка при обновлении настройки уведомления." "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, в некоторых комнатах вы можете не получать уведомления." "Опросы" - "Только для администраторов" - "Заблокировать людей" - "Удалить сообщения" - "Для всех" - "Пригласить людей" + "Только администраторы" + "Блокировать людей могут" + "Удалять сообщения могут" + "Все" + "Приглашать людей могут" "Модерация участников" "Сообщения и содержание" "Администраторы и модераторы" - "Удалить людей" - "Изменить изображение комнаты" + "Удалять людей могут" + "Менять изображение комнаты могут" "Информация о комнате" - "Изменить название комнаты" - "Сменить тему комнаты" - "Отправить сообщение" + "Менять название комнаты могут" + "Менять тему комнаты могут" + "Отправлять сообщения могут" "Редактировать роль администраторов" "Вы не сможете отменить это действие. Вы устанавливаете уровень пользователю соответствующий вашему." "Добавить администратора?" @@ -36,14 +36,14 @@ "Уже зарегистрирован" "Уже приглашены" "Зашифровано" - "Не зашифровано" - "Общественная комната" + "Шифрования нет" + "Общедоступная комната" "Редактировать комнату" "Произошла неизвестная ошибка и информацию не удалось изменить." "Не удалось обновить комнату" "Сообщения зашифрованы. Только у вас и у получателей есть уникальные ключи для их разблокировки." "Шифрование сообщений включено" - "При загрузке настроек уведомлений произошла ошибка." + "Произошла ошибка при загрузке настроек уведомлений." "Не удалось отключить звук в этой комнате, попробуйте еще раз." "Не удалось включить звук в эту комнату, попробуйте еще раз." "Пригласить в комнату" @@ -78,7 +78,7 @@ "Они снова смогут присоединиться в эту комнату если их пригласят." "Разбанить пользователя?" "Посмотреть профиль" - "Заблокирован" + "Заблокированные" "Участники" "В ожидании" "Удаление %1$s…" @@ -88,7 +88,7 @@ "Разблокировка %1$s" "Разрешить пользовательские настройки" "Включение этого параметра отменяет настройки по умолчанию" - "Уведомить меня в этом чате" + "Уведомлять меня в этом чате" "Вы можете изменить его в своем %1$s." "основные настройки" "Настройка по умолчанию" @@ -97,19 +97,19 @@ "Не удалось восстановить режим по умолчанию, попробуйте еще раз." "Не удалось настроить режим, попробуйте еще раз." "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, вы не будете получать уведомления в этой комнате." - "Все сообщения" + "О всех сообщениях" "Только упоминания и ключевые слова" "В этой комнате уведомить меня о" "Администраторы" - "Измените мою роль" - "Понижение до участника" + "Изменить мою роль" + "Понизить до участника" "Понизить до модератора" "Модерация участников" "Сообщения и содержание" "Модераторы" "Разрешения" "Сбросить разрешения" - "Как только вы сбросите разрешения, вы потеряете текущие настройки." + "Как только вы сбросите разрешения, все текущие настройки будут утеряны." "Сбросить разрешения?" "Роли" "Информация о комнате" diff --git a/features/roomlist/impl/src/main/res/values-el/translations.xml b/features/roomlist/impl/src/main/res/values-el/translations.xml index a7e2a0f79f..2ace716541 100644 --- a/features/roomlist/impl/src/main/res/values-el/translations.xml +++ b/features/roomlist/impl/src/main/res/values-el/translations.xml @@ -1,6 +1,6 @@ - "Αποσύνδεση &amp; Αναβάθμιση" + "Αποσύνδεση & Αναβάθμιση" "Ο διακομιστής σου υποστηρίζει τώρα ένα νέο, ταχύτερο πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για αναβάθμιση τώρα. Κάνοντας αυτό τώρα θα σε βοηθήσει να αποφύγεις μια αναγκαστική αποσύνδεση όταν το παλιό πρωτόκολλο καταργηθεί αργότερα." "Ο οικιακός διακομιστής σου δεν υποστηρίζει πλέον το παλιό πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για να συνεχίσεις να χρησιμοποιείς την εφαρμογή." "Διαθέσιμη αναβάθμιση" diff --git a/features/roomlist/impl/src/main/res/values-pt/translations.xml b/features/roomlist/impl/src/main/res/values-pt/translations.xml index f73a6bd681..02e716e4ec 100644 --- a/features/roomlist/impl/src/main/res/values-pt/translations.xml +++ b/features/roomlist/impl/src/main/res/values-pt/translations.xml @@ -10,9 +10,9 @@ "Insere a tua chave de recuperação" "Para garantir que nunca perdes uma chamada importante, altera as configurações para permitir notificações em ecrã inteiro quando o telemóvel está bloqueado." "Melhora a tua experiência de chamada" - "Tens a certeza que queres rejeitar o convite para %1$s?" - "Rejeitar conite" - "Tens a certeza que queres rejeitar esta conversa privada com %1$s?" + "Tens a certeza que queres rejeitar o convite para entra em %1$s?" + "Rejeitar convite" + "Tem a certeza que queres rejeitar esta conversa privada com %1$s?" "Rejeitar conversa" "Sem convites" "%1$s (%2$s) convidou-te" diff --git a/features/roomlist/impl/src/main/res/values-ru/translations.xml b/features/roomlist/impl/src/main/res/values-ru/translations.xml index a057f9284d..c5cb31c29b 100644 --- a/features/roomlist/impl/src/main/res/values-ru/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ru/translations.xml @@ -1,17 +1,17 @@ "Выйти и обновить" - "Теперь ваш сервер поддерживает новый, более быстрый протокол. Выйдите из системы и снова войдите в систему для обновления прямо сейчас. Сделав это сейчас, вы сможете избежать принудительного выхода из системы при последующем удалении старого протокола." - "Ваш homeserver больше не поддерживает старый протокол. Пожалуйста, выйдите из системы и войдите снова, чтобы продолжить использование приложения." + "Теперь ваш сервер поддерживает новый, более быстрый протокол. Чтобы обновить его прямо сейчас, выйдите и войдите в свою учётную запись снова. Сделав это сейчас, вы сможете избежать принудительного выхода из системы при последующем удалении старого протокола." + "Ваш домашний сервер больше не поддерживает старый протокол. Пожалуйста, выйдите и войдите в свою учётную запись снова, чтобы продолжить использование приложения." "Доступно обновление" "Создайте новый ключ восстановления, который можно использовать для восстановления зашифрованной истории сообщений в случае потери доступа к своим устройствам." "Настроить восстановление" - "В настоящее время резервная копия вашего чата не синхронизирована. Требуется подтвердить вашим ключом восстановления, чтобы сохранить доступ к резервной копии чата." + "В настоящее время резервная копия ваших чатов не синхронизирована. Вам потребуется ввести свой ключ восстановления, чтобы сохранить доступ к резервной копии чатов." "Введите " "ключ восстановления" - "Чтобы никогда не пропустить важный звонок, измените настройки, чтобы разрешить полноэкранные уведомления, когда ваш телефон заблокирован." + "Чтобы больше не пропускать важные звонки, разрешите приложению показывать полноэкранные уведомления на заблокированном экране телефона." "Улучшите качество звонков" "Вы уверены, что хотите отклонить приглашение в %1$s?" "Отклонить приглашение" @@ -28,7 +28,7 @@ "Добавить чат в избранное можно в настройках чата. На данный момент вы можете убрать фильтры, чтобы увидеть другие ваши чаты." "У вас пока нет избранных чатов" - "Приглашает" + "Приглашения" "У вас нет отложенных приглашений." "Низкий приоритет" "Вы можете убрать фильтры, чтобы увидеть другие ваши чаты." @@ -38,7 +38,7 @@ "Комнаты" "Вас пока нет ни в одной комнате" "Непрочитанные" - "Поздравляю! + "Поздравляем! У вас нет непрочитанных сообщений!" "Все чаты" "Пометить как прочитанное" diff --git a/features/securebackup/impl/src/main/res/values-ru/translations.xml b/features/securebackup/impl/src/main/res/values-ru/translations.xml index 37e07f7b4c..7c78badea8 100644 --- a/features/securebackup/impl/src/main/res/values-ru/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ru/translations.xml @@ -12,10 +12,10 @@ "Введите " "ключ восстановления" - "Резервная копия чата в настоящее время не синхронизирована." + "В настоящее время резервная копия ваших чатов не синхронизирована." "Настроить восстановление" "Получите доступ к зашифрованным сообщениям, если вы потеряете все свои устройства или выйдете из системы %1$s отовсюду." - "Откройте %1$s на настольном устройстве" + "Откройте %1$s на компьютере" "Войдите в свой аккаунт еще раз" "Когда вас попросят подтвердить устройство, выберите %1$s" "“Сбросить все”" diff --git a/features/userprofile/shared/src/main/res/values-ru/translations.xml b/features/userprofile/shared/src/main/res/values-ru/translations.xml index 665ee4207f..b790f579f1 100644 --- a/features/userprofile/shared/src/main/res/values-ru/translations.xml +++ b/features/userprofile/shared/src/main/res/values-ru/translations.xml @@ -6,5 +6,5 @@ "Разблокировать" "Вы снова сможете увидеть все сообщения." "Разблокировать пользователя" - "Произошла ошибка при попытке открытия комнаты" + "Произошла ошибка при запуске чата" diff --git a/features/verifysession/impl/src/main/res/values-cs/translations.xml b/features/verifysession/impl/src/main/res/values-cs/translations.xml index bfa46cafd9..61946555a5 100644 --- a/features/verifysession/impl/src/main/res/values-cs/translations.xml +++ b/features/verifysession/impl/src/main/res/values-cs/translations.xml @@ -17,6 +17,7 @@ "Porovnejte čísla" "Vaše nová relace je nyní ověřena. Má přístup k vašim zašifrovaným zprávám a ostatní uživatelé ji uvidí jako důvěryhodnou." "Zadejte klíč pro obnovení" + "Buď vypršel časový limit požadavku, požadavek byl zamítnut, nebo došlo k nesouladu ověření." "Pro přístup k historii zašifrovaných zpráv prokažte, že jste to vy." "Otevřete existující relaci" "Opakovat ověření" @@ -24,6 +25,10 @@ "Čekání na shodu" "Porovnejte jedinečnou sadu emotikonů." "Porovnejte jedinečné emotikony a ujistěte se, že jsou zobrazeny ve stejném pořadí." + "Přihlášen" + "Pokračujte, pouze pokud jste toto ověření zahájili." + "Ověřte druhé zařízení, aby byla vaše historie zpráv zabezpečená." + "Požadováno ověření" "Neshodují se" "Shodují se" "Pro pokračování přijměte požadavek na zahájení ověření v jiné relaci." diff --git a/features/verifysession/impl/src/main/res/values-et/translations.xml b/features/verifysession/impl/src/main/res/values-et/translations.xml index 3aca307e0b..24ac0736c4 100644 --- a/features/verifysession/impl/src/main/res/values-et/translations.xml +++ b/features/verifysession/impl/src/main/res/values-et/translations.xml @@ -17,6 +17,7 @@ "Võrdle numbreid" "Sinu uus sessioon on nüüd verifitseeritud. Sellel sessioonil on nüüd ligipääs sinu krüptitud sõnumitele ja teised osapooled näevad teda usaldusväärsena." "Sisesta taastevõti" + "Kas verifitseerimine aegus, teine osapool keeldus vastanast või tekkis vastuste mittevastavus." "Saamaks ligipääsu krüptitud sõnumite ajaloole tõesta et tegemist on sinuga." "Ava olemasolev sessioon" "Proovi verifitseerimist uuesti" @@ -24,6 +25,10 @@ "Ootame kinnitust sobivusele" "Võrdle unikaalset emojide kombinatsiooni" "Võrdle unikaalset emojide kombinatsiooni ning kontrolli, et nad on täpselt samas järjekorras." + "Sisselogitud" + "Jätka vaid siis, kui sina algatasid verifitseerimise." + "Hoidmaks oma sõnumiajalugu turvatuna verifitseeri teine seade." + "Verifitseerimispäring" "Nad ei klapi omavahel" "Nad klapivad omavahel" "Jätkamaks nõustu verifitseerimisprotsessi alustamisega oma teises sessioonis." diff --git a/features/verifysession/impl/src/main/res/values-fr/translations.xml b/features/verifysession/impl/src/main/res/values-fr/translations.xml index d80520199a..4632373a92 100644 --- a/features/verifysession/impl/src/main/res/values-fr/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fr/translations.xml @@ -24,6 +24,9 @@ "En attente de correspondance" "Comparer un groupe unique d’Emojis." "Comparez les emoji uniques en veillant à ce qu’ils apparaissent dans le même ordre." + "Continuez uniquement si c’est vous qui avez commencé cette vérification." + "Vérifiez l’autre appareil pour sécuriser l’historique de vos messages." + "Vérification demandée" "Ils ne correspondent pas" "Ils correspondent" "Pour continuer, acceptez la demande de lancement de la procédure de vérification dans votre autre session." diff --git a/features/verifysession/impl/src/main/res/values-hu/translations.xml b/features/verifysession/impl/src/main/res/values-hu/translations.xml index 52c92e16f6..10e0559bca 100644 --- a/features/verifysession/impl/src/main/res/values-hu/translations.xml +++ b/features/verifysession/impl/src/main/res/values-hu/translations.xml @@ -17,6 +17,7 @@ "Számok összehasonlítása" "Az új munkamenete most már ellenőrizve van. Eléri a titkosított üzeneteit, és a többi felhasználó is megbízhatónak fogja látni." "Adja meg a helyreállítási kulcsot" + "A kérés túllépte az időkorlátot, el lett utasítva, vagy ellenőrzési eltérés történt." "Bizonyítsa, hogy valóban Ön az, hogy elérje a titkosított üzeneteinek előzményeit." "Meglévő munkamenet megnyitása" "Ellenőrzés újrapróbálása" @@ -24,6 +25,10 @@ "Várakozás az egyezésre" "Egyedi emodzsik összehasonlítása." "Hasonlítsa össze az egyedi emodzsikat, meggyőződve arról, hogy azonos a sorrendjük." + "Bejelentkezve" + "Csak akkor folytassa, ha Ön kezdeményezte ezt az ellenőrzést." + "Az üzenetelőzmények biztonságának megőrzése érdekében ellenőrizze a másik eszközt." + "Ellenőrzés kérve" "Nem egyeznek" "Megegyeznek" "A folytatáshoz fogadja el az ellenőrzési folyamat indítási kérését a másik munkamenetében." diff --git a/features/verifysession/impl/src/main/res/values-pt/translations.xml b/features/verifysession/impl/src/main/res/values-pt/translations.xml index 0c7731faf9..d5bdaaf719 100644 --- a/features/verifysession/impl/src/main/res/values-pt/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pt/translations.xml @@ -24,6 +24,10 @@ "A aguardar correspondência" "Compara um conjunto único de emojis." "Compara os emojis únicos, certificando-te de que aparecem pela mesma ordem." + "Sessão iniciada" + "Continue apenas se tiver iniciado esta verificação." + "Verifique o outro dispositivo para manter o histórico de mensagens seguro." + "Verificação solicitada" "Não correspondem" "Correspondem" "Para continuar, aceita o pedido de verificação na tua outra sessão." diff --git a/features/verifysession/impl/src/main/res/values-ru/translations.xml b/features/verifysession/impl/src/main/res/values-ru/translations.xml index d92b5c6f7b..ff8574487a 100644 --- a/features/verifysession/impl/src/main/res/values-ru/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ru/translations.xml @@ -23,6 +23,7 @@ "Введите " "ключ восстановления" + "Запрос был отклонен, так как время ожидания запроса истекло, либо произошла ошибка при проверке." "Чтобы получить доступ к зашифрованной истории сообщений, докажите, что это вы." "Открыть существующий сеанс" "Повторить проверку" @@ -30,6 +31,10 @@ "Ожидание соответствия" "Сравните уникальный набор эмодзи." "Сравните уникальные смайлики, убедившись, что они расположены в том же порядке." + "Вход выполнен" + "Продолжайте только в том случае, если вы инициировали эту проверку." + "Чтобы сохранить историю сообщений в безопасности, проверьте другое устройство." + "Запрос на верификация" "Они не совпадают" "Они совпадают" "Для продолжения работы примите запрос на запуск процесса проверки в другом сеансе." diff --git a/features/verifysession/impl/src/main/res/values/localazy.xml b/features/verifysession/impl/src/main/res/values/localazy.xml index 4b5f49a875..be10ce5aa1 100644 --- a/features/verifysession/impl/src/main/res/values/localazy.xml +++ b/features/verifysession/impl/src/main/res/values/localazy.xml @@ -17,6 +17,7 @@ "Compare numbers" "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted." "Enter recovery key" + "Either the request timed out, the request was denied, or there was a verification mismatch." "Prove it’s you in order to access your encrypted message history." "Open an existing session" "Retry verification" @@ -24,6 +25,10 @@ "Waiting to match" "Compare a unique set of emojis." "Compare the unique emoji, ensuring they appear in the same order." + "Signed in" + "Only continue if you initiated this verification." + "Verify the other device to keep your message history secure." + "Verification requested" "They don’t match" "They match" "Accept the request to start the verification process in your other session to continue." diff --git a/libraries/eventformatter/impl/src/main/res/values-ru/translations.xml b/libraries/eventformatter/impl/src/main/res/values-ru/translations.xml index 385630ef4c..b2a86a6962 100644 --- a/libraries/eventformatter/impl/src/main/res/values-ru/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-ru/translations.xml @@ -1,10 +1,10 @@ "(изображение тоже было изменено)" - "%1$s сменили свое изображение" + "%1$s сменил своё изображение" "Вы сменили изображение профиля" - "%1$s был понижен в должности до участника" - "%1$s был понижен в должности до модератора" + "%1$s был понижен до участника" + "%1$s был понижен до модератора" "%1$s изменил свое отображаемое имя с %2$s на %3$s" "Вы изменили свое отображаемое имя с %1$s на %2$s" "%1$s удалил свое отображаемое имя (оно было %2$s)" @@ -38,7 +38,7 @@ "%1$s больше не заинтересован в присоединении" "Вы отменили запрос на присоединение" "%1$s покинул комнату" - "Вы вышли из комнаты" + "Вы покинули комнату" "%1$s изменил название комнаты на: %2$s" "Вы изменили название комнаты на: %1$s" "%1$s удалил название комнаты" diff --git a/libraries/ui-strings/src/main/res/values-be/translations.xml b/libraries/ui-strings/src/main/res/values-be/translations.xml index f382194509..10b64deda4 100644 --- a/libraries/ui-strings/src/main/res/values-be/translations.xml +++ b/libraries/ui-strings/src/main/res/values-be/translations.xml @@ -106,6 +106,7 @@ "Адправіць паведамленне" "Падзяліцца" "Абагуліць спасылку" + "Паказаць" "Увайдзіце яшчэ раз" "Выйсці" "Усё роўна выйсці" @@ -132,6 +133,7 @@ "Ідзе званок (не падтрымліваецца)" "Званок пачаўся" "Рэзервовае капіраванне чатаў" + "Скапіравана ў буфер абмену" "Аўтарскае права" "Стварэнне пакоя…" "Выйшаў з пакоя" @@ -253,6 +255,7 @@ "Чакаем…" "Чакаю гэта паведамленне" "Вы" + "(%1$s)" "Пацвярджэнне" "Памылка" "Поспех" @@ -281,6 +284,9 @@ "Гэй, пагавары са мной у %1$s: %2$s" "%1$s Android" "Паведаміць аб памылцы з дапамогай Rageshake" + "Хто заўгодна" + "Доступ у пакой" + "Папрасіце далучыцца" "Не ўдалося выбраць носьбіт, паўтарыце спробу." "Не атрымалася апрацаваць медыяфайл для загрузкі, паспрабуйце яшчэ раз." "Не атрымалася загрузіць медыяфайлы, паспрабуйце яшчэ раз." diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index 0dc74b6c1d..8f0a20bd47 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -66,6 +66,7 @@ "Zapomněli jste heslo?" "Přeposlat" "Přejít zpět" + "Ignorovat" "Pozvat" "Pozvat přátele" "Pozvat přátele do %1$s" @@ -140,6 +141,7 @@ "Tmavé" "Chyba dešifrování" "Možnosti pro vývojáře" + "ID zařízení" "Přímý chat" "Znovu nezobrazovat" "(upraveno)" @@ -249,6 +251,8 @@ Důvod: %1$s." "Uživatelské jméno" "Ověření zrušeno" "Ověření dokončeno" + "Ověření se nezdařilo" + "Ověřeno" "Ověřit zařízení" "Video" "Hlasová zpráva" @@ -256,6 +260,8 @@ Důvod: %1$s." "Čekání na dešifrovací klíč" "Vy" "Zdá se, že se identita %1$s změnila. %2$s" + "Zdá se, že identita %1$s %2$s se změnila. %3$s" + "(%1$s)" "Potvrzení" "Chyba" "Úspěch" @@ -289,6 +295,13 @@ Důvod: %1$s." "Přístup do místnosti" "Kdokoli může požádat o vstup do místnosti, ale správce nebo moderátor bude muset žádost přijmout" "Požádat o připojení" + "Zrušit žádost" + "Ano, zrušit" + "Opravdu chcete zrušit svou žádost o vstup do této místnosti?" + "Zrušit žádost o vstup" + "Zpráva (nepovinné)" + "Pokud bude váš požadavek přijat, obdržíte pozvánku na vstup do místnosti." + "Žádost o vstup odeslána" "Výběr média se nezdařil, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." @@ -320,6 +333,8 @@ Důvod: %1$s." "Odblokovat" "Znovu uvidíte všechny zprávy od nich." "Odblokovat uživatele" + "K ověření tohoto uživatele použijte webovou aplikaci." + "Ověřit %1$s" "%1$s z %2$s" "%1$s Připnuté zprávy" "Načítání zprávy…" diff --git a/libraries/ui-strings/src/main/res/values-el/translations.xml b/libraries/ui-strings/src/main/res/values-el/translations.xml index 06e21e9d57..482e4b3eb7 100644 --- a/libraries/ui-strings/src/main/res/values-el/translations.xml +++ b/libraries/ui-strings/src/main/res/values-el/translations.xml @@ -245,6 +245,7 @@ "Όνομα χρήστη" "Η επαλήθευση ακυρώθηκε" "Η επαλήθευση ολοκληρώθηκε" + "Επαληθεύτηκε" "Επαλήθευση συσκευής" "Βίντεο" "Φωνητικό μήνυμα" @@ -252,6 +253,8 @@ "Αναμονή για αυτό το μήνυμα" "Εσύ" "Η ταυτότητα του χρήστη %1$s φαίνεται να έχει αλλάξει. %2$s" + "Η ταυτότητα του %1$s %2$s φαίνεται να έχει αλλάξει. %3$s" + "(%1$s)" "Επιβεβαίωση" "Σφάλμα" "Επιτυχία" @@ -280,6 +283,12 @@ "Γεια, μίλα μου στην εφαρμογή %1$s :%2$s" "%1$s Android" "Κούνησε δυνατά τη συσκευή σου για να αναφέρεις κάποιο σφάλμα" + "Οποιοσδήποτε μπορεί να συμμετάσχει σε αυτό το δωμάτιο" + "Οποιοσδήποτε" + "Πρόσβαση Δωματίου" + "Οποιοσδήποτε μπορεί να ζητήσει να συμμετάσχει στο δωμάτιο, αλλά ένας διαχειριστής ή συντονιστής θα πρέπει να αποδεχθεί το αίτημα" + "Αίτημα συμμετοχής" + "Μήνυμα (προαιρετικό)" "Αποτυχία επιλογής πολυμέσου, δοκίμασε ξανά." "Αποτυχία μεταφόρτωσης μέσου, δοκίμασε ξανά." "Αποτυχία μεταφόρτωσης πολυμέσων, δοκίμασε ξανά." diff --git a/libraries/ui-strings/src/main/res/values-et/translations.xml b/libraries/ui-strings/src/main/res/values-et/translations.xml index 6a6b1559a9..5905e40b12 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -64,6 +64,7 @@ "Kas unustasid salasõna?" "Edasta" "Tagasi eelmisesse vaatesse" + "Eira" "Kutsu" "Kutsu osalejaid" "Kutsu huvilisi kasutama rakendust %1$s" @@ -138,6 +139,7 @@ "Tume" "Dekrüptimisviga" "Arendaja valikud" + "Seadme tunnus" "Otsevestlus" "Ära enam näita seda uuesti" "(muudetud)" @@ -245,6 +247,8 @@ Põhjus: %1$s." "Kasutajanimi" "Verifitseerimine on katkestatud" "Verifitseerimine on tehtud" + "Verifitseerimine ei õnnestunud" + "Verifitseeritud" "Verifitseeri seade" "Video" "Häälsõnum" @@ -252,6 +256,8 @@ Põhjus: %1$s." "Ootame selle sõnumi dekrüptimisvõtit" "Sina" "Kasutaja %1$s võrguidentiteet tundub olema muutunud. %2$s" + "Kasutaja %1$s %2$s võrguidentiteet tundub olema muutunud. %3$s" + "(%1$s)" "Kinnitus" "Viga" "Õnnestus" @@ -285,6 +291,13 @@ Põhjus: %1$s." "Ligipääs jututoale" "Kõik võivad paluda selle jututoaga liitumist, kuid peakasutaja või moderaator peavad selle kinnitama" "Küsi võimalust liitumiseks" + "Tühista liitumispalve" + "Jah, tühista" + "Kas sa oled kindel, et soovid tühistada oma palve jututoaga liitumiseks?" + "Tühista liitumispalve" + "Selgitus (kui soovid lisada)" + "Kui sinu liitumispalvega ollakse nõus, siis saad kutse jututoaga liitumiseks." + "Liitumispalve on saadetud" "Meediafaili valimine ei õnnestunud. Palun proovi uuesti." "Meediafaili töötlemine enne üleslaadimist ei õnnestunud. Palun proovi uuesti." "Meediafaili üleslaadimine ei õnnestunud. Palun proovi uuesti." @@ -315,6 +328,8 @@ Põhjus: %1$s." "Eemalda blokeering" "Nüüd näed sa jälle kõiki tema sõnumeid" "Eemalda kasutajalt blokeering" + "Kasutaja verifitseerimiseks kasuta veebirakendust." + "Verifitseeri kasutaja %1$s" "%1$s / %2$s" "%1$s esiletõstetud sõnumit" "Laadime sõnumit…" 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 f7fadeb1f0..a1b8f54f34 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -64,6 +64,7 @@ "Mot de passe oublié ?" "Transférer" "Retour" + "Ignorer" "Inviter" "Inviter des amis" "Inviter des amis à %1$s" @@ -138,6 +139,7 @@ "Sombre" "Erreur de déchiffrement" "Options pour les développeurs" + "Identifiant de session" "Discussion à deux" "Ne plus afficher" "(modifié)" @@ -245,6 +247,7 @@ Raison: %1$s." "Nom d’utilisateur" "Vérification annulée" "Vérification terminée" + "Vérifié(e)" "Vérifier la session" "Vidéo" "Message vocal" @@ -252,6 +255,8 @@ Raison: %1$s." "En attente de la clé de déchiffrement" "Vous" "L’identité de %1$s semble avoir changé. %2$s" + "L’identité de %1$s %2$s semble avoir changé. %3$s" + "(%1$s)" "Confirmation" "Erreur" "Succès" @@ -285,6 +290,10 @@ Raison: %1$s." "Accès au salon" "Tout le monde peut demander à rejoindre le salon, mais un administrateur ou un modérateur devra accepter la demande" "Demander à rejoindre" + "Annuler la demande" + "Message (facultatif)" + "Vous recevrez une invitation à rejoindre le salon si votre demande est acceptée." + "Demande de rejoindre le salon envoyée" "Échec de la sélection du média, veuillez réessayer." "Échec du traitement des médias à télécharger, veuillez réessayer." "Échec du téléchargement du média, veuillez réessayer." @@ -315,6 +324,8 @@ Raison: %1$s." "Débloquer" "Vous pourrez à nouveau voir tous ses messages." "Débloquer l’utilisateur" + "Utilisez l’application Web pour vérifier cet utilisateur." + "Vérifier %1$s" "%1$s sur %2$s" "%1$s Messages épinglés" "Chargement du message…" 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 71ed67df1a..6e2d70e004 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -64,6 +64,7 @@ "Elfelejtette a jelszót?" "Tovább" "Visszalépés" + "Mellőzés" "Meghívás" "Ismerősök meghívása" "Ismerősök meghívása ide: %1$s" @@ -138,6 +139,7 @@ "Sötét" "Visszafejtési hiba" "Fejlesztői beállítások" + "Eszközazonosító" "Közvetlen csevegés" "Ne jelenjen meg többé" "(szerkesztve)" @@ -245,6 +247,8 @@ Ok: %1$s." "Felhasználónév" "Az ellenőrzés megszakítva" "Az ellenőrzés befejeződött" + "Az ellenőrzés sikertelen" + "Ellenőrizve" "Eszköz ellenőrzése" "Videó" "Hangüzenet" @@ -252,6 +256,8 @@ Ok: %1$s." "Várakozás a visszafejtési kulcsra" "Ön" "Úgy tűnik, hogy %1$s személyazonossága megváltozott. %2$s" + "Úgy tűnik, hogy %1$s %2$s személyazonossága megváltozott. %3$s" + "(%1$s)" "Megerősítés" "Hiba" "Sikeres" @@ -285,6 +291,13 @@ Ok: %1$s." "Szobahozzáférés" "Bárki kérheti, hogy csatlakozzon a szobához, de egy adminisztrátornak vagy moderátornak el kell fogadnia a kérést" "Csatlakozás kérése" + "Kérés visszavonása" + "Igen, visszavonás" + "Biztos, hogy visszavonja a szobához való csatlakozási kérését?" + "Csatlakozási kérés visszavonása" + "Üzenet (nem kötelező)" + "Ha a kérését elfogadják, meghívót kap a szobához való csatlakozáshoz." + "Csatlakozási kérés elküldve" "Nem sikerült kiválasztani a médiát, próbálja újra." "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra." "Nem sikerült a média feltöltése, próbálja újra." @@ -315,6 +328,8 @@ Ok: %1$s." "Letiltás feloldása" "Újra láthatja az összes üzenetét." "Felhasználó kitiltásának feloldása" + "Használja a webes alkalmazást a felhasználó ellenőrzéséhez." + "A(z) %1$s ellenőrzése" "%1$s / %2$s" "%1$s kitűzött üzenet" "Üzenet betöltése…" diff --git a/libraries/ui-strings/src/main/res/values-pt/translations.xml b/libraries/ui-strings/src/main/res/values-pt/translations.xml index ca2ef09cb3..14504d056c 100644 --- a/libraries/ui-strings/src/main/res/values-pt/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt/translations.xml @@ -64,6 +64,7 @@ "Esqueceu-se da senha?" "Reencaminhar" "Voltar" + "Ignorar" "Convidar" "Convidar pessoas" "Convidar amigos para %1$s" @@ -138,6 +139,7 @@ "Escuro" "Erro de decifragem" "Opções de programador" + "ID do dispositivo" "Conversa direta" "Não mostrar novamente" "(editada)" @@ -185,7 +187,7 @@ Razão: %1$s." "Ligação permanente" "Permissão" "Afixado" - "Por favor, aguarda…" + "Por favor, aguarde…" "Tens a certeza que queres concluir esta sondagem?" "Sondagem: %1$s" "Total de votos: %1$s" @@ -245,6 +247,7 @@ Razão: %1$s." "Nome de utilizador" "Verificação cancelada" "Verificação concluída" + "Verificado" "Verificar o dispositivo" "Vídeo" "Mensagem de voz" @@ -252,6 +255,8 @@ Razão: %1$s." "À espera desta mensagem" "Você" "A identidade de %1$s parece ter mudado. %2$s" + "A identidade de %1$s (username: %2$s ) aparenta ter mudado. %3$s" + "(%1$s)" "Confirmação" "Erro" "Sucesso" @@ -269,7 +274,7 @@ Razão: %1$s." "A %1$s não tem permissão para aceder à tua localização. Continua para ativares o acesso." "A %1$s não tem permissão para aceder ao teu microfone. Permite o acesso para gravar uma mensagem de voz." "Algumas mensagens não foram enviadas" - "Ocorreu um erro, desculpa" + "Desculpe, ocorreu um erro" "A autenticidade desta mensagem cifrada não pode ser garantida neste dispositivo." "Criptografado por um usuário verificado anteriormente." "Não cifrado." @@ -280,6 +285,15 @@ Razão: %1$s." "Alô! Fala comigo na %1$s: %2$s" "%1$s Android" "Agita o dispositivo em fúria para comunicar um problema" + "Qualquer pessoa pode entrar nesta sala" + "Qualquer pessoa" + "Acesso à sala" + "Qualquer pessoa pode pedir para entrar na sala, mas um administrador ou um moderador terá de aceitar o pedido" + "Pedir para participar" + "Cancelar pedido" + "Mensagem (opcional)" + "Irá receber um convite para participar na sala se seu pedido for aceite." + "Pedido de adesão enviado" "Falha ao selecionar multimédia, por favor tente novamente." "Falha ao processar multimédia para carregamento, por favor tente novamente." "Falhar ao carregar multimédia, por favor tente novamente." @@ -310,6 +324,8 @@ Razão: %1$s." "Desbloquear" "Poderás voltar a ver todas as suas mensagens." "Desbloquear utilizador" + "Utiliza a aplicação Web para verificar este utilizador." + "Verifique %1$s" "%1$s de %2$s" "%1$s mensagens afixadas" "A carregar mensagem…" diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index 55aab3c184..866e210cf1 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -66,6 +66,7 @@ "Забыли пароль?" "Переслать" "Вернуться" + "Игнорировать" "Пригласить" "Пригласить в комнату" "Пригласить в %1$s" @@ -73,7 +74,7 @@ "Приглашения" "Присоединиться" "Подробнее" - "Выйти" + "Покинуть" "Покинуть беседу" "Покинуть комнату" "Загрузить еще" @@ -106,6 +107,7 @@ "Отправить сообщение" "Поделиться" "Поделиться ссылкой" + "Показать" "Повторите вход" "Выйти" "Все равно выйти" @@ -132,12 +134,14 @@ "Выполняется звонок (не поддерживается)" "Звонок начат" "Резервная копия чатов" + "Скопировано в буфер обмена" "Авторское право" "Создание комнаты…" "Покинул комнату" - "Темная" + "Тёмное" "Ошибка расшифровки" "Для разработчика" + "Идентификатор устройства" "Личный чат" "Не показывать больше" "(изменено)" @@ -146,10 +150,10 @@ "Шифрование включено" "Введите свой PIN-код" "Ошибка" - "Произошла ошибка, возможно, вы не будете получать уведомления о новых сообщениях. Устраните неполадки с уведомлениями в настройках. + "Произошла ошибка. Вы можете не получать уведомления о новых сообщениях. Устраните неполадки с уведомлениями в настройках. -Причина:%1$s." - "Для всех" +Причина: %1$s." + "Все" "Ошибка" "Избранное" "Избранное" @@ -161,8 +165,8 @@ "В ответ на %1$s" "Установить APK" "Идентификатор Matrix ID не найден, приглашение может быть не получено." - "Покинуть комнату" - "Светлая" + "Покидание комнаты" + "Светлое" "Ссылка скопирована в буфер обмена" "Загрузка…" @@ -175,9 +179,9 @@ "Оформление сообщения" "Сообщение удалено" "Современный" - "Без звука" + "Выкл. звук" "Ничего не найдено" - "Нету названия комнаты" + "Название комнаты отсутствует" "Не в сети" "Лицензии с открытым исходным кодом" "или" @@ -210,11 +214,11 @@ "Редактор форматированного текста" "Комната" "Название комнаты" - "напр., название вашего проекта" - "Сохраненные изменения" + "например, название вашего проекта" + "Изменения сохранены" "Сохранение" - "Блокировка экрана" - "Поиск человека" + "Блокировка приложения" + "Найти кого-нибудь" "Результаты поиска" "Безопасность" "Просмотрено" @@ -225,15 +229,15 @@ "Сервер не поддерживается" "Адрес сервера" "Настройки" - "Делится местонахождением" + "Поделился местоположением" "Выход…" "Что-то пошло не так" - "Начало чата…" + "Чат запускается…" "Стикер" "Успешно" "Предложения" "Синхронизация" - "Системная" + "Системное" "Текст" "Уведомление о третьей стороне" "Обсуждение" @@ -244,23 +248,29 @@ "Не удалось отправить приглашения одному или нескольким пользователям." "Не удалось отправить приглашение(я)" "Разблокировать" - "Включить звук" + "Вкл. звук" "Неподдерживаемое событие" "Имя пользователя" "Проверка отменена" "Проверка завершена" + "Сбой проверки" + "Проверено" "Подтверждение устройства" "Видео" "Голосовое сообщение" "Ожидание…" "Ожидание ключа расшифровки" + "Вы" + "Судя по всему, идентификатор %1$s изменился. %2$s" + "Пользователь %1$s сменил имя пользователя на %2$s. %3$s" + "(%1$s)" "Подтверждение" "Ошибка" "Успешно" "Предупреждение" "Изменения не сохранены. Вы действительно хотите вернуться?" "Сохранить изменения?" - "Ваш homeserver необходимо обновить, чтобы он поддерживал Matrix Authentication Service и создание учетной записи." + "Ваш домашний сервер необходимо обновить, чтобы он поддерживал Matrix Authentication Service и создание учётных записей." "Не удалось создать постоянную ссылку" "Не удалось загрузить карту %1$s. Пожалуйста, повторите попытку позже." "Не удалось загрузить сообщения" @@ -282,15 +292,27 @@ "Привет, поговори со мной по %1$s: %2$s" "%1$s Android" "Встряхните устройство, чтобы сообщить об ошибке" + "Любой желающий может присоединиться к этой комнате" + "Любой" + "Доступ в комнату" + "Любой желающий может подать заявку на присоединение к комнате, но администратор или модератор должен будет принять запрос." + "Попросить присоединиться" + "Отменить запрос" + "Да, отменить" + "Вы действительно хотите отменить заявку на вступление в эту комнату?" + "Отменить запрос на присоединение" + "Сообщение (опционально)" + "Вы получите приглашение присоединиться к комнате, как только ваш запрос будет принят." + "Запрос на присоединение отправлен" "Не удалось выбрать носитель, попробуйте еще раз." "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось загрузить медиафайлы, попробуйте еще раз." "Нажмите на сообщение и выберите “%1$s”, чтобы добавить его сюда." "Закрепите важные сообщения, чтобы их можно было легко найти" - "%1$d Закрепленное сообщение" - "%1$d Закрепленных сообщений" - "%1$d Закрепленных сообщений" + "%1$d закреплённое сообщение" + "%1$d закреплённых сообщения" + "%1$d закреплённых сообщений" "Закрепленные сообщения" "Вы собираетесь перейти в свою учетную запись %1$s, чтобы сбросить идентификацию. После этого вы вернетесь в приложение." @@ -313,6 +335,8 @@ "Разблокировать" "Вы снова сможете увидеть все сообщения." "Разблокировать пользователя" + "Используйте веб-приложение для проверки этого пользователя." + "Верифицировать %1$s" "%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 24e35e6e58..f80e8b1a3b 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -64,6 +64,7 @@ "Forgot password?" "Forward" "Go back" + "Ignore" "Invite" "Invite people" "Invite people to %1$s" @@ -138,6 +139,7 @@ "Dark" "Decryption error" "Developer options" + "Device ID" "Direct chat" "Do not show this again" "(edited)" @@ -245,6 +247,8 @@ Reason: %1$s." "Username" "Verification cancelled" "Verification complete" + "Verification failed" + "Verified" "Verify device" "Video" "Voice message" @@ -287,6 +291,13 @@ Reason: %1$s." "Room Access" "Anyone can ask to join the room but an administrator or a moderator will have to accept the request" "Ask to join" + "Cancel request" + "Yes, cancel" + "Are you sure that you want to cancel your request to join this room?" + "Cancel request to join" + "Message (optional)" + "You will receive an invite to join the room if your request is accepted." + "Request to join sent" "Failed selecting media, please try again." "Failed processing media to upload, please try again." "Failed uploading media, please try again." @@ -317,6 +328,8 @@ Reason: %1$s." "Unblock" "You\'ll be able to see all messages from them again." "Unblock user" + "Use the web app to verify this user." + "Verify %1$s" "%1$s of %2$s" "%1$s Pinned messages" "Loading message…" diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_10_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_10_de.png similarity index 100% rename from screenshots/de/features.messages.impl.actionlist_SheetContent_Day_10_de.png rename to screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_10_de.png diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_11_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png similarity index 100% rename from screenshots/de/features.messages.impl.actionlist_SheetContent_Day_11_de.png rename to screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_12_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png similarity index 100% rename from screenshots/de/features.messages.impl.actionlist_SheetContent_Day_12_de.png rename to screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_2_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png similarity index 100% rename from screenshots/de/features.messages.impl.actionlist_SheetContent_Day_2_de.png rename to screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png 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 new file mode 100644 index 0000000000..02de885c5f --- /dev/null +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23bc2e3eaba71cb846544eb97e89b823f2648c425ae6f00c4d5e1d5498c0e6e2 +size 46829 diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_4_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png similarity index 100% rename from screenshots/de/features.messages.impl.actionlist_SheetContent_Day_4_de.png rename to screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_5_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png similarity index 100% rename from screenshots/de/features.messages.impl.actionlist_SheetContent_Day_5_de.png rename to screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_6_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png similarity index 100% rename from screenshots/de/features.messages.impl.actionlist_SheetContent_Day_6_de.png rename to screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_7_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png similarity index 100% rename from screenshots/de/features.messages.impl.actionlist_SheetContent_Day_7_de.png rename to screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_8_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png similarity index 100% rename from screenshots/de/features.messages.impl.actionlist_SheetContent_Day_8_de.png rename to screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_9_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_9_de.png similarity index 100% rename from screenshots/de/features.messages.impl.actionlist_SheetContent_Day_9_de.png rename to screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_9_de.png diff --git a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_3_de.png b/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_3_de.png deleted file mode 100644 index 398650d7d8..0000000000 --- a/screenshots/de/features.messages.impl.actionlist_SheetContent_Day_3_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5a055db86e2ab8ff3551d960d363960d7286e4c95d851b3860f599cd40660ff4 -size 46106 diff --git a/screenshots/de/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_de.png b/screenshots/de/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_de.png index 7d9d4f922f..c72cb0ec4e 100644 --- a/screenshots/de/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_de.png +++ b/screenshots/de/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6749aaf3f44aa684541bf62114ce92b987305182c4f6ee65a5910e1d0ba82ac3 -size 21629 +oid sha256:0466a368266f2958a54307b34dc6d1152c50cded092811268c062c9f86f896e8 +size 25597 diff --git a/screenshots/de/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_de.png b/screenshots/de/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_de.png new file mode 100644 index 0000000000..2281d36854 --- /dev/null +++ b/screenshots/de/features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c530c4887b65128e9aceaf9c46196204e0a049386fb796cacfa30ee83d99084c +size 25593 diff --git a/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_de.png b/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_de.png index 8abb964552..6f94e9c728 100644 --- a/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_de.png +++ b/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bac606b3b9465075f0bcf4815cd16dbfadbf9db619597bb2cdabe6395b1df11d -size 61363 +oid sha256:5666a5d1feff7e59e1bd2b4e94a51eafd373f83ecc1ed6aa8a06092c50e73061 +size 65249 diff --git a/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_de.png b/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_de.png new file mode 100644 index 0000000000..a8a3fea732 --- /dev/null +++ b/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:354ed0f8a645638db63722d0955cddbf02bb008f39bf834fe24908afa37f880f +size 65382 diff --git a/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_de.png b/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_de.png deleted file mode 100644 index f36f49ce72..0000000000 --- a/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:93a166c63f0b29cb6791d1b96f4c77d74fe29e14c2baaf7c33c4d2571176943f -size 9952 diff --git a/screenshots/de/libraries.textcomposer_ComposerModeView_Day_0_de.png b/screenshots/de/libraries.textcomposer_ComposerModeView_Day_0_de.png new file mode 100644 index 0000000000..7ecf002112 --- /dev/null +++ b/screenshots/de/libraries.textcomposer_ComposerModeView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e7bdf3b06a82ef3ab52d8dfb804a5e7d1157e4db38f25311c9f4828525c6952 +size 6305 diff --git a/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png b/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png index d655f19524..4e35edbe2c 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22323989077b8cce6a5fe26a6c5dff9c4d712eaef8fc8381df2fe43e124fa605 -size 40654 +oid sha256:b2713c1a6becca31bd61ac33cb30ea706941cb3ec287e951f2fdaad938a7232b +size 40601 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png index a7146ae26e..4e20922a3b 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2df79923fb5ffe8e31c9926c3e7b8fc138dd28b282ea01c51b2b46856b075275 -size 18883 +oid sha256:3ecae15f2e602777469676ed077ef29bff074024cd794311158d04b9811d52e4 +size 18860 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png index 4f8c751556..7cd756a085 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f85a09751dfdc38bb8a7bdaea8c8272743ce1e7adc52f003303ced96b4750cd8 -size 21781 +oid sha256:4238ca250f17086205bb34379ea3a8dbb5cd1b24027109e3659283d260b50713 +size 22010 diff --git a/screenshots/de/libraries.textcomposer_TextComposerSimple_Day_0_de.png b/screenshots/de/libraries.textcomposer_TextComposerSimple_Day_0_de.png index 6321c61371..59be0da866 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerSimple_Day_0_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerSimple_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7d76ea7bc5607d2c42f245428bce71e52b0376e4ab8e0930ac3b39dff70894e -size 45150 +oid sha256:23a2eafd230932521cf6ff71329ff17b331d600ae77955737552f33f82ea8d85 +size 44499 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index 02afde07c8..91088e828e 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,45 +1,58 @@ // Generated file, do not edit export const screenshots = [ ["en","en-dark","de",], -["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20006,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20014,], ["features.invite.impl.response_AcceptDeclineInviteView_Day_0_en","features.invite.impl.response_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_1_en","features.invite.impl.response_AcceptDeclineInviteView_Night_1_en",20006,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_2_en","features.invite.impl.response_AcceptDeclineInviteView_Night_2_en",20006,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_3_en","features.invite.impl.response_AcceptDeclineInviteView_Night_3_en",20006,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_4_en","features.invite.impl.response_AcceptDeclineInviteView_Night_4_en",20006,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20006,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_1_en","features.invite.impl.response_AcceptDeclineInviteView_Night_1_en",20014,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_2_en","features.invite.impl.response_AcceptDeclineInviteView_Night_2_en",20014,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_3_en","features.invite.impl.response_AcceptDeclineInviteView_Night_3_en",20014,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_4_en","features.invite.impl.response_AcceptDeclineInviteView_Night_4_en",20014,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20014,], ["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",0,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20006,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20006,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20006,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20014,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20014,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20014,], ["features.login.impl.accountprovider_AccountProviderView_Day_0_en","features.login.impl.accountprovider_AccountProviderView_Night_0_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_1_en","features.login.impl.accountprovider_AccountProviderView_Night_1_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_2_en","features.login.impl.accountprovider_AccountProviderView_Night_2_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_3_en","features.login.impl.accountprovider_AccountProviderView_Night_3_en",0,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20006,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20006,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20006,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20006,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en",20006,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en",20006,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en",20006,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en",20006,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20006,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20006,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20006,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20006,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20006,], -["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",20006,], +["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",20017,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20017,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20017,], +["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",20017,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20017,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20017,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20017,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20017,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20017,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20017,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20017,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20014,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20014,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20014,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20014,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en",20014,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en",20014,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en",20014,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en",20014,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20014,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20014,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20014,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20014,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20014,], +["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",20014,], ["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",20006,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20014,], ["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",20006,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20014,], ["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",20006,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20014,], ["libraries.designsystem.components.async_AsyncIndicatorFailure_Day_0_en","libraries.designsystem.components.async_AsyncIndicatorFailure_Night_0_en",0,], ["libraries.designsystem.components.async_AsyncIndicatorLoading_Day_0_en","libraries.designsystem.components.async_AsyncIndicatorLoading_Night_0_en",0,], ["libraries.designsystem.components.async_AsyncLoading_Day_0_en","libraries.designsystem.components.async_AsyncLoading_Night_0_en",0,], -["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20006,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20014,], ["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,], @@ -49,11 +62,11 @@ export const screenshots = [ ["libraries.matrix.ui.components_AttachmentThumbnail_Day_6_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_6_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_7_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_7_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_8_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_8_en",0,], -["features.messages.impl.attachments.preview_AttachmentsView_0_en","",20006,], -["features.messages.impl.attachments.preview_AttachmentsView_1_en","",20006,], -["features.messages.impl.attachments.preview_AttachmentsView_2_en","",20006,], -["features.messages.impl.attachments.preview_AttachmentsView_3_en","",20006,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20006,], +["features.messages.impl.attachments.preview_AttachmentsView_0_en","",20014,], +["features.messages.impl.attachments.preview_AttachmentsView_1_en","",20014,], +["features.messages.impl.attachments.preview_AttachmentsView_2_en","",20014,], +["features.messages.impl.attachments.preview_AttachmentsView_3_en","",20014,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20014,], ["libraries.designsystem.components.avatar_Avatar_Avatars_0_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_10_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_11_en","",0,], @@ -136,13 +149,13 @@ export const screenshots = [ ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], ["libraries.designsystem.components_BigCheckmark_Day_0_en","libraries.designsystem.components_BigCheckmark_Night_0_en",0,], ["libraries.designsystem.components_BigIcon_Day_0_en","libraries.designsystem.components_BigIcon_Night_0_en",0,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20006,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20006,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20006,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20006,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20006,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20006,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20006,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20014,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20014,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20014,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20014,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20014,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20014,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20014,], ["libraries.designsystem.components_BloomInitials_Day_0_en","libraries.designsystem.components_BloomInitials_Night_0_en",0,], ["libraries.designsystem.components_BloomInitials_Day_1_en","libraries.designsystem.components_BloomInitials_Night_1_en",0,], ["libraries.designsystem.components_BloomInitials_Day_2_en","libraries.designsystem.components_BloomInitials_Night_2_en",0,], @@ -153,96 +166,100 @@ export const screenshots = [ ["libraries.designsystem.components_BloomInitials_Day_7_en","libraries.designsystem.components_BloomInitials_Night_7_en",0,], ["libraries.designsystem.components_Bloom_Day_0_en","libraries.designsystem.components_Bloom_Night_0_en",0,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportView_Day_0_en","features.rageshake.impl.bugreport_BugReportView_Night_0_en",20006,], -["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",20006,], -["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",20006,], -["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",20006,], -["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",20006,], +["features.rageshake.impl.bugreport_BugReportView_Day_0_en","features.rageshake.impl.bugreport_BugReportView_Night_0_en",20014,], +["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",20014,], +["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",20014,], +["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",20014,], +["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",20014,], ["libraries.designsystem.atomic.molecules_ButtonColumnMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ButtonColumnMolecule_Night_0_en",0,], ["libraries.designsystem.atomic.molecules_ButtonRowMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ButtonRowMolecule_Night_0_en",0,], ["features.call.impl.ui_CallScreenPipView_Day_0_en","features.call.impl.ui_CallScreenPipView_Night_0_en",0,], ["features.call.impl.ui_CallScreenPipView_Day_1_en","features.call.impl.ui_CallScreenPipView_Night_1_en",0,], ["features.call.impl.ui_CallScreenView_Day_0_en","features.call.impl.ui_CallScreenView_Night_0_en",0,], -["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20006,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20006,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20006,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_0_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_10_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_10_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_1_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_1_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_2_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_2_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_3_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_3_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_4_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_4_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_5_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_5_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_6_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_6_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_7_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_7_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_8_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_8_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en",20006,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",20006,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",20006,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",20006,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",20006,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",20006,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",20006,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",20006,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20014,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20014,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20014,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_0_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_10_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_10_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_1_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_1_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_2_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_2_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_3_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_3_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_4_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_4_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_5_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_5_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_6_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_6_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_7_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_7_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_8_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_8_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en",20014,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",20014,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",20014,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",20014,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",20014,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",20014,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",20014,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",20014,], ["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",20006,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20006,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20014,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20014,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20006,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20014,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], ["libraries.designsystem.theme_ColorAliases_Day_0_en","libraries.designsystem.theme_ColorAliases_Night_0_en",0,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20010,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20010,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20014,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20014,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20017,], +["libraries.textcomposer_ComposerModeView_Day_1_en","libraries.textcomposer_ComposerModeView_Night_1_en",0,], +["libraries.textcomposer_ComposerModeView_Day_2_en","libraries.textcomposer_ComposerModeView_Night_2_en",0,], +["libraries.textcomposer_ComposerModeView_Day_3_en","libraries.textcomposer_ComposerModeView_Night_3_en",0,], ["libraries.textcomposer.components_ComposerOptionsButton_Day_0_en","libraries.textcomposer.components_ComposerOptionsButton_Night_0_en",0,], ["libraries.designsystem.components.avatar_CompositeAvatar_Avatars_en","",0,], -["features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en",20006,], -["features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en",20006,], +["features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en",20014,], +["features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en",20014,], ["features.preferences.impl.developer.tracing_ConfigureTracingView_Day_0_en","features.preferences.impl.developer.tracing_ConfigureTracingView_Night_0_en",0,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20006,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20006,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20006,], -["features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20006,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20014,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20014,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20014,], +["features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20014,], ["libraries.designsystem.components.dialogs_ConfirmationDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ConfirmationDialog_Day_0_en","libraries.designsystem.components.dialogs_ConfirmationDialog_Night_0_en",0,], ["features.networkmonitor.api.ui_ConnectivityIndicatorView_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicatorView_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20006,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20006,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20006,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20006,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20006,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20006,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20006,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20006,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20006,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20006,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20006,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20006,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20006,], -["features.createroom.impl.root_CreateRoomRootView_Day_0_en","features.createroom.impl.root_CreateRoomRootView_Night_0_en",20006,], -["features.createroom.impl.root_CreateRoomRootView_Day_1_en","features.createroom.impl.root_CreateRoomRootView_Night_1_en",20006,], -["features.createroom.impl.root_CreateRoomRootView_Day_2_en","features.createroom.impl.root_CreateRoomRootView_Night_2_en",20006,], -["features.createroom.impl.root_CreateRoomRootView_Day_3_en","features.createroom.impl.root_CreateRoomRootView_Night_3_en",20006,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en","",20006,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en","",20006,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20014,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20014,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20014,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20014,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20014,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20014,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20014,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20014,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20014,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20014,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20014,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20014,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20014,], +["features.createroom.impl.root_CreateRoomRootView_Day_0_en","features.createroom.impl.root_CreateRoomRootView_Night_0_en",20014,], +["features.createroom.impl.root_CreateRoomRootView_Day_1_en","features.createroom.impl.root_CreateRoomRootView_Night_1_en",20014,], +["features.createroom.impl.root_CreateRoomRootView_Day_2_en","features.createroom.impl.root_CreateRoomRootView_Night_2_en",20014,], +["features.createroom.impl.root_CreateRoomRootView_Day_3_en","features.createroom.impl.root_CreateRoomRootView_Night_3_en",20014,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en","",20014,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en","",20014,], ["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",20006,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20006,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20006,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20014,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20014,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20014,], ["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",20006,], -["features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",20006,], -["features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en",20006,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20014,], +["features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",20014,], +["features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en",20014,], ["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",20006,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20006,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20006,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20006,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20006,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20006,], -["libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Day_0_en","libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Night_0_en",20006,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20014,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20014,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20014,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20014,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20014,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20014,], +["libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Day_0_en","libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Night_0_en",20014,], ["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,], @@ -254,12 +271,12 @@ export const screenshots = [ ["libraries.designsystem.text_DpScale_1_0f__en","",0,], ["libraries.designsystem.text_DpScale_1_5f__en","",0,], ["libraries.designsystem.theme.components_DropdownMenuItem_Menus_en","",0,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20006,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20006,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20006,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20006,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20006,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20006,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20014,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20014,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20014,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20014,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20014,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20014,], ["libraries.matrix.ui.components_EditableAvatarView_Day_0_en","libraries.matrix.ui.components_EditableAvatarView_Night_0_en",0,], ["libraries.matrix.ui.components_EditableAvatarView_Day_1_en","libraries.matrix.ui.components_EditableAvatarView_Night_1_en",0,], ["libraries.matrix.ui.components_EditableAvatarView_Day_2_en","libraries.matrix.ui.components_EditableAvatarView_Night_2_en",0,], @@ -269,9 +286,9 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Night_0_en",0,], ["features.messages.impl.timeline.components.customreaction_EmojiItem_Day_0_en","features.messages.impl.timeline.components.customreaction_EmojiItem_Night_0_en",0,], ["features.messages.impl.timeline.components.customreaction_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction_EmojiPicker_Night_0_en",0,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20006,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20006,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20006,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20014,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20014,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20014,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], ["libraries.designsystem.theme.components_FilledButtonLargeLowPadding_Buttons_en","",0,], @@ -282,15 +299,15 @@ export const screenshots = [ ["libraries.designsystem.theme.components_FloatingActionButton_Floating Action Buttons_en","",0,], ["libraries.designsystem.atomic.pages_FlowStepPage_Day_0_en","libraries.designsystem.atomic.pages_FlowStepPage_Night_0_en",0,], ["features.messages.impl.timeline.focus_FocusRequestStateView_Day_0_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_0_en",0,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20006,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20006,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20006,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20014,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20014,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20014,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_0_en","features.messages.impl.forward_ForwardMessagesView_Night_0_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_1_en","features.messages.impl.forward_ForwardMessagesView_Night_1_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_2_en","features.messages.impl.forward_ForwardMessagesView_Night_2_en",0,], -["features.messages.impl.forward_ForwardMessagesView_Day_3_en","features.messages.impl.forward_ForwardMessagesView_Night_3_en",20006,], -["features.roomlist.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.roomlist.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20006,], +["features.messages.impl.forward_ForwardMessagesView_Day_3_en","features.messages.impl.forward_ForwardMessagesView_Night_3_en",20014,], +["features.roomlist.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.roomlist.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20014,], ["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,], @@ -312,44 +329,45 @@ export const screenshots = [ ["libraries.designsystem.icons_IconsCompound_Day_4_en","libraries.designsystem.icons_IconsCompound_Night_4_en",0,], ["libraries.designsystem.icons_IconsOther_Day_0_en","libraries.designsystem.icons_IconsOther_Night_0_en",0,], ["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_0_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_0_en",0,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20010,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20014,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20017,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_0_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_0_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_10_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_10_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_11_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_11_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_1_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_1_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_2_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_2_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_3_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_3_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20006,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20014,], ["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",20006,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20014,], ["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",20006,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20014,], ["libraries.designsystem.atomic.molecules_InfoListItemMolecule_Day_0_en","libraries.designsystem.atomic.molecules_InfoListItemMolecule_Night_0_en",0,], ["libraries.designsystem.atomic.organisms_InfoListOrganism_Day_0_en","libraries.designsystem.atomic.organisms_InfoListOrganism_Night_0_en",0,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20006,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20014,], ["features.joinroom.impl_JoinRoomView_Day_0_en","features.joinroom.impl_JoinRoomView_Night_0_en",0,], ["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",0,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20006,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20006,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20006,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20006,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20006,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20006,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20006,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20006,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20006,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20014,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20014,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20014,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20014,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20014,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20014,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20014,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20014,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20014,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], ["libraries.designsystem.components_LabelledOutlinedTextField_Day_0_en","libraries.designsystem.components_LabelledOutlinedTextField_Night_0_en",0,], ["libraries.designsystem.components_LabelledTextField_Day_0_en","libraries.designsystem.components_LabelledTextField_Night_0_en",0,], ["features.leaveroom.api_LeaveRoomView_Day_0_en","features.leaveroom.api_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.api_LeaveRoomView_Day_1_en","features.leaveroom.api_LeaveRoomView_Night_1_en",20006,], -["features.leaveroom.api_LeaveRoomView_Day_2_en","features.leaveroom.api_LeaveRoomView_Night_2_en",20006,], -["features.leaveroom.api_LeaveRoomView_Day_3_en","features.leaveroom.api_LeaveRoomView_Night_3_en",20006,], -["features.leaveroom.api_LeaveRoomView_Day_4_en","features.leaveroom.api_LeaveRoomView_Night_4_en",20006,], -["features.leaveroom.api_LeaveRoomView_Day_5_en","features.leaveroom.api_LeaveRoomView_Night_5_en",20006,], -["features.leaveroom.api_LeaveRoomView_Day_6_en","features.leaveroom.api_LeaveRoomView_Night_6_en",20006,], +["features.leaveroom.api_LeaveRoomView_Day_1_en","features.leaveroom.api_LeaveRoomView_Night_1_en",20014,], +["features.leaveroom.api_LeaveRoomView_Day_2_en","features.leaveroom.api_LeaveRoomView_Night_2_en",20014,], +["features.leaveroom.api_LeaveRoomView_Day_3_en","features.leaveroom.api_LeaveRoomView_Night_3_en",20014,], +["features.leaveroom.api_LeaveRoomView_Day_4_en","features.leaveroom.api_LeaveRoomView_Night_4_en",20014,], +["features.leaveroom.api_LeaveRoomView_Day_5_en","features.leaveroom.api_LeaveRoomView_Night_5_en",20014,], +["features.leaveroom.api_LeaveRoomView_Day_6_en","features.leaveroom.api_LeaveRoomView_Night_6_en",20014,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress Indicators_en","",0,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], @@ -400,29 +418,29 @@ export const screenshots = [ ["libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List supporting text - small padding_List sections_en","",0,], ["libraries.textcomposer.components_LiveWaveformView_Day_0_en","libraries.textcomposer.components_LiveWaveformView_Night_0_en",0,], ["appnav.room.joined_LoadingRoomNodeView_Day_0_en","appnav.room.joined_LoadingRoomNodeView_Night_0_en",0,], -["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20006,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20006,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20006,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20006,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20014,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20014,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20014,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20014,], ["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",20006,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20006,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20006,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20006,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20006,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20006,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20006,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20006,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20006,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20006,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20006,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20006,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20006,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20006,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20006,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20006,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20014,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20014,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20014,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20014,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20014,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20014,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20014,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20014,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20014,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20014,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20014,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20014,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20014,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20014,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20014,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20014,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20006,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20014,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.matrix.ui.components_MatrixUserHeaderPlaceholder_Day_0_en","libraries.matrix.ui.components_MatrixUserHeaderPlaceholder_Night_0_en",0,], ["libraries.matrix.ui.components_MatrixUserHeader_Day_0_en","libraries.matrix.ui.components_MatrixUserHeader_Night_0_en",0,], @@ -432,7 +450,7 @@ export const screenshots = [ ["libraries.mediaviewer.api.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_10_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_1_en","",0,], -["libraries.mediaviewer.api.viewer_MediaViewerView_2_en","",20006,], +["libraries.mediaviewer.api.viewer_MediaViewerView_2_en","",20014,], ["libraries.mediaviewer.api.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_5_en","",0,], @@ -444,7 +462,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",20006,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20014,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_0_en","features.messages.impl.timeline.components_MessageEventBubble_Night_0_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_10_en","features.messages.impl.timeline.components_MessageEventBubble_Night_10_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_11_en","features.messages.impl.timeline.components_MessageEventBubble_Night_11_en",0,], @@ -461,7 +479,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_8_en","features.messages.impl.timeline.components_MessageEventBubble_Night_8_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_9_en","features.messages.impl.timeline.components_MessageEventBubble_Night_9_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20006,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20014,], ["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,], @@ -469,24 +487,25 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessagesReactionButton_Day_1_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_1_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButton_Day_2_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_2_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButton_Day_3_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_3_en",0,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20010,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20010,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20006,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20006,], -["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20006,], -["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",20006,], -["features.messages.impl_MessagesView_Day_13_en","features.messages.impl_MessagesView_Night_13_en",20006,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20006,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20006,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20006,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20006,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20006,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20006,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20006,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20006,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20006,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20014,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20014,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20017,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20014,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20014,], +["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20014,], +["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",20014,], +["features.messages.impl_MessagesView_Day_13_en","features.messages.impl_MessagesView_Night_13_en",20014,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20014,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20014,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20014,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20014,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20014,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20014,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20014,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20014,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20014,], ["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",20006,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20014,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom Sheets_en","",0,], ["appicon.element_MonochromeIcon_en","",0,], @@ -495,29 +514,29 @@ export const screenshots = [ ["libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple selection List item - selection in trailing content_List items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple selection List item - selection in supporting text_List items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItem_Multiple selection List item - no selection_List items_en","",0,], -["features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Day_0_en","features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Night_0_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20006,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20006,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20006,], +["features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Day_0_en","features.roomlist.impl.components_NativeSlidingSyncMigrationBanner_Night_0_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20014,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20014,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20014,], ["libraries.oidc.impl.webview_OidcView_Day_0_en","libraries.oidc.impl.webview_OidcView_Night_0_en",0,], ["libraries.oidc.impl.webview_OidcView_Day_1_en","libraries.oidc.impl.webview_OidcView_Night_1_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.onboarding.impl_OnBoardingView_Day_0_en","features.onboarding.impl_OnBoardingView_Night_0_en",20006,], -["features.onboarding.impl_OnBoardingView_Day_1_en","features.onboarding.impl_OnBoardingView_Night_1_en",20006,], -["features.onboarding.impl_OnBoardingView_Day_2_en","features.onboarding.impl_OnBoardingView_Night_2_en",20006,], -["features.onboarding.impl_OnBoardingView_Day_3_en","features.onboarding.impl_OnBoardingView_Night_3_en",20006,], -["features.onboarding.impl_OnBoardingView_Day_4_en","features.onboarding.impl_OnBoardingView_Night_4_en",20006,], +["features.onboarding.impl_OnBoardingView_Day_0_en","features.onboarding.impl_OnBoardingView_Night_0_en",20014,], +["features.onboarding.impl_OnBoardingView_Day_1_en","features.onboarding.impl_OnBoardingView_Night_1_en",20014,], +["features.onboarding.impl_OnBoardingView_Day_2_en","features.onboarding.impl_OnBoardingView_Night_2_en",20014,], +["features.onboarding.impl_OnBoardingView_Day_3_en","features.onboarding.impl_OnBoardingView_Night_3_en",20014,], +["features.onboarding.impl_OnBoardingView_Day_4_en","features.onboarding.impl_OnBoardingView_Night_4_en",20014,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], @@ -532,65 +551,65 @@ export const screenshots = [ ["libraries.designsystem.components_PageTitleWithIconFull_Day_3_en","libraries.designsystem.components_PageTitleWithIconFull_Night_3_en",0,], ["libraries.designsystem.components_PageTitleWithIconFull_Day_4_en","libraries.designsystem.components_PageTitleWithIconFull_Night_4_en",0,], ["libraries.designsystem.components_PageTitleWithIconMinimal_Day_0_en","libraries.designsystem.components_PageTitleWithIconMinimal_Night_0_en",0,], -["libraries.mediaviewer.api.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.api.local.pdf_PdfPagesErrorView_Night_0_en",20006,], -["features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Night_0_en",20006,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20006,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20006,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20006,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20006,], +["libraries.mediaviewer.api.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.api.local.pdf_PdfPagesErrorView_Night_0_en",20014,], +["features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Night_0_en",20014,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20014,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20014,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20014,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20014,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_Night_0_en",0,], ["libraries.designsystem.components_PinIcon_Day_0_en","libraries.designsystem.components_PinIcon_Night_0_en",0,], ["features.lockscreen.impl.unlock.keypad_PinKeypad_Day_0_en","features.lockscreen.impl.unlock.keypad_PinKeypad_Night_0_en",0,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20006,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20006,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20014,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20014,], ["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",20006,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20006,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20006,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20006,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20006,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20006,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20006,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20006,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20006,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20006,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20006,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20006,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20006,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20006,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20014,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20014,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20014,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20014,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20014,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20014,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20014,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20014,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20014,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20014,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20014,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20014,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20014,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20014,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20006,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20006,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20006,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20006,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20006,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20014,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20014,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20014,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20014,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20014,], ["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",20006,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20006,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20006,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20006,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20006,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20006,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20006,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20006,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20006,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20006,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20006,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20014,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20014,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20014,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20014,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20014,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20014,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20014,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20014,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20014,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20014,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20014,], ["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,], @@ -607,196 +626,197 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceTextLight_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20006,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20006,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20006,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20006,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20014,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20014,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20014,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20014,], ["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","",20006,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20006,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20014,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20014,], ["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",0,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20006,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20006,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20006,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20006,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20006,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20006,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20006,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20006,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20006,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20006,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20006,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20006,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20006,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20006,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20006,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20006,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20014,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20014,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20014,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20014,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20014,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20014,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20014,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20014,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20014,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20014,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20014,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20014,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20014,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20014,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20014,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20014,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20006,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20006,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20014,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20014,], ["features.rageshake.api.preferences_RageshakePreferencesView_Day_1_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_1_en",0,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20006,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20006,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20006,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20006,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20006,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20006,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20006,], +["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",20014,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20014,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20014,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20014,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20014,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20014,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20014,], ["libraries.designsystem.atomic.atoms_RedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_RedIndicatorAtom_Night_0_en",0,], ["features.messages.impl.timeline.components_ReplySwipeIndicator_Day_0_en","features.messages.impl.timeline.components_ReplySwipeIndicator_Night_0_en",0,], -["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20006,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20006,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20006,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20006,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20006,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20006,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20006,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20006,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20006,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20006,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20006,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20006,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20014,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20014,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20014,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20014,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20014,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20014,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20014,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20014,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20014,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20014,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20014,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20014,], ["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",20006,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20006,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20006,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20006,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",20006,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",20006,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",20006,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",20006,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",20006,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",20006,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",20006,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",20006,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20014,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20014,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20014,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20014,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",20014,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",20014,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",20014,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",20014,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",20014,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",20014,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",20014,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",20014,], ["features.roomaliasresolver.impl_RoomAliasResolverView_Day_0_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_0_en",0,], ["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",0,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20006,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20014,], ["features.roomdetails.impl.components_RoomBadgeNegative_Day_0_en","features.roomdetails.impl.components_RoomBadgeNegative_Night_0_en",0,], ["features.roomdetails.impl.components_RoomBadgeNeutral_Day_0_en","features.roomdetails.impl.components_RoomBadgeNeutral_Night_0_en",0,], ["features.roomdetails.impl.components_RoomBadgePositive_Day_0_en","features.roomdetails.impl.components_RoomBadgePositive_Night_0_en",0,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20006,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20006,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",20006,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",20006,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",20006,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",20006,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",20006,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",20006,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",20006,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",20006,], -["features.roomdetails.impl_RoomDetails_0_en","",20006,], -["features.roomdetails.impl_RoomDetails_10_en","",20006,], -["features.roomdetails.impl_RoomDetails_11_en","",20006,], -["features.roomdetails.impl_RoomDetails_12_en","",20006,], -["features.roomdetails.impl_RoomDetails_13_en","",20006,], -["features.roomdetails.impl_RoomDetails_1_en","",20006,], -["features.roomdetails.impl_RoomDetails_2_en","",20006,], -["features.roomdetails.impl_RoomDetails_3_en","",20006,], -["features.roomdetails.impl_RoomDetails_4_en","",20006,], -["features.roomdetails.impl_RoomDetails_5_en","",20006,], -["features.roomdetails.impl_RoomDetails_6_en","",20006,], -["features.roomdetails.impl_RoomDetails_7_en","",20006,], -["features.roomdetails.impl_RoomDetails_8_en","",20006,], -["features.roomdetails.impl_RoomDetails_9_en","",20006,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20006,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20006,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20006,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20006,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20006,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20006,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20006,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_4_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_4_en",20006,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_5_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_5_en",20006,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_6_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_6_en",20006,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_7_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_7_en",20006,], -["features.roomlist.impl.components_RoomListContentView_Day_0_en","features.roomlist.impl.components_RoomListContentView_Night_0_en",20006,], -["features.roomlist.impl.components_RoomListContentView_Day_1_en","features.roomlist.impl.components_RoomListContentView_Night_1_en",20006,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20014,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20014,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",20014,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",20014,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",20014,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",20014,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",20014,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",20014,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",20014,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",20014,], +["features.roomdetails.impl_RoomDetails_0_en","",20014,], +["features.roomdetails.impl_RoomDetails_10_en","",20014,], +["features.roomdetails.impl_RoomDetails_11_en","",20014,], +["features.roomdetails.impl_RoomDetails_12_en","",20014,], +["features.roomdetails.impl_RoomDetails_13_en","",20014,], +["features.roomdetails.impl_RoomDetails_1_en","",20014,], +["features.roomdetails.impl_RoomDetails_2_en","",20014,], +["features.roomdetails.impl_RoomDetails_3_en","",20014,], +["features.roomdetails.impl_RoomDetails_4_en","",20014,], +["features.roomdetails.impl_RoomDetails_5_en","",20014,], +["features.roomdetails.impl_RoomDetails_6_en","",20014,], +["features.roomdetails.impl_RoomDetails_7_en","",20014,], +["features.roomdetails.impl_RoomDetails_8_en","",20014,], +["features.roomdetails.impl_RoomDetails_9_en","",20014,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20014,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20014,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20014,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20014,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20014,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20014,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20014,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_4_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_4_en",20014,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_5_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_5_en",20014,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_6_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_6_en",20014,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_7_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_7_en",20014,], +["features.roomlist.impl.components_RoomListContentView_Day_0_en","features.roomlist.impl.components_RoomListContentView_Night_0_en",20014,], +["features.roomlist.impl.components_RoomListContentView_Day_1_en","features.roomlist.impl.components_RoomListContentView_Night_1_en",20014,], ["features.roomlist.impl.components_RoomListContentView_Day_2_en","features.roomlist.impl.components_RoomListContentView_Night_2_en",0,], -["features.roomlist.impl.components_RoomListContentView_Day_3_en","features.roomlist.impl.components_RoomListContentView_Night_3_en",20006,], -["features.roomlist.impl.components_RoomListContentView_Day_4_en","features.roomlist.impl.components_RoomListContentView_Night_4_en",20006,], -["features.roomlist.impl.filters_RoomListFiltersView_Day_0_en","features.roomlist.impl.filters_RoomListFiltersView_Night_0_en",20006,], -["features.roomlist.impl.filters_RoomListFiltersView_Day_1_en","features.roomlist.impl.filters_RoomListFiltersView_Night_1_en",20006,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_0_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_0_en",20006,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_1_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_1_en",20006,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_2_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_2_en",20006,], +["features.roomlist.impl.components_RoomListContentView_Day_3_en","features.roomlist.impl.components_RoomListContentView_Night_3_en",20014,], +["features.roomlist.impl.components_RoomListContentView_Day_4_en","features.roomlist.impl.components_RoomListContentView_Night_4_en",20014,], +["features.roomlist.impl.filters_RoomListFiltersView_Day_0_en","features.roomlist.impl.filters_RoomListFiltersView_Night_0_en",20014,], +["features.roomlist.impl.filters_RoomListFiltersView_Day_1_en","features.roomlist.impl.filters_RoomListFiltersView_Night_1_en",20014,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_0_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_0_en",20014,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_1_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_1_en",20014,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_2_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_2_en",20014,], ["features.roomlist.impl.search_RoomListSearchContent_Day_0_en","features.roomlist.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.roomlist.impl.search_RoomListSearchContent_Day_1_en","features.roomlist.impl.search_RoomListSearchContent_Night_1_en",20006,], -["features.roomlist.impl.search_RoomListSearchContent_Day_2_en","features.roomlist.impl.search_RoomListSearchContent_Night_2_en",20006,], -["features.roomlist.impl_RoomListView_Day_0_en","features.roomlist.impl_RoomListView_Night_0_en",20006,], -["features.roomlist.impl_RoomListView_Day_10_en","features.roomlist.impl_RoomListView_Night_10_en",20006,], -["features.roomlist.impl_RoomListView_Day_1_en","features.roomlist.impl_RoomListView_Night_1_en",20006,], -["features.roomlist.impl_RoomListView_Day_2_en","features.roomlist.impl_RoomListView_Night_2_en",20006,], -["features.roomlist.impl_RoomListView_Day_3_en","features.roomlist.impl_RoomListView_Night_3_en",20006,], -["features.roomlist.impl_RoomListView_Day_4_en","features.roomlist.impl_RoomListView_Night_4_en",20006,], -["features.roomlist.impl_RoomListView_Day_5_en","features.roomlist.impl_RoomListView_Night_5_en",20006,], -["features.roomlist.impl_RoomListView_Day_6_en","features.roomlist.impl_RoomListView_Night_6_en",20006,], -["features.roomlist.impl_RoomListView_Day_7_en","features.roomlist.impl_RoomListView_Night_7_en",20006,], +["features.roomlist.impl.search_RoomListSearchContent_Day_1_en","features.roomlist.impl.search_RoomListSearchContent_Night_1_en",20014,], +["features.roomlist.impl.search_RoomListSearchContent_Day_2_en","features.roomlist.impl.search_RoomListSearchContent_Night_2_en",20014,], +["features.roomlist.impl_RoomListView_Day_0_en","features.roomlist.impl_RoomListView_Night_0_en",20014,], +["features.roomlist.impl_RoomListView_Day_10_en","features.roomlist.impl_RoomListView_Night_10_en",20014,], +["features.roomlist.impl_RoomListView_Day_1_en","features.roomlist.impl_RoomListView_Night_1_en",20014,], +["features.roomlist.impl_RoomListView_Day_2_en","features.roomlist.impl_RoomListView_Night_2_en",20014,], +["features.roomlist.impl_RoomListView_Day_3_en","features.roomlist.impl_RoomListView_Night_3_en",20014,], +["features.roomlist.impl_RoomListView_Day_4_en","features.roomlist.impl_RoomListView_Night_4_en",20014,], +["features.roomlist.impl_RoomListView_Day_5_en","features.roomlist.impl_RoomListView_Night_5_en",20014,], +["features.roomlist.impl_RoomListView_Day_6_en","features.roomlist.impl_RoomListView_Night_6_en",20014,], +["features.roomlist.impl_RoomListView_Day_7_en","features.roomlist.impl_RoomListView_Night_7_en",20014,], ["features.roomlist.impl_RoomListView_Day_8_en","features.roomlist.impl_RoomListView_Night_8_en",0,], ["features.roomlist.impl_RoomListView_Day_9_en","features.roomlist.impl_RoomListView_Night_9_en",0,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",20006,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",20006,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",20006,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20006,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20006,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20006,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20006,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20006,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",20014,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",20014,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",20014,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20014,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20014,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20014,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20014,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20014,], ["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",0,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20006,], -["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",20006,], -["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",20006,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20014,], +["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",20014,], +["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",20014,], ["libraries.designsystem.atomic.molecules_RoomMembersCountMolecule_Day_0_en","libraries.designsystem.atomic.molecules_RoomMembersCountMolecule_Night_0_en",0,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en",20006,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en",20006,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en",20006,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en",20006,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en",20006,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en",20006,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en",20006,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_7_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_7_en",20006,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_8_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_8_en",20006,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en",20014,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en",20014,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en",20014,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en",20014,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en",20014,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en",0,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en",20014,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_7_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_7_en",20014,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_8_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_8_en",20014,], ["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_9_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20006,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20006,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20006,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20006,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20006,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20006,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20006,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20006,], -["features.createroom.impl.components_RoomPrivacyOption_Day_0_en","features.createroom.impl.components_RoomPrivacyOption_Night_0_en",20006,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20006,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20006,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20006,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20006,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20006,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20006,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20014,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20014,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20014,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20014,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20014,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20014,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20014,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20014,], +["features.createroom.impl.components_RoomPrivacyOption_Day_0_en","features.createroom.impl.components_RoomPrivacyOption_Night_0_en",20014,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20014,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20014,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20014,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20014,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20014,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20014,], ["features.roomlist.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.roomlist.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_0_en","features.roomlist.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_10_en","features.roomlist.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -819,10 +839,10 @@ export const screenshots = [ ["features.roomlist.impl.components_RoomSummaryRow_Day_26_en","features.roomlist.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_27_en","features.roomlist.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_28_en","features.roomlist.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.roomlist.impl.components_RoomSummaryRow_Day_29_en","features.roomlist.impl.components_RoomSummaryRow_Night_29_en",20006,], -["features.roomlist.impl.components_RoomSummaryRow_Day_2_en","features.roomlist.impl.components_RoomSummaryRow_Night_2_en",20006,], -["features.roomlist.impl.components_RoomSummaryRow_Day_30_en","features.roomlist.impl.components_RoomSummaryRow_Night_30_en",20006,], -["features.roomlist.impl.components_RoomSummaryRow_Day_31_en","features.roomlist.impl.components_RoomSummaryRow_Night_31_en",20006,], +["features.roomlist.impl.components_RoomSummaryRow_Day_29_en","features.roomlist.impl.components_RoomSummaryRow_Night_29_en",20014,], +["features.roomlist.impl.components_RoomSummaryRow_Day_2_en","features.roomlist.impl.components_RoomSummaryRow_Night_2_en",20014,], +["features.roomlist.impl.components_RoomSummaryRow_Day_30_en","features.roomlist.impl.components_RoomSummaryRow_Night_30_en",20014,], +["features.roomlist.impl.components_RoomSummaryRow_Day_31_en","features.roomlist.impl.components_RoomSummaryRow_Night_31_en",20014,], ["features.roomlist.impl.components_RoomSummaryRow_Day_3_en","features.roomlist.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_4_en","features.roomlist.impl.components_RoomSummaryRow_Night_4_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_5_en","features.roomlist.impl.components_RoomSummaryRow_Night_5_en",0,], @@ -830,58 +850,58 @@ export const screenshots = [ ["features.roomlist.impl.components_RoomSummaryRow_Day_7_en","features.roomlist.impl.components_RoomSummaryRow_Night_7_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_8_en","features.roomlist.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_9_en","features.roomlist.impl.components_RoomSummaryRow_Night_9_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20006,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20006,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20006,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20014,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20014,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20014,], ["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",20006,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20006,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20006,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20014,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20014,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20014,], ["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","",20006,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search views_en","",20014,], ["libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search views_en","",0,], ["libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search views_en","",0,], ["libraries.designsystem.theme.components_SearchBarInactive_Search views_en","",0,], -["features.createroom.impl.components_SearchMultipleUsersResultItem_en","",20006,], -["features.createroom.impl.components_SearchSingleUserResultItem_en","",20006,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20006,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20006,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20006,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20006,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_0_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_0_en",20006,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_1_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_1_en",20006,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_2_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_2_en",20006,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20006,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20006,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20006,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20006,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20006,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20006,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20006,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20006,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20006,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20006,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20006,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20006,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20006,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20006,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20006,], +["features.createroom.impl.components_SearchMultipleUsersResultItem_en","",20014,], +["features.createroom.impl.components_SearchSingleUserResultItem_en","",20014,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20014,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20014,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20014,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20014,], +["features.securebackup.impl.enable_SecureBackupEnableView_Day_0_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_0_en",20014,], +["features.securebackup.impl.enable_SecureBackupEnableView_Day_1_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_1_en",20014,], +["features.securebackup.impl.enable_SecureBackupEnableView_Day_2_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_2_en",20014,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20014,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20014,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20014,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20014,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20014,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20014,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20014,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20014,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20014,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20014,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20014,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20014,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20014,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20014,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20014,], ["libraries.matrix.ui.components_SelectedRoom_Day_0_en","libraries.matrix.ui.components_SelectedRoom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoom_Day_1_en","libraries.matrix.ui.components_SelectedRoom_Night_1_en",0,], ["libraries.matrix.ui.components_SelectedRoom_Day_2_en","libraries.matrix.ui.components_SelectedRoom_Night_2_en",0,], @@ -889,11 +909,11 @@ export const screenshots = [ ["libraries.matrix.ui.components_SelectedUser_Day_0_en","libraries.matrix.ui.components_SelectedUser_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedUsersRowList_Day_0_en","libraries.matrix.ui.components_SelectedUsersRowList_Night_0_en",0,], ["libraries.textcomposer.components_SendButton_Day_0_en","libraries.textcomposer.components_SendButton_Night_0_en",0,], -["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",20006,], -["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",20006,], -["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",20006,], -["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",20006,], -["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",20006,], +["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",20014,], +["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",20014,], +["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",20014,], +["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",20014,], +["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",20014,], ["libraries.matrix.ui.messages.sender_SenderName_Day_0_en","libraries.matrix.ui.messages.sender_SenderName_Night_0_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_1_en","libraries.matrix.ui.messages.sender_SenderName_Night_1_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_2_en","libraries.matrix.ui.messages.sender_SenderName_Night_2_en",0,], @@ -903,40 +923,26 @@ export const screenshots = [ ["libraries.matrix.ui.messages.sender_SenderName_Day_6_en","libraries.matrix.ui.messages.sender_SenderName_Night_6_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_7_en","libraries.matrix.ui.messages.sender_SenderName_Night_7_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_8_en","libraries.matrix.ui.messages.sender_SenderName_Night_8_en",0,], -["features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20006,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20006,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20006,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20006,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20006,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20006,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20006,], +["features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20014,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20014,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20014,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20014,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20014,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20014,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20014,], ["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",20006,], -["features.messages.impl.timeline.components.reactionsummary_SheetContent_Day_0_en","features.messages.impl.timeline.components.reactionsummary_SheetContent_Night_0_en",0,], -["features.messages.impl.actionlist_SheetContent_Day_0_en","features.messages.impl.actionlist_SheetContent_Night_0_en",0,], -["features.messages.impl.actionlist_SheetContent_Day_10_en","features.messages.impl.actionlist_SheetContent_Night_10_en",20006,], -["features.messages.impl.actionlist_SheetContent_Day_11_en","features.messages.impl.actionlist_SheetContent_Night_11_en",20006,], -["features.messages.impl.actionlist_SheetContent_Day_12_en","features.messages.impl.actionlist_SheetContent_Night_12_en",20006,], -["features.messages.impl.actionlist_SheetContent_Day_1_en","features.messages.impl.actionlist_SheetContent_Night_1_en",0,], -["features.messages.impl.actionlist_SheetContent_Day_2_en","features.messages.impl.actionlist_SheetContent_Night_2_en",20006,], -["features.messages.impl.actionlist_SheetContent_Day_3_en","features.messages.impl.actionlist_SheetContent_Night_3_en",20006,], -["features.messages.impl.actionlist_SheetContent_Day_4_en","features.messages.impl.actionlist_SheetContent_Night_4_en",20006,], -["features.messages.impl.actionlist_SheetContent_Day_5_en","features.messages.impl.actionlist_SheetContent_Night_5_en",20006,], -["features.messages.impl.actionlist_SheetContent_Day_6_en","features.messages.impl.actionlist_SheetContent_Night_6_en",20006,], -["features.messages.impl.actionlist_SheetContent_Day_7_en","features.messages.impl.actionlist_SheetContent_Night_7_en",20006,], -["features.messages.impl.actionlist_SheetContent_Day_8_en","features.messages.impl.actionlist_SheetContent_Night_8_en",20006,], -["features.messages.impl.actionlist_SheetContent_Day_9_en","features.messages.impl.actionlist_SheetContent_Night_9_en",20006,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20006,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20006,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20006,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20006,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20006,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20006,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20006,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20006,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20006,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20014,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20014,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20014,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20014,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20014,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20014,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20014,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20014,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20014,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20014,], ["libraries.designsystem.components.dialogs_SingleSelectionDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_SingleSelectionDialog_Day_0_en","libraries.designsystem.components.dialogs_SingleSelectionDialog_Night_0_en",0,], ["libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single selection List item - custom formatter_List items_en","",0,], @@ -945,7 +951,7 @@ export const screenshots = [ ["libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single selection List item - no selection, supporting text_List items_en","",0,], ["libraries.designsystem.components.list_SingleSelectionListItem_Single selection List item - no selection_List items_en","",0,], ["libraries.designsystem.theme.components_Sliders_Sliders_en","",0,], -["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20006,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20014,], ["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,], @@ -955,37 +961,37 @@ export const screenshots = [ ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], ["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",0,], -["features.location.api.internal_StaticMapPlaceholder_Day_1_en","features.location.api.internal_StaticMapPlaceholder_Night_1_en",20006,], +["features.location.api.internal_StaticMapPlaceholder_Day_1_en","features.location.api.internal_StaticMapPlaceholder_Night_1_en",20014,], ["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",20006,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20014,], ["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",20006,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20014,], ["libraries.designsystem.theme.components_TextButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonSmall_Buttons_en","",0,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20006,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20006,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20006,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20006,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20006,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20006,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20006,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20014,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20014,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20014,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20014,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20014,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20014,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20014,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], ["libraries.designsystem.theme.components_TextFieldDark_TextFields_en","",0,], @@ -997,26 +1003,26 @@ export const screenshots = [ ["libraries.designsystem.theme.components_TextFieldValueTextFieldDark_TextFields_en","",0,], ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en","",20006,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en","",20006,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en","",20006,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en","",20014,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en","",20014,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en","",20014,], ["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",20006,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20006,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20014,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20014,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_5_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_6_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_6_en",0,], ["features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20006,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20014,], ["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",20006,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20006,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20006,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20014,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20014,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20014,], ["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,], @@ -1024,16 +1030,16 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20006,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20006,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20014,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20014,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_6_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20006,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20014,], ["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",20006,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20006,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20014,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20014,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en",0,], @@ -1042,38 +1048,38 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20006,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20014,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20006,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20014,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRow_Night_0_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20006,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20014,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20006,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20006,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20014,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20014,], ["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_2_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemInformativeView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemInformativeView_Night_0_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20006,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20014,], ["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20006,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20006,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20006,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20006,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20006,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20014,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20014,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20014,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20014,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20014,], ["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",20006,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20006,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20014,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20014,], ["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",20006,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20014,], ["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,], @@ -1082,8 +1088,8 @@ export const screenshots = [ ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_5_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_5_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_6_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_6_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_7_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_7_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20006,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20006,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20014,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20014,], ["features.messages.impl.timeline.components_TimelineItemStateEventRow_Day_0_en","features.messages.impl.timeline.components_TimelineItemStateEventRow_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemStateView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemStateView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemStickerView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemStickerView_Night_0_en",0,], @@ -1096,7 +1102,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_4_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_5_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20006,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20014,], ["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_1_en",0,], @@ -1120,84 +1126,84 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_9_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en",0,], -["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20006,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20006,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20014,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20014,], ["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",0,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20006,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20006,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20006,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20006,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20006,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20006,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20006,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20006,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20014,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20014,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20014,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20014,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20014,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20014,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20014,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20014,], ["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",20006,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20014,], ["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",20006,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20014,], ["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",20006,], +["features.messages.impl.timeline_TimelineView_Day_8_en","features.messages.impl.timeline_TimelineView_Night_8_en",20014,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.theme.components_TopAppBar_App Bars_en","",0,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20006,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20006,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20006,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20006,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20006,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20006,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20006,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20006,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20014,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20014,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20014,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20014,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20014,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20014,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20014,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20014,], ["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",20006,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20006,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20006,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20006,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20006,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20006,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20014,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20014,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20014,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20014,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20014,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20014,], ["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","",20006,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20014,], ["libraries.matrix.ui.components_UnsavedAvatar_Day_0_en","libraries.matrix.ui.components_UnsavedAvatar_Night_0_en",0,], ["libraries.designsystem.components.avatar_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20006,], -["features.createroom.impl.components_UserListView_Day_0_en","features.createroom.impl.components_UserListView_Night_0_en",20006,], -["features.createroom.impl.components_UserListView_Day_1_en","features.createroom.impl.components_UserListView_Night_1_en",20006,], -["features.createroom.impl.components_UserListView_Day_2_en","features.createroom.impl.components_UserListView_Night_2_en",20006,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20014,], +["features.createroom.impl.components_UserListView_Day_0_en","features.createroom.impl.components_UserListView_Night_0_en",20014,], +["features.createroom.impl.components_UserListView_Day_1_en","features.createroom.impl.components_UserListView_Night_1_en",20014,], +["features.createroom.impl.components_UserListView_Day_2_en","features.createroom.impl.components_UserListView_Night_2_en",20014,], ["features.createroom.impl.components_UserListView_Day_3_en","features.createroom.impl.components_UserListView_Night_3_en",0,], ["features.createroom.impl.components_UserListView_Day_4_en","features.createroom.impl.components_UserListView_Night_4_en",0,], ["features.createroom.impl.components_UserListView_Day_5_en","features.createroom.impl.components_UserListView_Night_5_en",0,], ["features.createroom.impl.components_UserListView_Day_6_en","features.createroom.impl.components_UserListView_Night_6_en",0,], -["features.createroom.impl.components_UserListView_Day_7_en","features.createroom.impl.components_UserListView_Night_7_en",20006,], +["features.createroom.impl.components_UserListView_Day_7_en","features.createroom.impl.components_UserListView_Night_7_en",20014,], ["features.createroom.impl.components_UserListView_Day_8_en","features.createroom.impl.components_UserListView_Night_8_en",0,], -["features.createroom.impl.components_UserListView_Day_9_en","features.createroom.impl.components_UserListView_Night_9_en",20006,], +["features.createroom.impl.components_UserListView_Day_9_en","features.createroom.impl.components_UserListView_Night_9_en",20014,], ["features.preferences.impl.user_UserPreferences_Day_0_en","features.preferences.impl.user_UserPreferences_Night_0_en",0,], ["features.preferences.impl.user_UserPreferences_Day_1_en","features.preferences.impl.user_UserPreferences_Night_1_en",0,], ["features.preferences.impl.user_UserPreferences_Day_2_en","features.preferences.impl.user_UserPreferences_Night_2_en",0,], ["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",0,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20006,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20006,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20006,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20006,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20006,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20006,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20006,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20006,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20006,], -["features.verifysession.impl_VerifySelfSessionView_Day_0_en","features.verifysession.impl_VerifySelfSessionView_Night_0_en",20006,], -["features.verifysession.impl_VerifySelfSessionView_Day_10_en","features.verifysession.impl_VerifySelfSessionView_Night_10_en",20006,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20014,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20014,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20014,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20014,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20014,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20014,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20014,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20014,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20014,], +["features.verifysession.impl_VerifySelfSessionView_Day_0_en","features.verifysession.impl_VerifySelfSessionView_Night_0_en",20014,], +["features.verifysession.impl_VerifySelfSessionView_Day_10_en","features.verifysession.impl_VerifySelfSessionView_Night_10_en",20014,], ["features.verifysession.impl_VerifySelfSessionView_Day_11_en","features.verifysession.impl_VerifySelfSessionView_Night_11_en",0,], ["features.verifysession.impl_VerifySelfSessionView_Day_12_en","features.verifysession.impl_VerifySelfSessionView_Night_12_en",0,], -["features.verifysession.impl_VerifySelfSessionView_Day_1_en","features.verifysession.impl_VerifySelfSessionView_Night_1_en",20006,], -["features.verifysession.impl_VerifySelfSessionView_Day_2_en","features.verifysession.impl_VerifySelfSessionView_Night_2_en",20006,], -["features.verifysession.impl_VerifySelfSessionView_Day_3_en","features.verifysession.impl_VerifySelfSessionView_Night_3_en",20006,], -["features.verifysession.impl_VerifySelfSessionView_Day_4_en","features.verifysession.impl_VerifySelfSessionView_Night_4_en",20006,], -["features.verifysession.impl_VerifySelfSessionView_Day_5_en","features.verifysession.impl_VerifySelfSessionView_Night_5_en",20006,], -["features.verifysession.impl_VerifySelfSessionView_Day_6_en","features.verifysession.impl_VerifySelfSessionView_Night_6_en",20006,], -["features.verifysession.impl_VerifySelfSessionView_Day_7_en","features.verifysession.impl_VerifySelfSessionView_Night_7_en",20006,], -["features.verifysession.impl_VerifySelfSessionView_Day_8_en","features.verifysession.impl_VerifySelfSessionView_Night_8_en",20006,], -["features.verifysession.impl_VerifySelfSessionView_Day_9_en","features.verifysession.impl_VerifySelfSessionView_Night_9_en",20006,], +["features.verifysession.impl_VerifySelfSessionView_Day_1_en","features.verifysession.impl_VerifySelfSessionView_Night_1_en",20014,], +["features.verifysession.impl_VerifySelfSessionView_Day_2_en","features.verifysession.impl_VerifySelfSessionView_Night_2_en",20014,], +["features.verifysession.impl_VerifySelfSessionView_Day_3_en","features.verifysession.impl_VerifySelfSessionView_Night_3_en",20014,], +["features.verifysession.impl_VerifySelfSessionView_Day_4_en","features.verifysession.impl_VerifySelfSessionView_Night_4_en",20014,], +["features.verifysession.impl_VerifySelfSessionView_Day_5_en","features.verifysession.impl_VerifySelfSessionView_Night_5_en",20014,], +["features.verifysession.impl_VerifySelfSessionView_Day_6_en","features.verifysession.impl_VerifySelfSessionView_Night_6_en",20014,], +["features.verifysession.impl_VerifySelfSessionView_Day_7_en","features.verifysession.impl_VerifySelfSessionView_Night_7_en",20014,], +["features.verifysession.impl_VerifySelfSessionView_Day_8_en","features.verifysession.impl_VerifySelfSessionView_Night_8_en",20014,], +["features.verifysession.impl_VerifySelfSessionView_Day_9_en","features.verifysession.impl_VerifySelfSessionView_Night_9_en",20014,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_0_en","features.viewfolder.impl.file_ViewFileView_Night_0_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_1_en","features.viewfolder.impl.file_ViewFileView_Night_1_en",0,], @@ -1212,6 +1218,6 @@ export const screenshots = [ ["libraries.textcomposer.components_VoiceMessageRecording_Day_0_en","libraries.textcomposer.components_VoiceMessageRecording_Night_0_en",0,], ["libraries.textcomposer.components_VoiceMessage_Day_0_en","libraries.textcomposer.components_VoiceMessage_Night_0_en",0,], ["libraries.designsystem.components.media_WaveformPlaybackView_Day_0_en","libraries.designsystem.components.media_WaveformPlaybackView_Night_0_en",0,], -["features.ftue.impl.welcome_WelcomeView_Day_0_en","features.ftue.impl.welcome_WelcomeView_Night_0_en",20006,], +["features.ftue.impl.welcome_WelcomeView_Day_0_en","features.ftue.impl.welcome_WelcomeView_Night_0_en",20014,], ["libraries.designsystem.ruler_WithRulers_Day_0_en","libraries.designsystem.ruler_WithRulers_Night_0_en",0,], ]; diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png index 98180841e0..de60f4f118 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b59a279bbd690657950c21aaf5dd536073e95485cf22f737e3080cfbabbdade -size 117054 +oid sha256:68aa9bd1630bfe084c3cb54449946c95fdfc4fec4190453a648ffbb738c50c02 +size 118338 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png index 805e3e48ed..0b77bfc7a4 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:524cabb4d55590237ffad166d0ebc2fb14dfaef0bfa040916d4f7e727a70bb0a -size 103550 +oid sha256:159cdb25a248a86f348ec72a1af680a14d6fdf16d7d268649ef324f8296b0356 +size 104708