Use the new SDK thread info for redacted messages

This commit is contained in:
bxdxnn
2026-08-19 16:12:15 +00:00
parent dfbc75a9e1
commit 2ff6a56e7d
17 changed files with 65 additions and 17 deletions
@@ -91,7 +91,7 @@ internal fun MatrixTimelineItem.Event.canBeDisplayedInBubbleBlock(): Boolean {
// Can be grouped
is FailedToParseMessageLikeContent,
is MessageContent,
RedactedContent,
is RedactedContent,
is StickerContent,
is PollContent,
is UnableToDecryptContent,
@@ -47,7 +47,6 @@ import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction
import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender
import io.element.android.libraries.matrix.api.timeline.item.event.Receipt
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
import io.element.android.libraries.matrix.test.AN_EVENT_ID
@@ -65,6 +64,7 @@ import io.element.android.libraries.matrix.test.room.aRoomMember
import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermissions
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
import io.element.android.libraries.matrix.test.timeline.aMessageContent
import io.element.android.libraries.matrix.test.timeline.aRedactedContent
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
import io.element.android.libraries.matrix.test.timeline.item.event.aRoomMembershipContent
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
@@ -876,7 +876,7 @@ class TimelinePresenterTest {
(0 until 3).map { index ->
MatrixTimelineItem.Event(
uniqueId = UniqueId("redacted_$index"),
event = anEventTimelineItem(eventId = EventId("\$R$index"), content = RedactedContent),
event = anEventTimelineItem(eventId = EventId("\$R$index"), content = aRedactedContent()),
)
}
),
@@ -12,10 +12,14 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory
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.TimelineItemThreadInfo
import io.element.android.libraries.matrix.api.core.UniqueId
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.EventThreadInfo
import io.element.android.libraries.matrix.test.A_THREAD_ID
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.timeline.aMessageContent
import io.element.android.libraries.matrix.test.timeline.aRedactedContent
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
@@ -82,6 +86,42 @@ class TimelineItemsFactoryTest {
).inOrder()
}
@Test
fun `redacted message keeps its thread info`() = runTest {
val factory = aTimelineItemsFactory(
config = TimelineItemsFactoryConfig(
computeReadReceipts = false,
computeReactions = false,
)
)
val items = listOf(
MatrixTimelineItem.Event(
uniqueId = UniqueId("event-0"),
event = anEventTimelineItem(
sender = A_USER_ID,
timestamp = 0L,
content = aRedactedContent(
threadInfo = EventThreadInfo.ThreadResponse(A_THREAD_ID),
),
),
)
)
var threadInfo: TimelineItemThreadInfo? = null
factory.timelineItems.test {
factory.replaceWith(
timelineItems = items,
roomMembers = emptyList(),
renderReadReceipts = false,
)
threadInfo = awaitItem()
.filterIsInstance<TimelineItem.Event>()
.firstOrNull()
?.threadInfo
cancelAndIgnoreRemainingEvents()
}
assertThat(threadInfo).isEqualTo(TimelineItemThreadInfo.ThreadResponse(A_THREAD_ID))
}
private suspend fun TestScope.groupPositionsOf(timestamps: List<Long>): List<TimelineItemGroupPosition> {
val factory = aTimelineItemsFactory(
config = TimelineItemsFactoryConfig(
@@ -16,11 +16,11 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.AN_EVENT_ID_2
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.test.timeline.aRedactedContent
import io.element.android.libraries.mediaplayer.api.MediaPlayer
import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer
import io.element.android.tests.testutils.testCoroutineDispatchers
@@ -88,7 +88,7 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf<MatrixTimelineItem>(
sender = A_USER_ID,
senderProfile = ProfileDetails.Unavailable,
timestamp = 9442,
content = RedactedContent,
content = aRedactedContent(),
origin = null,
timelineItemDebugInfoProvider = {
TimelineItemDebugInfo(
@@ -56,7 +56,7 @@ class DefaultPinnedMessagesBannerFormatter(
is PollContent -> {
content.question.prefixWith(CommonStrings.a11y_poll)
}
RedactedContent -> {
is RedactedContent -> {
sp.getString(CommonStrings.common_message_removed)
}
else -> {
@@ -89,7 +89,7 @@ class DefaultRoomLatestEventFormatter(
): CharSequence? {
return when (content) {
is MessageContent -> content.process(senderDisambiguatedDisplayName, isDmRoom, isOutgoing)
RedactedContent -> {
is RedactedContent -> {
val message = sp.getString(CommonStrings.common_message_removed)
message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing)
}
@@ -62,7 +62,7 @@ class DefaultTimelineEventFormatter(
is CallNotifyContent -> {
sp.getString(CommonStrings.common_call_started)
}
RedactedContent,
is RedactedContent,
is LegacyCallInviteContent,
is StickerContent,
is PollContent,
@@ -81,7 +81,7 @@ class DefaultPinnedMessagesBannerFormatterTest : RobolectricTest() {
fun `Redacted content`() {
val expected = "Message removed"
val senderName = "Someone"
val message = createRoomEvent(false, senderName, RedactedContent)
val message = createRoomEvent(false, senderName, RedactedContent(threadInfo = null))
val result = formatter.format(message)
assertThat(result).isEqualTo(expected)
}
@@ -85,7 +85,7 @@ class DefaultRoomLatestEventFormatterTest : RobolectricTest() {
val expected = "Message removed"
val senderName = "Someone"
sequenceOf(false, true).forEach { isDm ->
val message = createLatestEvent(false, senderName, RedactedContent)
val message = createLatestEvent(false, senderName, RedactedContent(threadInfo = null))
val result = formatter.format(message, isDm)
if (isDm) {
assertThat(result).isEqualTo(expected)
@@ -32,7 +32,9 @@ data class MessageContent(
val type: MessageType,
) : EventContent
data object RedactedContent : EventContent
data class RedactedContent(
val threadInfo: EventThreadInfo?,
) : EventContent
data class StickerContent(
val filename: String,
@@ -51,6 +51,7 @@ data class EventTimelineItem(
is PollContent -> content.threadInfo
is StickerContent -> content.threadInfo
is UnableToDecryptContent -> content.threadInfo
is RedactedContent -> content.threadInfo
else -> null
}
@@ -81,7 +81,7 @@ class TimelineEventContentMapper(
)
}
is MsgLikeKind.Redacted -> {
RedactedContent
RedactedContent(threadInfo = extractThreadInfo(it.content))
}
is MsgLikeKind.Poll -> {
PollContent(
@@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails
import io.element.android.libraries.matrix.api.timeline.item.event.Receipt
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.SendHandleProvider
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
@@ -137,6 +138,10 @@ fun aStickerContent(
threadInfo = threadInfo,
)
fun aRedactedContent(
threadInfo: EventThreadInfo? = null,
) = RedactedContent(threadInfo = threadInfo)
fun aTimelineItemDebugInfo(
model: String = "Rust(Model())",
originalJson: String? = null,
@@ -117,7 +117,7 @@ class InReplyToDetailsDisambiguatedProvider : InReplyToDetailsProvider() {
class InReplyToDetailsInformativeProvider : InReplyToDetailsProvider() {
override val values: Sequence<InReplyToDetails>
get() = sequenceOf(
RedactedContent,
RedactedContent(threadInfo = null),
UnableToDecryptContent(data = UnableToDecryptContent.Data.Unknown, threadInfo = null),
).map {
aInReplyToDetails(
@@ -656,7 +656,7 @@ class InReplyToMetadataKtTest : RobolectricTest() {
fun `redacted content`() = runTest {
moleculeFlow(RecompositionMode.Immediate) {
anInReplyToDetailsReady(
eventContent = RedactedContent
eventContent = RedactedContent(threadInfo = null)
).metadata(hideImage = false)
}.test {
awaitItem().let {
@@ -79,7 +79,7 @@ class EventItemFactory(
LegacyCallInviteContent,
is PollContent,
is ProfileChangeContent,
RedactedContent,
is RedactedContent,
is RoomMembershipContent,
is StateContent,
is StickerContent,
@@ -35,7 +35,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessa
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.OtherState
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
import io.element.android.libraries.matrix.api.timeline.item.event.StickerMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
@@ -49,6 +48,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.timeline.aMessageContent
import io.element.android.libraries.matrix.test.timeline.aPollContent
import io.element.android.libraries.matrix.test.timeline.aProfileChangeMessageContent
import io.element.android.libraries.matrix.test.timeline.aRedactedContent
import io.element.android.libraries.matrix.test.timeline.aStickerContent
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
import io.element.android.libraries.matrix.test.timeline.item.event.aRoomMembershipContent
@@ -80,7 +80,7 @@ class EventItemFactoryTest {
LegacyCallInviteContent,
aPollContent(),
aProfileChangeMessageContent(),
RedactedContent,
aRedactedContent(),
aRoomMembershipContent(
userId = A_USER_ID,
),