Introduce LiveLocationContent for the timeline (needs sdk)

This commit is contained in:
ganfra
2026-03-03 22:22:02 +01:00
parent f4bf596e3b
commit 046d135e4b
20 changed files with 235 additions and 82 deletions

View File

@@ -32,6 +32,8 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.features.location.api.internal.StaticMapPlaceholder
import io.element.android.features.location.api.internal.StaticMapUrlBuilder
import io.element.android.features.location.api.internal.centerBottomEdge
import io.element.android.libraries.designsystem.components.LocationPinMarker
import io.element.android.libraries.designsystem.components.PinVariant
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Icon
@@ -45,6 +47,7 @@ fun StaticMapView(
lat: Double,
lon: Double,
zoom: Double,
pinVariant: PinVariant,
contentDescription: String?,
modifier: Modifier = Modifier,
darkMode: Boolean = !ElementTheme.isLightTheme,
@@ -95,12 +98,7 @@ fun StaticMapView(
// We apply ContentScale.Fit to scale the image to fill the AsyncImage should this be the case.
contentScale = ContentScale.Fit,
)
Icon(
resourceId = CommonDrawables.pin,
contentDescription = null,
tint = Color.Unspecified,
modifier = Modifier.centerBottomEdge(this),
)
LocationPinMarker(variant = pinVariant, modifier = Modifier.centerBottomEdge(this))
} else {
StaticMapPlaceholder(
showProgress = collectedState.value.isLoading(),
@@ -127,6 +125,7 @@ internal fun StaticMapViewPreview() = ElementPreview {
lon = 0.0,
zoom = 0.0,
contentDescription = null,
pinVariant = PinVariant.PinnedLocation,
modifier = Modifier.size(400.dp),
)
}

View File

@@ -58,7 +58,7 @@ internal class MapTilerStaticMapUrlBuilder(
// image smaller than the available space in pixels.
// The resulting image will have to be scaled to fit the available space in order
// to keep the perceived content size constant at the expense of sharpness.
return "$baseUrl/$mapId/static/$lon,$lat,$finalZoom/${finalWidth}x${finalHeight}$scale.webp?key=$apiKey&attribution=bottomleft"
return "$baseUrl/$mapId/static/$lon,$lat,$finalZoom/${finalWidth}x${finalHeight}$scale.webp?key=$apiKey&attribution=topright"
}
override fun isServiceAvailable() = apiKey.isNotEmpty()

View File

@@ -39,7 +39,7 @@ import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugIn
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
import io.element.android.libraries.matrix.ui.messages.reply.aProfileTimelineDetailsReady
import io.element.android.libraries.matrix.ui.messages.reply.aProfileDetailsReady
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@@ -166,7 +166,7 @@ internal fun aTimelineItemEvent(
isMine = isMine,
isEditable = isEditable,
canBeRepliedTo = canBeRepliedTo,
senderProfile = aProfileTimelineDetailsReady(
senderProfile = aProfileDetailsReady(
displayName = senderDisplayName,
displayNameAmbiguous = displayNameAmbiguous,
),

View File

@@ -8,10 +8,8 @@
package io.element.android.features.messages.impl.timeline.components.event
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -19,33 +17,28 @@ import androidx.compose.ui.unit.dp
import io.element.android.features.location.api.StaticMapView
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContentProvider
import io.element.android.libraries.designsystem.components.PinVariant
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun TimelineItemLocationView(
content: TimelineItemLocationContent,
modifier: Modifier = Modifier,
) {
Column(modifier = modifier.fillMaxWidth()) {
content.description?.let {
Text(
text = it,
modifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 8.dp),
)
}
StaticMapView(
modifier = Modifier
.fillMaxWidth()
.heightIn(max = 188.dp),
lat = content.location.lat,
lon = content.location.lon,
zoom = 15.0,
contentDescription = content.body
)
}
StaticMapView(
modifier = modifier
.fillMaxWidth()
.heightIn(max = 188.dp),
pinVariant = content.pinVariant,
lat = content.location.lat,
lon = content.location.lon,
zoom = 15.0,
contentDescription = content.body
)
}
@PreviewsDayNight

View File

@@ -9,8 +9,10 @@
package io.element.android.features.messages.impl.timeline.factories.event
import dev.zacsweers.metro.Inject
import io.element.android.features.location.api.Location
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRtcNotificationContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.libraries.matrix.api.core.EventId
@@ -22,6 +24,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventTimeline
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.LegacyCallInviteContent
import io.element.android.libraries.matrix.api.timeline.item.event.LiveLocationContent
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.ProfileChangeContent
@@ -70,10 +73,10 @@ class TimelineItemContentFactory(
is FailedToParseMessageLikeContent -> failedToParseMessageFactory.create(itemContent)
is FailedToParseStateContent -> failedToParseStateFactory.create(itemContent)
is MessageContent -> {
val senderDisambiguatedDisplayName = senderProfile.getDisambiguatedDisplayName(sender)
messageFactory.create(
senderId = sender,
senderProfile = senderProfile,
content = itemContent,
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
eventId = eventId,
)
}
@@ -96,6 +99,24 @@ class TimelineItemContentFactory(
is UnableToDecryptContent -> utdFactory.create(itemContent)
is CallNotifyContent -> TimelineItemRtcNotificationContent()
is UnknownContent -> TimelineItemUnknownContent
is LiveLocationContent -> {
val lastKnownLocation = itemContent.locations.mapNotNull { beacon ->
Location.fromGeoUri(beacon.geoUri)
}.lastOrNull()
if (lastKnownLocation != null) {
TimelineItemLocationContent(
body = itemContent.body.trimEnd(),
description = itemContent.description?.trimEnd(),
assetType = itemContent.assetType,
senderId = sender,
senderProfile = senderProfile,
location = lastKnownLocation,
mode = TimelineItemLocationContent.Mode.Live(isActive = itemContent.isLive)
)
} else {
TimelineItemUnknownContent
}
}
}
}
}

View File

@@ -29,8 +29,13 @@ import io.element.android.features.messages.impl.utils.TextPillificationHelper
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
import io.element.android.libraries.androidutils.text.safeLinkify
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.designsystem.components.PinVariant
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
@@ -39,10 +44,13 @@ import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessa
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.OtherMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails
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.VideoMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.getAvatarUrl
import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName
import io.element.android.libraries.matrix.ui.messages.toHtmlDocument
import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor
import kotlinx.collections.immutable.persistentListOf
@@ -65,11 +73,13 @@ class TimelineItemContentMessageFactory(
) {
fun create(
content: MessageContent,
senderDisambiguatedDisplayName: String,
senderId: UserId,
senderProfile: ProfileDetails,
eventId: EventId?,
): TimelineItemEventContent {
return when (val messageType = content.type) {
is EmoteMessageType -> {
val senderDisambiguatedDisplayName = senderProfile.getDisambiguatedDisplayName(senderId)
val emoteBody = "* $senderDisambiguatedDisplayName ${messageType.body.trimEnd()}"
val dom = messageType.formatted?.toHtmlDocument(
permalinkParser = permalinkParser,
@@ -135,8 +145,8 @@ class TimelineItemContentMessageFactory(
}
is LocationMessageType -> {
val location = Location.fromGeoUri(messageType.geoUri)
val body = messageType.body.trimEnd()
if (location == null) {
val body = messageType.body.trimEnd()
TimelineItemTextContent(
body = body,
htmlDocument = null,
@@ -145,9 +155,13 @@ class TimelineItemContentMessageFactory(
)
} else {
TimelineItemLocationContent(
body = messageType.body.trimEnd(),
body = body,
location = location,
description = messageType.description
description = messageType.description,
senderId = senderId,
senderProfile = senderProfile,
assetType = messageType.assetType,
mode = TimelineItemLocationContent.Mode.Static
)
}
}

View File

@@ -31,6 +31,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.CallNotifyCon
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.LegacyCallInviteContent
import io.element.android.libraries.matrix.api.timeline.item.event.LiveLocationContent
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.ProfileChangeContent
@@ -81,7 +82,8 @@ internal fun MatrixTimelineItem.Event.canBeDisplayedInBubbleBlock(): Boolean {
RedactedContent,
is StickerContent,
is PollContent,
is UnableToDecryptContent -> true
is UnableToDecryptContent,
is LiveLocationContent -> true
// Can't be grouped
is FailedToParseStateContent,
is ProfileChangeContent,

View File

@@ -28,7 +28,8 @@ class TimelineItemEventContentProvider : PreviewParameterProvider<TimelineItemEv
aTimelineItemAudioContent("An even bigger bigger bigger bigger bigger bigger bigger sound name which doesn't fit .mp3"),
aTimelineItemVoiceContent(),
aTimelineItemLocationContent(),
aTimelineItemLocationContent("Location description"),
aTimelineItemLocationContent(),
aTimelineItemLocationContent(mode = TimelineItemLocationContent.Mode.Live(isActive = true)),
aTimelineItemPollContent(),
aTimelineItemNoticeContent(),
aTimelineItemRedactedContent(),

View File

@@ -9,13 +9,55 @@
package io.element.android.features.messages.impl.timeline.model.event
import io.element.android.features.location.api.Location
import io.element.android.libraries.designsystem.components.PinVariant
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails
import io.element.android.libraries.matrix.api.timeline.item.event.getAvatarUrl
import io.element.android.libraries.matrix.api.timeline.item.event.getDisplayName
data class TimelineItemLocationContent(
val body: String,
val senderId: UserId,
val senderProfile: ProfileDetails,
val location: Location,
val description: String? = null,
val assetType: AssetType? = null,
val mode: Mode,
) : TimelineItemEventContent {
val pinVariant = when (mode) {
is Mode.Live -> {
if (mode.isActive) {
PinVariant.UserLocation(avatarData = senderAvatar(), isLive = true)
} else {
PinVariant.StaleLocation
}
}
Mode.Static -> {
when (assetType) {
AssetType.PIN -> PinVariant.PinnedLocation
AssetType.SENDER,
null -> PinVariant.UserLocation(avatarData = senderAvatar(), isLive = false)
}
}
}
private fun senderAvatar() = AvatarData(
senderId.value,
name = senderProfile.getDisplayName(),
url = senderProfile.getAvatarUrl(),
// Size is irrelevant as the PinMarker will override anyway.
size = AvatarSize.TimelineSender
)
sealed interface Mode {
data object Static : Mode
data class Live(val isActive: Boolean) : Mode
}
override val type: String = "TimelineItemLocationContent"
}

View File

@@ -10,21 +10,34 @@ package io.element.android.features.messages.impl.timeline.model.event
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.location.api.Location
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails
import io.element.android.libraries.matrix.ui.components.aMatrixUser
import io.element.android.libraries.matrix.ui.messages.reply.aProfileDetailsReady
open class TimelineItemLocationContentProvider : PreviewParameterProvider<TimelineItemLocationContent> {
override val values: Sequence<TimelineItemLocationContent>
get() = sequenceOf(
aTimelineItemLocationContent(),
aTimelineItemLocationContent("This is a description!"),
aTimelineItemLocationContent(mode = TimelineItemLocationContent.Mode.Live(isActive = true)),
aTimelineItemLocationContent(mode = TimelineItemLocationContent.Mode.Live(isActive = false)),
)
}
fun aTimelineItemLocationContent(description: String? = null) = TimelineItemLocationContent(
body = "User location geo:52.2445,0.7186;u=5000",
fun aTimelineItemLocationContent(
body: String = "",
senderId: UserId = UserId("@sender:matrix.org"),
senderProfile: ProfileDetails = aProfileDetailsReady(),
mode: TimelineItemLocationContent.Mode = TimelineItemLocationContent.Mode.Static,
) = TimelineItemLocationContent(
body = body,
location = Location(
lat = 52.2445,
lon = 0.7186,
accuracy = 5000f,
),
description = description,
senderId = senderId,
senderProfile = senderProfile,
mode = mode
)

View File

@@ -31,7 +31,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.matrix.test.core.FakeSendHandle
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
import io.element.android.libraries.matrix.ui.messages.reply.aProfileTimelineDetailsReady
import io.element.android.libraries.matrix.ui.messages.reply.aProfileDetailsReady
import kotlinx.collections.immutable.toImmutableList
internal fun aMessageEvent(
@@ -52,7 +52,7 @@ internal fun aMessageEvent(
eventId = eventId,
transactionId = transactionId,
senderId = A_USER_ID,
senderProfile = aProfileTimelineDetailsReady(displayName = A_USER_NAME),
senderProfile = aProfileDetailsReady(displayName = A_USER_NAME),
senderAvatar = AvatarData(A_USER_ID.value, A_USER_NAME, size = AvatarSize.TimelineSender),
content = content,
sentTime = "",

View File

@@ -60,8 +60,10 @@ import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageTy
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.permalink.FakePermalinkParser
import io.element.android.libraries.matrix.test.timeline.aProfileDetails
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.test.util.FileExtensionExtractorWithoutValidation
@@ -84,7 +86,8 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = OtherMessageType(msgType = "a_type", body = "body")),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemTextContent(
@@ -102,14 +105,18 @@ class TimelineItemContentMessageFactoryTest {
val assetType = AssetType.SENDER
val result = sut.create(
content = createMessageContent(type = LocationMessageType("body", "geo:1,2", "description", assetType)),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemLocationContent(
body = "body",
location = Location(lat = 1.0, lon = 2.0, accuracy = 0.0F),
location = Location(lat = 1.0, lon = 2.0, accuracy = null),
description = "description",
assetType = assetType,
mode = TimelineItemLocationContent.Mode.Static,
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
)
assertThat(result).isEqualTo(expected)
}
@@ -119,7 +126,8 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = LocationMessageType("body", "", null, null)),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemTextContent(
@@ -136,7 +144,8 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = TextMessageType("body", null)),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemTextContent(
@@ -153,7 +162,8 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = TextMessageType("https://www.example.org", null)),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
) as TimelineItemTextContent
val expected = TimelineItemTextContent(
@@ -200,7 +210,8 @@ class TimelineItemContentMessageFactoryTest {
formatted = FormattedBody(MessageFormat.HTML, expected.toString())
)
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
assertThat((result as TimelineItemTextContent).formattedBody).isEqualTo(expected)
@@ -218,7 +229,8 @@ class TimelineItemContentMessageFactoryTest {
formatted = FormattedBody(MessageFormat.UNKNOWN, "formatted")
)
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
assertThat((result as TimelineItemTextContent).formattedBody).isEqualTo(SpannedString("body"))
@@ -229,7 +241,8 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = VideoMessageType("filename", null, null, MediaSource("url"), null)),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemVideoContent(
@@ -282,7 +295,8 @@ class TimelineItemContentMessageFactoryTest {
),
isEdited = true,
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemVideoContent(
@@ -312,7 +326,8 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = AudioMessageType("filename", null, null, MediaSource("url"), null)),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemAudioContent(
@@ -348,7 +363,8 @@ class TimelineItemContentMessageFactoryTest {
),
isEdited = true,
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemAudioContent(
@@ -371,7 +387,8 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = VoiceMessageType("filename", null, null, MediaSource("url"), null, null)),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemVoiceContent(
@@ -413,7 +430,8 @@ class TimelineItemContentMessageFactoryTest {
),
isEdited = true,
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemVoiceContent(
@@ -438,7 +456,8 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = ImageMessageType("filename", "body", null, MediaSource("url"), null)),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemImageContent(
@@ -518,7 +537,8 @@ class TimelineItemContentMessageFactoryTest {
),
isEdited = true,
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemImageContent(
@@ -547,7 +567,8 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = FileMessageType("filename", null, null, MediaSource("url"), null)),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemFileContent(
@@ -589,7 +610,8 @@ class TimelineItemContentMessageFactoryTest {
),
isEdited = true,
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemFileContent(
@@ -612,7 +634,8 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = NoticeMessageType("body", null)),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemNoticeContent(
@@ -634,7 +657,8 @@ class TimelineItemContentMessageFactoryTest {
formatted = FormattedBody(MessageFormat.HTML, "formatted")
)
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
(result as TimelineItemNoticeContent).formattedBody.assertSpannedEquals(SpannedString("formatted"))
@@ -645,7 +669,8 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = EmoteMessageType("body", null)),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails("Bob"),
eventId = AN_EVENT_ID,
)
val expected = TimelineItemEmoteContent(
@@ -667,7 +692,8 @@ class TimelineItemContentMessageFactoryTest {
formatted = FormattedBody(MessageFormat.HTML, "formatted")
)
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails("Bob"),
eventId = AN_EVENT_ID,
)
@@ -693,7 +719,8 @@ class TimelineItemContentMessageFactoryTest {
formatted = FormattedBody(MessageFormat.HTML, "Test <a href=\"https://www.example.org\">me@matrix.org</a>")
)
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
(result as TimelineItemTextContent).formattedBody.assertSpannedEquals(expectedSpanned)
@@ -718,7 +745,8 @@ class TimelineItemContentMessageFactoryTest {
formatted = FormattedBody(MessageFormat.HTML, "Test https://www.example.org")
)
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)
(result as TimelineItemTextContent).formattedBody.assertSpannedEquals(expectedSpanned)
@@ -744,7 +772,8 @@ class TimelineItemContentMessageFactoryTest {
formatted = FormattedBody(MessageFormat.HTML, "Test https://www.example.org")
)
),
senderDisambiguatedDisplayName = "Bob",
senderId = A_USER_ID,
senderProfile = aProfileDetails(),
eventId = AN_EVENT_ID,
)

View File

@@ -23,7 +23,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSen
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.core.FakeSendHandle
import io.element.android.libraries.matrix.ui.messages.reply.aProfileTimelineDetailsReady
import io.element.android.libraries.matrix.ui.messages.reply.aProfileDetailsReady
import kotlinx.collections.immutable.toImmutableList
import org.junit.Test
@@ -34,7 +34,7 @@ class TimelineItemGrouperTest {
id = UniqueId("0"),
senderId = A_USER_ID,
senderAvatar = anAvatarData(),
senderProfile = aProfileTimelineDetailsReady(displayName = ""),
senderProfile = aProfileDetailsReady(displayName = ""),
content = TimelineItemStateEventContent(body = "a state event"),
reactionsState = aTimelineItemReactions(count = 0),
readReceiptState = TimelineItemReadReceipts(emptyList<ReadReceiptData>().toImmutableList()),