diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadata.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadata.kt index 9e5e468cd9..3e3f46ffb5 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadata.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadata.kt @@ -10,12 +10,15 @@ package io.element.android.libraries.matrix.ui.messages.reply import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable +import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType import io.element.android.libraries.matrix.api.timeline.item.event.CallNotifyContent import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.GalleryItemType +import io.element.android.libraries.matrix.api.timeline.item.event.GalleryMessageType import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType import io.element.android.libraries.matrix.api.timeline.item.event.LegacyCallInviteContent import io.element.android.libraries.matrix.api.timeline.item.event.LiveLocationContent @@ -35,6 +38,7 @@ import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType import io.element.android.libraries.matrix.ui.messages.reply.InReplyToMetadata.Text import io.element.android.libraries.matrix.ui.messages.reply.InReplyToMetadata.Thumbnail +import io.element.android.libraries.ui.strings.CommonPlurals import io.element.android.libraries.ui.strings.CommonStrings @Immutable @@ -104,6 +108,52 @@ internal fun InReplyToDetails.Ready.metadata(hideImage: Boolean): InReplyToMetad type = AttachmentThumbnailType.Voice, ) ) + is GalleryMessageType -> { + val caption = textContent?.takeIf { it.isNotBlank() } + val isMediaGallery = type.items.all { it is GalleryItemType.Image || it is GalleryItemType.Video } + if (isMediaGallery) { + val text = caption ?: pluralStringResource( + CommonPlurals.common_gallery_reply_media_items, + type.items.size, + type.items.size, + ) + val firstMediaItem = type.items.firstOrNull { it is GalleryItemType.Image || it is GalleryItemType.Video } + val thumbnailSource = when (firstMediaItem) { + is GalleryItemType.Image -> (firstMediaItem.content.info?.thumbnailSource ?: firstMediaItem.content.source).takeUnless { hideImage } + is GalleryItemType.Video -> firstMediaItem.content.info?.thumbnailSource?.takeUnless { hideImage } + else -> null + } + val blurHash = when (firstMediaItem) { + is GalleryItemType.Image -> firstMediaItem.content.info?.blurhash + is GalleryItemType.Video -> firstMediaItem.content.info?.blurhash + else -> null + } + val type = when (firstMediaItem) { + is GalleryItemType.Video -> AttachmentThumbnailType.Video + else -> AttachmentThumbnailType.Image + } + Thumbnail( + AttachmentThumbnailInfo( + thumbnailSource = thumbnailSource, + textContent = text, + type = type, + blurHash = blurHash, + ) + ) + } else { + val text = caption ?: pluralStringResource( + CommonPlurals.common_gallery_reply_attachments, + type.items.size, + type.items.size, + ) + Thumbnail( + AttachmentThumbnailInfo( + textContent = text, + type = AttachmentThumbnailType.File, + ) + ) + } + } else -> Text(textContent ?: eventContent.body) } is StickerContent -> Thumbnail( 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 0847315867..3fcb2f8f18 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 @@ -24,6 +24,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventContent import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.GalleryItemType +import io.element.android.libraries.matrix.api.timeline.item.event.GalleryMessageType import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType import io.element.android.libraries.matrix.api.timeline.item.event.OtherState @@ -52,6 +54,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import kotlin.time.Duration.Companion.minutes +@Suppress("LargeClass") class InReplyToMetadataKtTest : RobolectricTest() { @Test fun `any message content`() = runTest { @@ -407,6 +410,226 @@ class InReplyToMetadataKtTest : RobolectricTest() { } } + @Test + fun `a gallery message with all media items shows media count`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + withConfigurationAndContext { + anInReplyToDetailsReady( + eventContent = aMessageContent( + messageType = GalleryMessageType( + body = "", + formatted = null, + items = listOf( + GalleryItemType.Image( + ImageMessageType( + filename = "image1.jpg", + caption = null, + formattedCaption = null, + source = aMediaSource(), + info = anImageInfo(), + ) + ), + GalleryItemType.Image( + ImageMessageType( + filename = "image2.jpg", + caption = null, + formattedCaption = null, + source = aMediaSource(), + info = anImageInfo(), + ) + ), + ), + ) + ), + textContent = "", + ).metadata(hideImage = false) + } + }.test { + awaitItem().let { + assertThat(it).isEqualTo( + InReplyToMetadata.Thumbnail( + attachmentThumbnailInfo = AttachmentThumbnailInfo( + thumbnailSource = aMediaSource(), + textContent = "2 media items…", + type = AttachmentThumbnailType.Image, + blurHash = A_BLUR_HASH, + ) + ) + ) + } + } + } + + @Test + fun `a gallery message with attachment items shows attachment count`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + withConfigurationAndContext { + anInReplyToDetailsReady( + eventContent = aMessageContent( + messageType = GalleryMessageType( + body = "", + formatted = null, + items = listOf( + GalleryItemType.File( + FileMessageType( + filename = "doc1.pdf", + caption = null, + formattedCaption = null, + source = aMediaSource(), + info = FileInfo( + mimetype = null, + size = null, + thumbnailInfo = null, + thumbnailSource = null, + ), + ) + ), + GalleryItemType.File( + FileMessageType( + filename = "doc2.pdf", + caption = null, + formattedCaption = null, + source = aMediaSource(), + info = FileInfo( + mimetype = null, + size = null, + thumbnailInfo = null, + thumbnailSource = null, + ), + ) + ), + ), + ) + ), + textContent = "", + ).metadata(hideImage = false) + } + }.test { + awaitItem().let { + assertThat(it).isEqualTo( + InReplyToMetadata.Thumbnail( + attachmentThumbnailInfo = AttachmentThumbnailInfo( + thumbnailSource = null, + textContent = "2 attachments…", + type = AttachmentThumbnailType.File, + blurHash = null, + ) + ) + ) + } + } + } + + @Test + fun `a gallery message with caption shows caption instead of count`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + withConfigurationAndContext { + anInReplyToDetailsReady( + eventContent = aMessageContent( + messageType = GalleryMessageType( + body = "My vacation photos", + formatted = null, + items = listOf( + GalleryItemType.Image( + ImageMessageType( + filename = "image1.jpg", + caption = null, + formattedCaption = null, + source = aMediaSource(), + info = anImageInfo(), + ) + ), + GalleryItemType.Image( + ImageMessageType( + filename = "image2.jpg", + caption = null, + formattedCaption = null, + source = aMediaSource(), + info = anImageInfo(), + ) + ), + ), + ) + ), + textContent = "My vacation photos", + ).metadata(hideImage = false) + } + }.test { + awaitItem().let { + assertThat(it).isEqualTo( + InReplyToMetadata.Thumbnail( + attachmentThumbnailInfo = AttachmentThumbnailInfo( + thumbnailSource = aMediaSource(), + textContent = "My vacation photos", + type = AttachmentThumbnailType.Image, + blurHash = A_BLUR_HASH, + ) + ) + ) + } + } + } + + @Test + fun `a gallery message with attachment items and caption shows caption instead of count`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + withConfigurationAndContext { + anInReplyToDetailsReady( + eventContent = aMessageContent( + messageType = GalleryMessageType( + body = "My documents", + formatted = null, + items = listOf( + GalleryItemType.File( + FileMessageType( + filename = "doc1.pdf", + caption = null, + formattedCaption = null, + source = aMediaSource(), + info = FileInfo( + mimetype = null, + size = null, + thumbnailInfo = null, + thumbnailSource = null, + ), + ) + ), + GalleryItemType.File( + FileMessageType( + filename = "doc2.pdf", + caption = null, + formattedCaption = null, + source = aMediaSource(), + info = FileInfo( + mimetype = null, + size = null, + thumbnailInfo = null, + thumbnailSource = null, + ), + ) + ), + ), + ) + ), + textContent = "My documents", + ).metadata(hideImage = false) + } + }.test { + awaitItem().let { + assertThat(it).isEqualTo( + InReplyToMetadata.Thumbnail( + attachmentThumbnailInfo = AttachmentThumbnailInfo( + thumbnailSource = null, + textContent = "My documents", + type = AttachmentThumbnailType.File, + blurHash = null, + ) + ) + ) + } + } + } + @Test fun `a poll content`() = runTest { moleculeFlow(RecompositionMode.Immediate) { diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 82acfd14b3..b032c41d4b 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -268,6 +268,14 @@ Reason: %1$s." "Forward message" "Frequently used" "Gallery" + + "%1$d attachment…" + "%1$d attachments…" + + + "%1$d media item…" + "%1$d media items…" + "GIF" "Group call in progress" "Image"