Add threadInfo field to message like timeline events (#5930)
* Add `threadInfo` field to message like timeline events: - Polls - Stickers - UTDs * Add missing cases for `EventTimeline.threadInfo()`
This commit is contained in:
committed by
GitHub
parent
602498a36b
commit
0b5c4fc8bb
@@ -220,6 +220,7 @@ class PollContentStateFactoryTest {
|
||||
votes = votes,
|
||||
endTime = endTime,
|
||||
isEdited = false,
|
||||
threadInfo = null,
|
||||
)
|
||||
|
||||
private fun aPollContentState(
|
||||
|
||||
@@ -103,7 +103,11 @@ class DefaultPinnedMessagesBannerFormatterTest {
|
||||
fun `Unable to decrypt content`() {
|
||||
val expected = "Waiting for this message"
|
||||
val senderName = "Someone"
|
||||
val message = createRoomEvent(false, senderName, UnableToDecryptContent(UnableToDecryptContent.Data.Unknown))
|
||||
val message = createRoomEvent(
|
||||
sentByYou = false,
|
||||
senderDisplayName = senderName,
|
||||
content = UnableToDecryptContent(data = UnableToDecryptContent.Data.Unknown, threadInfo = null)
|
||||
)
|
||||
val result = formatter.format(message)
|
||||
assertThat(result).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@@ -112,7 +112,11 @@ class DefaultRoomLatestEventFormatterTest {
|
||||
val expected = "Waiting for this message"
|
||||
val senderName = "Someone"
|
||||
sequenceOf(false, true).forEach { isDm ->
|
||||
val message = createLatestEvent(false, senderName, UnableToDecryptContent(UnableToDecryptContent.Data.Unknown))
|
||||
val message = createLatestEvent(
|
||||
sentByYou = false,
|
||||
senderDisplayName = senderName,
|
||||
content = UnableToDecryptContent(data = UnableToDecryptContent.Data.Unknown, threadInfo = null),
|
||||
)
|
||||
val result = formatter.format(message, isDm)
|
||||
if (isDm) {
|
||||
assertThat(result).isEqualTo(expected)
|
||||
|
||||
@@ -26,7 +26,7 @@ data class MessageContent(
|
||||
val inReplyTo: InReplyTo?,
|
||||
val isEdited: Boolean,
|
||||
val threadInfo: EventThreadInfo?,
|
||||
val type: MessageType
|
||||
val type: MessageType,
|
||||
) : EventContent
|
||||
|
||||
data object RedactedContent : EventContent
|
||||
@@ -36,6 +36,7 @@ data class StickerContent(
|
||||
val body: String?,
|
||||
val info: ImageInfo,
|
||||
val source: MediaSource,
|
||||
val threadInfo: EventThreadInfo?,
|
||||
) : EventContent {
|
||||
val bestDescription: String
|
||||
get() = body ?: filename
|
||||
@@ -49,10 +50,12 @@ data class PollContent(
|
||||
val votes: ImmutableMap<String, ImmutableList<UserId>>,
|
||||
val endTime: ULong?,
|
||||
val isEdited: Boolean,
|
||||
val threadInfo: EventThreadInfo?,
|
||||
) : EventContent
|
||||
|
||||
data class UnableToDecryptContent(
|
||||
val data: Data
|
||||
val data: Data,
|
||||
val threadInfo: EventThreadInfo?,
|
||||
) : EventContent {
|
||||
@Immutable
|
||||
sealed interface Data {
|
||||
|
||||
@@ -39,7 +39,13 @@ data class EventTimelineItem(
|
||||
return (content as? MessageContent)?.inReplyTo
|
||||
}
|
||||
|
||||
fun threadInfo(): EventThreadInfo? = (content as? MessageContent)?.threadInfo
|
||||
fun threadInfo(): EventThreadInfo? = when (content) {
|
||||
is MessageContent -> content.threadInfo
|
||||
is PollContent -> content.threadInfo
|
||||
is StickerContent -> content.threadInfo
|
||||
is UnableToDecryptContent -> content.threadInfo
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun hasNotLoadedInReplyTo(): Boolean {
|
||||
val details = inReplyTo()
|
||||
|
||||
@@ -36,6 +36,7 @@ import io.element.android.libraries.matrix.impl.room.join.map
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableMap
|
||||
import org.matrix.rustcomponents.sdk.EmbeddedEventDetails
|
||||
import org.matrix.rustcomponents.sdk.MsgLikeContent
|
||||
import org.matrix.rustcomponents.sdk.MsgLikeKind
|
||||
import org.matrix.rustcomponents.sdk.TimelineItemContent
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
@@ -68,37 +69,11 @@ class TimelineEventContentMapper(
|
||||
when (val kind = it.content.kind) {
|
||||
is MsgLikeKind.Message -> {
|
||||
val inReplyTo = it.content.inReplyTo
|
||||
val threadSummary = it.content.threadSummary?.use { summary ->
|
||||
val numberOfReplies = summary.numReplies().toLong()
|
||||
val latestEvent = summary.latestEvent()
|
||||
val details = when (latestEvent) {
|
||||
is EmbeddedEventDetails.Unavailable -> AsyncData.Uninitialized
|
||||
is EmbeddedEventDetails.Pending -> AsyncData.Loading()
|
||||
is EmbeddedEventDetails.Error -> AsyncData.Failure(IllegalStateException(latestEvent.message))
|
||||
is EmbeddedEventDetails.Ready -> {
|
||||
AsyncData.Success(
|
||||
EmbeddedEventInfo(
|
||||
eventOrTransactionId = latestEvent.eventOrTransactionId.map(),
|
||||
content = map(latestEvent.content),
|
||||
senderId = UserId(latestEvent.sender),
|
||||
senderProfile = latestEvent.senderProfile.map(),
|
||||
timestamp = latestEvent.timestamp.toLong(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
ThreadSummary(
|
||||
latestEvent = details,
|
||||
numberOfReplies = numberOfReplies,
|
||||
)
|
||||
}
|
||||
val threadRootId = it.content.threadRoot?.let(::ThreadId)
|
||||
val threadInfo = when {
|
||||
threadSummary != null -> EventThreadInfo.ThreadRoot(threadSummary)
|
||||
threadRootId != null -> EventThreadInfo.ThreadResponse(threadRootId)
|
||||
else -> null
|
||||
}
|
||||
eventMessageMapper.map(kind, inReplyTo, threadInfo)
|
||||
eventMessageMapper.map(
|
||||
message = kind,
|
||||
inReplyTo = inReplyTo,
|
||||
threadInfo = extractThreadInfo(it.content)
|
||||
)
|
||||
}
|
||||
is MsgLikeKind.Redacted -> {
|
||||
RedactedContent
|
||||
@@ -114,11 +89,13 @@ class TimelineEventContentMapper(
|
||||
}.toImmutableMap(),
|
||||
endTime = kind.endTime,
|
||||
isEdited = kind.hasBeenEdited,
|
||||
threadInfo = extractThreadInfo(it.content),
|
||||
)
|
||||
}
|
||||
is MsgLikeKind.UnableToDecrypt -> {
|
||||
UnableToDecryptContent(
|
||||
data = kind.msg.map()
|
||||
data = kind.msg.map(),
|
||||
threadInfo = extractThreadInfo(it.content),
|
||||
)
|
||||
}
|
||||
is MsgLikeKind.Sticker -> {
|
||||
@@ -127,6 +104,7 @@ class TimelineEventContentMapper(
|
||||
body = null,
|
||||
info = kind.info.map(),
|
||||
source = kind.source.map(),
|
||||
threadInfo = extractThreadInfo(it.content),
|
||||
)
|
||||
}
|
||||
is MsgLikeKind.Other -> UnknownContent
|
||||
@@ -159,6 +137,43 @@ class TimelineEventContentMapper(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractThreadInfo(content: MsgLikeContent): EventThreadInfo? {
|
||||
val threadSummary = extractThreadSummary(content.threadSummary)
|
||||
val threadRootId = content.threadRoot?.let(::ThreadId)
|
||||
return when {
|
||||
threadSummary != null -> EventThreadInfo.ThreadRoot(threadSummary)
|
||||
threadRootId != null -> EventThreadInfo.ThreadResponse(threadRootId)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractThreadSummary(threadSummary: org.matrix.rustcomponents.sdk.ThreadSummary?): ThreadSummary? {
|
||||
return threadSummary?.use { summary ->
|
||||
val numberOfReplies = summary.numReplies().toLong()
|
||||
val latestEvent = summary.latestEvent()
|
||||
val details = when (latestEvent) {
|
||||
is EmbeddedEventDetails.Unavailable -> AsyncData.Uninitialized
|
||||
is EmbeddedEventDetails.Pending -> AsyncData.Loading()
|
||||
is EmbeddedEventDetails.Error -> AsyncData.Failure(IllegalStateException(latestEvent.message))
|
||||
is EmbeddedEventDetails.Ready -> {
|
||||
AsyncData.Success(
|
||||
EmbeddedEventInfo(
|
||||
eventOrTransactionId = latestEvent.eventOrTransactionId.map(),
|
||||
content = map(latestEvent.content),
|
||||
senderId = UserId(latestEvent.sender),
|
||||
senderProfile = latestEvent.senderProfile.map(),
|
||||
timestamp = latestEvent.timestamp.toLong(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
ThreadSummary(
|
||||
latestEvent = details,
|
||||
numberOfReplies = numberOfReplies,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun RustMembershipChange.map(): MembershipChange {
|
||||
|
||||
@@ -123,11 +123,13 @@ fun aStickerContent(
|
||||
info: ImageInfo,
|
||||
mediaSource: MediaSource,
|
||||
body: String? = null,
|
||||
threadInfo: EventThreadInfo? = null,
|
||||
) = StickerContent(
|
||||
filename = filename,
|
||||
body = body,
|
||||
info = info,
|
||||
source = mediaSource,
|
||||
threadInfo = threadInfo,
|
||||
)
|
||||
|
||||
fun aTimelineItemDebugInfo(
|
||||
@@ -148,6 +150,7 @@ fun aPollContent(
|
||||
votes: ImmutableMap<String, ImmutableList<UserId>> = persistentMapOf(),
|
||||
endTime: ULong? = null,
|
||||
isEdited: Boolean = false,
|
||||
threadInfo: EventThreadInfo? = null,
|
||||
) = PollContent(
|
||||
question = question,
|
||||
kind = kind,
|
||||
@@ -156,4 +159,5 @@ fun aPollContent(
|
||||
votes = votes,
|
||||
endTime = endTime,
|
||||
isEdited = isEdited,
|
||||
threadInfo = threadInfo,
|
||||
)
|
||||
|
||||
@@ -89,6 +89,7 @@ open class InReplyToDetailsProvider : PreviewParameterProvider<InReplyToDetails>
|
||||
votes = persistentMapOf(),
|
||||
endTime = null,
|
||||
isEdited = false,
|
||||
threadInfo = null,
|
||||
),
|
||||
).map {
|
||||
aInReplyToDetails(
|
||||
@@ -116,7 +117,7 @@ class InReplyToDetailsInformativeProvider : InReplyToDetailsProvider() {
|
||||
override val values: Sequence<InReplyToDetails>
|
||||
get() = sequenceOf(
|
||||
RedactedContent,
|
||||
UnableToDecryptContent(UnableToDecryptContent.Data.Unknown),
|
||||
UnableToDecryptContent(data = UnableToDecryptContent.Data.Unknown, threadInfo = null),
|
||||
).map {
|
||||
aInReplyToDetails(
|
||||
eventContent = it,
|
||||
|
||||
@@ -134,7 +134,8 @@ class InReplyToMetadataKtTest {
|
||||
filename = "filename",
|
||||
body = "body",
|
||||
info = anImageInfo(),
|
||||
source = aMediaSource(url = "url")
|
||||
source = aMediaSource(url = "url"),
|
||||
threadInfo = null,
|
||||
)
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
@@ -161,7 +162,8 @@ class InReplyToMetadataKtTest {
|
||||
filename = "filename",
|
||||
body = "body",
|
||||
info = anImageInfo(),
|
||||
source = aMediaSource(url = "url")
|
||||
source = aMediaSource(url = "url"),
|
||||
threadInfo = null,
|
||||
)
|
||||
).metadata(hideImage = true)
|
||||
}.test {
|
||||
@@ -445,7 +447,10 @@ class InReplyToMetadataKtTest {
|
||||
fun `unable to decrypt content`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = UnableToDecryptContent(UnableToDecryptContent.Data.Unknown)
|
||||
eventContent = UnableToDecryptContent(
|
||||
data = UnableToDecryptContent.Data.Unknown,
|
||||
threadInfo = null,
|
||||
),
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
|
||||
@@ -84,7 +84,7 @@ class DefaultEventItemFactoryTest {
|
||||
),
|
||||
mediaSource = MediaSource("")
|
||||
),
|
||||
UnableToDecryptContent(UnableToDecryptContent.Data.Unknown),
|
||||
UnableToDecryptContent(data = UnableToDecryptContent.Data.Unknown, threadInfo = null),
|
||||
UnknownContent,
|
||||
)
|
||||
contents.forEach {
|
||||
@@ -397,8 +397,8 @@ class DefaultEventItemFactoryTest {
|
||||
height = 1L,
|
||||
width = 2L,
|
||||
blurhash = null,
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user