mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 20:38:40 +00:00
Merge pull request #7176 from bxdxnn/misc/gallery-reply-text
Add item count text as the placeholder for in reply to box for galleries
This commit is contained in:
+50
@@ -10,12 +10,15 @@ package io.element.android.libraries.matrix.ui.messages.reply
|
|||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.compose.ui.res.pluralStringResource
|
||||||
import androidx.compose.ui.res.stringResource
|
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.AudioMessageType
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.CallNotifyContent
|
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.FailedToParseMessageLikeContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
|
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.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.ImageMessageType
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.LegacyCallInviteContent
|
import io.element.android.libraries.matrix.api.timeline.item.event.LegacyCallInviteContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.LiveLocationContent
|
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.components.AttachmentThumbnailType
|
||||||
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToMetadata.Text
|
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.matrix.ui.messages.reply.InReplyToMetadata.Thumbnail
|
||||||
|
import io.element.android.libraries.ui.strings.CommonPlurals
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -104,6 +108,52 @@ internal fun InReplyToDetails.Ready.metadata(hideImage: Boolean): InReplyToMetad
|
|||||||
type = AttachmentThumbnailType.Voice,
|
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)
|
else -> Text(textContent ?: eventContent.body)
|
||||||
}
|
}
|
||||||
is StickerContent -> Thumbnail(
|
is StickerContent -> Thumbnail(
|
||||||
|
|||||||
+223
@@ -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.FailedToParseMessageLikeContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
|
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.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.ImageMessageType
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType
|
import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
|
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 org.junit.Test
|
||||||
import kotlin.time.Duration.Companion.minutes
|
import kotlin.time.Duration.Companion.minutes
|
||||||
|
|
||||||
|
@Suppress("LargeClass")
|
||||||
class InReplyToMetadataKtTest : RobolectricTest() {
|
class InReplyToMetadataKtTest : RobolectricTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `any message content`() = runTest {
|
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
|
@Test
|
||||||
fun `a poll content`() = runTest {
|
fun `a poll content`() = runTest {
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|||||||
@@ -268,6 +268,14 @@ Reason: %1$s."</string>
|
|||||||
<string name="common_forward_message">"Forward message"</string>
|
<string name="common_forward_message">"Forward message"</string>
|
||||||
<string name="common_frequently_used">"Frequently used"</string>
|
<string name="common_frequently_used">"Frequently used"</string>
|
||||||
<string name="common_gallery">"Gallery"</string>
|
<string name="common_gallery">"Gallery"</string>
|
||||||
|
<plurals name="common_gallery_reply_attachments">
|
||||||
|
<item quantity="one">"%1$d attachment…"</item>
|
||||||
|
<item quantity="other">"%1$d attachments…"</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="common_gallery_reply_media_items">
|
||||||
|
<item quantity="one">"%1$d media item…"</item>
|
||||||
|
<item quantity="other">"%1$d media items…"</item>
|
||||||
|
</plurals>
|
||||||
<string name="common_gif">"GIF"</string>
|
<string name="common_gif">"GIF"</string>
|
||||||
<string name="common_group_call_in_progress">"Group call in progress"</string>
|
<string name="common_group_call_in_progress">"Group call in progress"</string>
|
||||||
<string name="common_image">"Image"</string>
|
<string name="common_image">"Image"</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user