Merge TimelineItemEventRow's textForInReplyTo and attachmentThumbnailInfoForInReplyTo functions (#1859)
The flow is somewhat misleading so its logic has been merged into `InReplyToDetails.metadata()`.
This commit is contained in:
parent
de3362798e
commit
c33c0f583c
5 changed files with 445 additions and 103 deletions
|
|
@ -23,6 +23,11 @@ plugins {
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "io.element.android.features.messages.impl"
|
namespace = "io.element.android.features.messages.impl"
|
||||||
|
testOptions {
|
||||||
|
unitTests {
|
||||||
|
isIncludeAndroidResources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
anvil {
|
anvil {
|
||||||
|
|
@ -88,6 +93,8 @@ dependencies {
|
||||||
testImplementation(projects.libraries.voicerecorder.test)
|
testImplementation(projects.libraries.voicerecorder.test)
|
||||||
testImplementation(projects.libraries.mediaplayer.test)
|
testImplementation(projects.libraries.mediaplayer.test)
|
||||||
testImplementation(libs.test.mockk)
|
testImplementation(libs.test.mockk)
|
||||||
|
testImplementation(libs.test.junitext)
|
||||||
|
testImplementation(libs.test.robolectric)
|
||||||
|
|
||||||
ksp(libs.showkase.processor)
|
ksp(libs.showkase.processor)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
import androidx.constraintlayout.compose.ConstrainScope
|
import androidx.constraintlayout.compose.ConstrainScope
|
||||||
import androidx.constraintlayout.compose.ConstraintLayout
|
import androidx.constraintlayout.compose.ConstraintLayout
|
||||||
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||||
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
||||||
|
|
@ -66,6 +67,7 @@ import io.element.android.features.messages.impl.timeline.components.event.toExt
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState
|
import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView
|
import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView
|
||||||
import io.element.android.features.messages.impl.timeline.model.InReplyToDetails
|
import io.element.android.features.messages.impl.timeline.model.InReplyToDetails
|
||||||
|
import io.element.android.features.messages.impl.timeline.model.InReplyToMetadata
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
|
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
|
||||||
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
|
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
|
||||||
|
|
@ -75,6 +77,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||||
|
import io.element.android.features.messages.impl.timeline.model.metadata
|
||||||
import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
|
import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
|
||||||
import io.element.android.libraries.designsystem.components.EqualWidthColumn
|
import io.element.android.libraries.designsystem.components.EqualWidthColumn
|
||||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||||
|
|
@ -89,18 +92,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
|
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
|
|
||||||
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.MessageContent
|
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
|
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
|
|
||||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
|
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
|
||||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
|
|
||||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
|
||||||
import io.element.android.compound.theme.ElementTheme
|
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
@ -538,13 +530,10 @@ private fun MessageEventBubbleContent(
|
||||||
}
|
}
|
||||||
val inReplyTo = @Composable { inReplyTo: InReplyToDetails ->
|
val inReplyTo = @Composable { inReplyTo: InReplyToDetails ->
|
||||||
val senderName = inReplyTo.senderDisplayName ?: inReplyTo.senderId.value
|
val senderName = inReplyTo.senderDisplayName ?: inReplyTo.senderId.value
|
||||||
val attachmentThumbnailInfo = attachmentThumbnailInfoForInReplyTo(inReplyTo)
|
|
||||||
val text = textForInReplyTo(inReplyTo)
|
|
||||||
val topPadding = if (showThreadDecoration) 0.dp else 8.dp
|
val topPadding = if (showThreadDecoration) 0.dp else 8.dp
|
||||||
ReplyToContent(
|
ReplyToContent(
|
||||||
senderName = senderName,
|
senderName = senderName,
|
||||||
text = text,
|
metadata = inReplyTo.metadata(),
|
||||||
attachmentThumbnailInfo = attachmentThumbnailInfo,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = topPadding, start = 8.dp, end = 8.dp)
|
.padding(top = topPadding, start = 8.dp, end = 8.dp)
|
||||||
.clip(RoundedCornerShape(6.dp))
|
.clip(RoundedCornerShape(6.dp))
|
||||||
|
|
@ -585,11 +574,10 @@ private fun MessageEventBubbleContent(
|
||||||
@Composable
|
@Composable
|
||||||
private fun ReplyToContent(
|
private fun ReplyToContent(
|
||||||
senderName: String,
|
senderName: String,
|
||||||
text: String?,
|
metadata: InReplyToMetadata?,
|
||||||
attachmentThumbnailInfo: AttachmentThumbnailInfo?,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val paddings = if (attachmentThumbnailInfo != null) {
|
val paddings = if (metadata is InReplyToMetadata.Thumbnail) {
|
||||||
PaddingValues(start = 4.dp, end = 12.dp, top = 4.dp, bottom = 4.dp)
|
PaddingValues(start = 4.dp, end = 12.dp, top = 4.dp, bottom = 4.dp)
|
||||||
} else {
|
} else {
|
||||||
PaddingValues(horizontal = 12.dp, vertical = 4.dp)
|
PaddingValues(horizontal = 12.dp, vertical = 4.dp)
|
||||||
|
|
@ -599,9 +587,9 @@ private fun ReplyToContent(
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
.padding(paddings)
|
.padding(paddings)
|
||||||
) {
|
) {
|
||||||
if (attachmentThumbnailInfo != null) {
|
if (metadata is InReplyToMetadata.Thumbnail) {
|
||||||
AttachmentThumbnail(
|
AttachmentThumbnail(
|
||||||
info = attachmentThumbnailInfo,
|
info = metadata.attachmentThumbnailInfo,
|
||||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(36.dp)
|
.size(36.dp)
|
||||||
|
|
@ -619,7 +607,7 @@ private fun ReplyToContent(
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = text.orEmpty(),
|
text = metadata?.text.orEmpty(),
|
||||||
style = ElementTheme.typography.fontBodyMdRegular,
|
style = ElementTheme.typography.fontBodyMdRegular,
|
||||||
textAlign = TextAlign.Start,
|
textAlign = TextAlign.Start,
|
||||||
color = ElementTheme.materialColors.secondary,
|
color = ElementTheme.materialColors.secondary,
|
||||||
|
|
@ -630,60 +618,6 @@ private fun ReplyToContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun attachmentThumbnailInfoForInReplyTo(inReplyTo: InReplyToDetails): AttachmentThumbnailInfo? {
|
|
||||||
return when (val eventContent = inReplyTo.eventContent) {
|
|
||||||
is MessageContent -> when (val type = eventContent.type) {
|
|
||||||
is ImageMessageType -> AttachmentThumbnailInfo(
|
|
||||||
thumbnailSource = type.info?.thumbnailSource ?: type.source,
|
|
||||||
textContent = eventContent.body,
|
|
||||||
type = AttachmentThumbnailType.Image,
|
|
||||||
blurHash = type.info?.blurhash,
|
|
||||||
)
|
|
||||||
is VideoMessageType -> AttachmentThumbnailInfo(
|
|
||||||
thumbnailSource = type.info?.thumbnailSource,
|
|
||||||
textContent = eventContent.body,
|
|
||||||
type = AttachmentThumbnailType.Video,
|
|
||||||
blurHash = type.info?.blurhash,
|
|
||||||
)
|
|
||||||
is FileMessageType -> AttachmentThumbnailInfo(
|
|
||||||
thumbnailSource = type.info?.thumbnailSource,
|
|
||||||
textContent = eventContent.body,
|
|
||||||
type = AttachmentThumbnailType.File,
|
|
||||||
)
|
|
||||||
is LocationMessageType -> AttachmentThumbnailInfo(
|
|
||||||
textContent = eventContent.body,
|
|
||||||
type = AttachmentThumbnailType.Location,
|
|
||||||
)
|
|
||||||
is AudioMessageType -> AttachmentThumbnailInfo(
|
|
||||||
textContent = eventContent.body,
|
|
||||||
type = AttachmentThumbnailType.Audio,
|
|
||||||
)
|
|
||||||
is VoiceMessageType -> AttachmentThumbnailInfo(
|
|
||||||
type = AttachmentThumbnailType.Voice,
|
|
||||||
)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
is PollContent -> AttachmentThumbnailInfo(
|
|
||||||
textContent = eventContent.question,
|
|
||||||
type = AttachmentThumbnailType.Poll,
|
|
||||||
)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun textForInReplyTo(inReplyTo: InReplyToDetails): String {
|
|
||||||
return when (val eventContent = inReplyTo.eventContent) {
|
|
||||||
is MessageContent -> when (eventContent.type) {
|
|
||||||
is LocationMessageType -> stringResource(CommonStrings.common_shared_location)
|
|
||||||
is VoiceMessageType -> stringResource(CommonStrings.common_voice_message)
|
|
||||||
else -> inReplyTo.textContent ?: eventContent.body
|
|
||||||
}
|
|
||||||
is PollContent -> eventContent.question
|
|
||||||
else -> ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun TimelineItemEventRowPreview() = ElementPreview {
|
internal fun TimelineItemEventRowPreview() = ElementPreview {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.messages.impl.timeline.model
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
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.FileMessageType
|
||||||
|
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.MessageContent
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
|
||||||
|
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
|
||||||
|
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
internal sealed interface InReplyToMetadata {
|
||||||
|
|
||||||
|
val text: String?
|
||||||
|
|
||||||
|
data class Thumbnail(
|
||||||
|
val attachmentThumbnailInfo: AttachmentThumbnailInfo
|
||||||
|
) : InReplyToMetadata {
|
||||||
|
override val text: String? = attachmentThumbnailInfo.textContent
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Text(
|
||||||
|
override val text: String
|
||||||
|
) : InReplyToMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes metadata for the in reply to message.
|
||||||
|
*
|
||||||
|
* Metadata can be either a thumbnail with a text OR just a text.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
internal fun InReplyToDetails.metadata(): InReplyToMetadata? = when (eventContent) {
|
||||||
|
is MessageContent -> when (val type = eventContent.type) {
|
||||||
|
is ImageMessageType -> InReplyToMetadata.Thumbnail(
|
||||||
|
AttachmentThumbnailInfo(
|
||||||
|
thumbnailSource = type.info?.thumbnailSource ?: type.source,
|
||||||
|
textContent = eventContent.body,
|
||||||
|
type = AttachmentThumbnailType.Image,
|
||||||
|
blurHash = type.info?.blurhash,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
is VideoMessageType -> InReplyToMetadata.Thumbnail(
|
||||||
|
AttachmentThumbnailInfo(
|
||||||
|
thumbnailSource = type.info?.thumbnailSource,
|
||||||
|
textContent = eventContent.body,
|
||||||
|
type = AttachmentThumbnailType.Video,
|
||||||
|
blurHash = type.info?.blurhash,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
is FileMessageType -> InReplyToMetadata.Thumbnail(
|
||||||
|
AttachmentThumbnailInfo(
|
||||||
|
thumbnailSource = type.info?.thumbnailSource,
|
||||||
|
textContent = eventContent.body,
|
||||||
|
type = AttachmentThumbnailType.File,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
is LocationMessageType -> InReplyToMetadata.Thumbnail(
|
||||||
|
AttachmentThumbnailInfo(
|
||||||
|
textContent = stringResource(CommonStrings.common_shared_location),
|
||||||
|
type = AttachmentThumbnailType.Location,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
is AudioMessageType -> InReplyToMetadata.Thumbnail(
|
||||||
|
AttachmentThumbnailInfo(
|
||||||
|
textContent = eventContent.body,
|
||||||
|
type = AttachmentThumbnailType.Audio,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
is VoiceMessageType -> InReplyToMetadata.Thumbnail(
|
||||||
|
AttachmentThumbnailInfo(
|
||||||
|
textContent = stringResource(CommonStrings.common_voice_message),
|
||||||
|
type = AttachmentThumbnailType.Voice,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else -> InReplyToMetadata.Text(textContent ?: eventContent.body)
|
||||||
|
}
|
||||||
|
is PollContent -> InReplyToMetadata.Thumbnail(
|
||||||
|
AttachmentThumbnailInfo(
|
||||||
|
textContent = eventContent.question,
|
||||||
|
type = AttachmentThumbnailType.Poll,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.model
|
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|
||||||
|
|
||||||
internal class TimelineItemGroupPositionProvider : PreviewParameterProvider<TimelineItemGroupPosition> {
|
|
||||||
override val values = sequenceOf(
|
|
||||||
TimelineItemGroupPosition.First,
|
|
||||||
TimelineItemGroupPosition.Middle,
|
|
||||||
TimelineItemGroupPosition.Last,
|
|
||||||
TimelineItemGroupPosition.None,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,321 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.messages.impl.timeline.model
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import app.cash.molecule.RecompositionMode
|
||||||
|
import app.cash.molecule.moleculeFlow
|
||||||
|
import app.cash.turbine.test
|
||||||
|
import com.google.common.truth.Truth
|
||||||
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||||
|
import io.element.android.libraries.matrix.api.media.FileInfo
|
||||||
|
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
|
||||||
|
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.RedactedContent
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
|
||||||
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
|
import io.element.android.libraries.matrix.test.media.aMediaSource
|
||||||
|
import io.element.android.libraries.matrix.test.room.aMessageContent
|
||||||
|
import io.element.android.libraries.matrix.test.room.aPollContent
|
||||||
|
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
|
||||||
|
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class InReplyToMetadataKtTest {
|
||||||
|
@Test
|
||||||
|
fun `any message content`() = runTest {
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
anInReplyToDetails(eventContent = aMessageContent()).metadata()
|
||||||
|
}.test {
|
||||||
|
awaitItem().let {
|
||||||
|
Truth.assertThat(it).isEqualTo(InReplyToMetadata.Text("textContent"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `an image message content`() = runTest {
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
anInReplyToDetails(
|
||||||
|
eventContent = aMessageContent(
|
||||||
|
messageType = ImageMessageType(
|
||||||
|
body = "body",
|
||||||
|
source = aMediaSource(),
|
||||||
|
info = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).metadata()
|
||||||
|
}.test {
|
||||||
|
awaitItem().let {
|
||||||
|
Truth.assertThat(it).isEqualTo(
|
||||||
|
InReplyToMetadata.Thumbnail(
|
||||||
|
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||||
|
thumbnailSource = aMediaSource(),
|
||||||
|
textContent = "body",
|
||||||
|
type = AttachmentThumbnailType.Image,
|
||||||
|
blurHash = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a video message content`() = runTest {
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
anInReplyToDetails(
|
||||||
|
eventContent = aMessageContent(
|
||||||
|
messageType = VideoMessageType(
|
||||||
|
body = "body",
|
||||||
|
source = aMediaSource(),
|
||||||
|
info = VideoInfo(
|
||||||
|
duration = null,
|
||||||
|
height = null,
|
||||||
|
width = null,
|
||||||
|
mimetype = null,
|
||||||
|
size = null,
|
||||||
|
thumbnailInfo = null,
|
||||||
|
thumbnailSource = aMediaSource(),
|
||||||
|
blurhash = null
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).metadata()
|
||||||
|
}.test {
|
||||||
|
awaitItem().let {
|
||||||
|
Truth.assertThat(it).isEqualTo(
|
||||||
|
InReplyToMetadata.Thumbnail(
|
||||||
|
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||||
|
thumbnailSource = aMediaSource(),
|
||||||
|
textContent = "body",
|
||||||
|
type = AttachmentThumbnailType.Video,
|
||||||
|
blurHash = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a file message content`() = runTest {
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
anInReplyToDetails(
|
||||||
|
eventContent = aMessageContent(
|
||||||
|
messageType = FileMessageType(
|
||||||
|
body = "body",
|
||||||
|
source = aMediaSource(),
|
||||||
|
info = FileInfo(
|
||||||
|
mimetype = null,
|
||||||
|
size = null,
|
||||||
|
thumbnailInfo = null,
|
||||||
|
thumbnailSource = aMediaSource(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).metadata()
|
||||||
|
}.test {
|
||||||
|
awaitItem().let {
|
||||||
|
Truth.assertThat(it).isEqualTo(
|
||||||
|
InReplyToMetadata.Thumbnail(
|
||||||
|
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||||
|
thumbnailSource = aMediaSource(),
|
||||||
|
textContent = "body",
|
||||||
|
type = AttachmentThumbnailType.File,
|
||||||
|
blurHash = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a audio message content`() = runTest {
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
anInReplyToDetails(
|
||||||
|
eventContent = aMessageContent(
|
||||||
|
messageType = AudioMessageType(
|
||||||
|
body = "body",
|
||||||
|
source = aMediaSource(),
|
||||||
|
info = AudioInfo(
|
||||||
|
duration = null,
|
||||||
|
size = null,
|
||||||
|
mimetype = null
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).metadata()
|
||||||
|
}.test {
|
||||||
|
awaitItem().let {
|
||||||
|
Truth.assertThat(it).isEqualTo(
|
||||||
|
InReplyToMetadata.Thumbnail(
|
||||||
|
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||||
|
textContent = "body",
|
||||||
|
type = AttachmentThumbnailType.Audio,
|
||||||
|
blurHash = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a location message content`() = runTest {
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
testEnv {
|
||||||
|
anInReplyToDetails(
|
||||||
|
eventContent = aMessageContent(
|
||||||
|
messageType = LocationMessageType(
|
||||||
|
body = "body",
|
||||||
|
geoUri = "geo:3.0,4.0;u=5.0",
|
||||||
|
description = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).metadata()
|
||||||
|
}
|
||||||
|
}.test {
|
||||||
|
awaitItem().let {
|
||||||
|
Truth.assertThat(it).isEqualTo(
|
||||||
|
InReplyToMetadata.Thumbnail(
|
||||||
|
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||||
|
thumbnailSource = null,
|
||||||
|
textContent = "Shared location",
|
||||||
|
type = AttachmentThumbnailType.Location,
|
||||||
|
blurHash = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a voice message content`() = runTest {
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
testEnv {
|
||||||
|
anInReplyToDetails(
|
||||||
|
eventContent = aMessageContent(
|
||||||
|
messageType = VoiceMessageType(
|
||||||
|
body = "body",
|
||||||
|
source = aMediaSource(),
|
||||||
|
info = null,
|
||||||
|
details = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).metadata()
|
||||||
|
}
|
||||||
|
}.test {
|
||||||
|
awaitItem().let {
|
||||||
|
Truth.assertThat(it).isEqualTo(
|
||||||
|
InReplyToMetadata.Thumbnail(
|
||||||
|
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||||
|
thumbnailSource = null,
|
||||||
|
textContent = "Voice message",
|
||||||
|
type = AttachmentThumbnailType.Voice,
|
||||||
|
blurHash = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a poll content`() = runTest {
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
anInReplyToDetails(
|
||||||
|
eventContent = aPollContent()
|
||||||
|
).metadata()
|
||||||
|
}.test {
|
||||||
|
awaitItem().let {
|
||||||
|
Truth.assertThat(it).isEqualTo(
|
||||||
|
InReplyToMetadata.Thumbnail(
|
||||||
|
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||||
|
thumbnailSource = null,
|
||||||
|
textContent = "Do you like polls?",
|
||||||
|
type = AttachmentThumbnailType.Poll,
|
||||||
|
blurHash = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `any other content`() = runTest {
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
anInReplyToDetails(
|
||||||
|
eventContent = RedactedContent
|
||||||
|
).metadata()
|
||||||
|
}.test {
|
||||||
|
awaitItem().let {
|
||||||
|
Truth.assertThat(it).isEqualTo(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun anInReplyToDetails(
|
||||||
|
eventId: EventId = AN_EVENT_ID,
|
||||||
|
senderId: UserId = A_USER_ID,
|
||||||
|
senderDisplayName: String? = "senderDisplayName",
|
||||||
|
senderAvatarUrl: String? = "senderAvatarUrl",
|
||||||
|
eventContent: EventContent? = aMessageContent(),
|
||||||
|
textContent: String? = "textContent",
|
||||||
|
) = InReplyToDetails(
|
||||||
|
eventId = eventId,
|
||||||
|
senderId = senderId,
|
||||||
|
senderDisplayName = senderDisplayName,
|
||||||
|
senderAvatarUrl = senderAvatarUrl,
|
||||||
|
eventContent = eventContent,
|
||||||
|
textContent = textContent,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun testEnv(content: @Composable () -> Any?): Any? {
|
||||||
|
var result: Any? = null
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalConfiguration provides Configuration(),
|
||||||
|
LocalContext provides ApplicationProvider.getApplicationContext(),
|
||||||
|
) {
|
||||||
|
content().apply {
|
||||||
|
result = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue