mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Merge remote-tracking branch 'upstream/develop' into fix/5356-sender-name-fallback
# Conflicts: # features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt
This commit is contained in:
+8
-2
@@ -51,6 +51,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.captionOrNull
|
||||
import io.element.android.features.messages.impl.timeline.model.event.htmlCaptionOrNull
|
||||
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
|
||||
import io.element.android.features.messages.impl.voicemessages.composer.DefaultVoiceMessageComposerPresenter
|
||||
import io.element.android.features.roomcall.api.RoomCallState
|
||||
@@ -377,7 +378,7 @@ class MessagesPresenter(
|
||||
TimelineItemAction.Edit,
|
||||
TimelineItemAction.EditPoll -> handleActionEdit(targetEvent, composerState, enableTextFormatting)
|
||||
TimelineItemAction.AddCaption -> handleActionAddCaption(targetEvent, composerState)
|
||||
TimelineItemAction.EditCaption -> handleActionEditCaption(targetEvent, composerState)
|
||||
TimelineItemAction.EditCaption -> handleActionEditCaption(targetEvent, composerState, enableTextFormatting)
|
||||
TimelineItemAction.RemoveCaption -> handleRemoveCaption(targetEvent)
|
||||
TimelineItemAction.Reply -> handleActionReply(targetEvent, composerState, timelineProtectionState)
|
||||
TimelineItemAction.ReplyInThread -> {
|
||||
@@ -546,10 +547,15 @@ class MessagesPresenter(
|
||||
private suspend fun handleActionEditCaption(
|
||||
targetEvent: TimelineItem.Event,
|
||||
composerState: MessageComposerState,
|
||||
enableTextFormatting: Boolean,
|
||||
) {
|
||||
val composerMode = MessageComposerMode.EditCaption(
|
||||
eventOrTransactionId = targetEvent.eventOrTransactionId,
|
||||
content = targetEvent.content.captionOrNull().orEmpty(),
|
||||
content = if (enableTextFormatting) {
|
||||
targetEvent.content.htmlCaptionOrNull() ?: targetEvent.content.captionOrNull()
|
||||
} else {
|
||||
targetEvent.content.captionOrNull()
|
||||
}.orEmpty(),
|
||||
)
|
||||
composerState.eventSink(
|
||||
MessageComposerEvent.SetMode(composerMode)
|
||||
|
||||
+8
@@ -108,6 +108,7 @@ class TimelineItemContentMessageFactory(
|
||||
fileSize = messageType.info?.size ?: 0,
|
||||
caption = messageType.caption?.trimEnd(),
|
||||
formattedCaption = formattedCaption,
|
||||
htmlCaption = messageType.formattedCaption?.body,
|
||||
isEdited = content.isEdited,
|
||||
mediaSource = messageType.source,
|
||||
thumbnailSource = messageType.info?.thumbnailSource,
|
||||
@@ -132,6 +133,7 @@ class TimelineItemContentMessageFactory(
|
||||
fileSize = messageType.info?.size ?: 0,
|
||||
caption = messageType.caption?.trimEnd(),
|
||||
formattedCaption = formattedCaption,
|
||||
htmlCaption = messageType.formattedCaption?.body,
|
||||
isEdited = content.isEdited,
|
||||
mediaSource = messageType.source,
|
||||
thumbnailSource = messageType.info?.thumbnailSource,
|
||||
@@ -174,6 +176,7 @@ class TimelineItemContentMessageFactory(
|
||||
fileSize = messageType.info?.size ?: 0,
|
||||
caption = messageType.caption?.trimEnd(),
|
||||
formattedCaption = formattedCaption,
|
||||
htmlCaption = messageType.formattedCaption?.body,
|
||||
isEdited = content.isEdited,
|
||||
thumbnailSource = messageType.info?.thumbnailSource,
|
||||
mediaSource = messageType.source,
|
||||
@@ -198,6 +201,7 @@ class TimelineItemContentMessageFactory(
|
||||
fileSize = messageType.info?.size ?: 0,
|
||||
caption = messageType.caption?.trimEnd(),
|
||||
formattedCaption = formattedCaption,
|
||||
htmlCaption = messageType.formattedCaption?.body,
|
||||
isEdited = content.isEdited,
|
||||
mediaSource = messageType.source,
|
||||
duration = messageType.info?.duration ?: Duration.ZERO,
|
||||
@@ -216,6 +220,7 @@ class TimelineItemContentMessageFactory(
|
||||
fileSize = messageType.info?.size ?: 0,
|
||||
caption = messageType.caption?.trimEnd(),
|
||||
formattedCaption = formattedCaption,
|
||||
htmlCaption = messageType.formattedCaption?.body,
|
||||
isEdited = content.isEdited,
|
||||
mediaSource = messageType.source,
|
||||
duration = messageType.info?.duration ?: Duration.ZERO,
|
||||
@@ -235,6 +240,7 @@ class TimelineItemContentMessageFactory(
|
||||
fileSize = messageType.info?.size ?: 0,
|
||||
caption = messageType.caption?.trimEnd(),
|
||||
formattedCaption = formattedCaption,
|
||||
htmlCaption = messageType.formattedCaption?.body,
|
||||
isEdited = content.isEdited,
|
||||
thumbnailSource = messageType.info?.thumbnailSource,
|
||||
mediaSource = messageType.source,
|
||||
@@ -345,6 +351,7 @@ class TimelineItemContentMessageFactory(
|
||||
body = messageType.body,
|
||||
caption = messageType.body.trimEnd().takeIf { it.isNotEmpty() },
|
||||
formattedCaption = formattedCaption,
|
||||
htmlCaption = messageType.formatted?.body,
|
||||
isEdited = content.isEdited,
|
||||
items = galleryItems.toImmutableList(),
|
||||
)
|
||||
@@ -402,6 +409,7 @@ class TimelineItemContentMessageFactory(
|
||||
body = messageType.body,
|
||||
caption = messageType.body.trimEnd().takeIf { it.isNotEmpty() },
|
||||
formattedCaption = formattedCaption,
|
||||
htmlCaption = messageType.formatted?.body,
|
||||
isEdited = content.isEdited,
|
||||
attachments = attachments.toImmutableList(),
|
||||
)
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+4
-2
@@ -12,6 +12,7 @@ import androidx.annotation.VisibleForTesting
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
@@ -99,7 +100,8 @@ internal const val MIN_REDACTED_RUN_SIZE = 3
|
||||
* Fold runs of [MIN_REDACTED_RUN_SIZE] or more consecutive deleted (redacted) messages into a single
|
||||
* [TimelineItem.GroupedEvents], so a long stretch of "Message removed" tiles shows as one expandable
|
||||
* "N removed messages" line, like element-web. Shorter runs, and anything that is not a redacted event
|
||||
* (day dividers included), are left untouched.
|
||||
* (day dividers included), are left untouched. A deleted message that still heads a thread is left
|
||||
* untouched too, so that its thread summary stays reachable.
|
||||
*
|
||||
* This runs as the final step of [TimelineItemGrouper.group], after the state and membership events
|
||||
* have been grouped. It is deliberately kept out of the normal grouping: a run needs a higher minimum
|
||||
@@ -134,7 +136,7 @@ internal fun List<TimelineItem>.collapseRedactedRuns(groupIds: MutableMap<String
|
||||
}
|
||||
|
||||
for (item in this) {
|
||||
if (item is TimelineItem.Event && item.content is TimelineItemRedactedContent) {
|
||||
if (item is TimelineItem.Event && item.content is TimelineItemRedactedContent && item.threadInfo !is TimelineItemThreadInfo.ThreadRoot) {
|
||||
run.add(item)
|
||||
} else {
|
||||
flushRun()
|
||||
|
||||
+1
@@ -14,6 +14,7 @@ data class TimelineItemAttachmentsContent(
|
||||
val body: String,
|
||||
val caption: String?,
|
||||
val formattedCaption: CharSequence?,
|
||||
val htmlCaption: String? = null,
|
||||
override val isEdited: Boolean,
|
||||
val attachments: ImmutableList<AttachmentItem>,
|
||||
) : TimelineItemEventContent, TimelineItemEventMutableContent {
|
||||
|
||||
+1
@@ -17,6 +17,7 @@ data class TimelineItemAudioContent(
|
||||
override val fileSize: Long?,
|
||||
override val caption: String?,
|
||||
override val formattedCaption: CharSequence?,
|
||||
override val htmlCaption: String? = null,
|
||||
override val isEdited: Boolean,
|
||||
val duration: Duration,
|
||||
override val mediaSource: MediaSource,
|
||||
|
||||
+10
@@ -33,6 +33,9 @@ sealed interface TimelineItemEventContentWithAttachment :
|
||||
val fileSize: Long?
|
||||
val caption: String?
|
||||
val formattedCaption: CharSequence?
|
||||
|
||||
/** The caption as it was written, in HTML, so that editing it can start from the formatted text. */
|
||||
val htmlCaption: String?
|
||||
val mediaSource: MediaSource
|
||||
val mimeType: String
|
||||
val formattedFileSize: String
|
||||
@@ -127,6 +130,13 @@ fun TimelineItemEventContent.formattedCaptionOrNull(): CharSequence? = when (thi
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun TimelineItemEventContent.htmlCaptionOrNull(): String? = when (this) {
|
||||
is TimelineItemEventContentWithAttachment -> htmlCaption
|
||||
is TimelineItemGalleryContent -> htmlCaption
|
||||
is TimelineItemAttachmentsContent -> htmlCaption
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun TimelineItemEventContentWithAttachment.duration(): Duration? {
|
||||
return when (this) {
|
||||
is TimelineItemAudioContent -> duration
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ data class TimelineItemFileContent(
|
||||
override val fileSize: Long?,
|
||||
override val caption: String?,
|
||||
override val formattedCaption: CharSequence?,
|
||||
override val htmlCaption: String? = null,
|
||||
override val isEdited: Boolean,
|
||||
override val mediaSource: MediaSource,
|
||||
val thumbnailSource: MediaSource?,
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ data class TimelineItemGalleryContent(
|
||||
val body: String,
|
||||
val caption: String?,
|
||||
val formattedCaption: CharSequence?,
|
||||
val htmlCaption: String? = null,
|
||||
override val isEdited: Boolean,
|
||||
val items: ImmutableList<GalleryItem>,
|
||||
) : TimelineItemEventContent, TimelineItemEventMutableContent {
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ data class TimelineItemImageContent(
|
||||
override val fileSize: Long?,
|
||||
override val caption: String?,
|
||||
override val formattedCaption: CharSequence?,
|
||||
override val htmlCaption: String? = null,
|
||||
override val isEdited: Boolean,
|
||||
override val mediaSource: MediaSource,
|
||||
val thumbnailSource: MediaSource?,
|
||||
|
||||
+2
@@ -28,6 +28,7 @@ fun aTimelineItemImageContent(
|
||||
blurhash: String? = A_BLUR_HASH,
|
||||
filename: String = "A picture.jpg",
|
||||
caption: String? = null,
|
||||
htmlCaption: String? = null,
|
||||
width: Int? = null,
|
||||
height: Int? = 300,
|
||||
) = TimelineItemImageContent(
|
||||
@@ -35,6 +36,7 @@ fun aTimelineItemImageContent(
|
||||
fileSize = 4 * 1024 * 1024L,
|
||||
caption = caption,
|
||||
formattedCaption = null,
|
||||
htmlCaption = htmlCaption,
|
||||
isEdited = false,
|
||||
mediaSource = MediaSource(""),
|
||||
thumbnailSource = null,
|
||||
|
||||
+1
@@ -15,6 +15,7 @@ data class TimelineItemStickerContent(
|
||||
override val fileSize: Long?,
|
||||
override val caption: String?,
|
||||
override val formattedCaption: CharSequence?,
|
||||
override val htmlCaption: String? = null,
|
||||
override val isEdited: Boolean,
|
||||
override val mediaSource: MediaSource,
|
||||
val thumbnailSource: MediaSource?,
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ data class TimelineItemVideoContent(
|
||||
override val fileSize: Long?,
|
||||
override val caption: String?,
|
||||
override val formattedCaption: CharSequence?,
|
||||
override val htmlCaption: String? = null,
|
||||
override val isEdited: Boolean,
|
||||
val duration: Duration,
|
||||
override val mediaSource: MediaSource,
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ data class TimelineItemVoiceContent(
|
||||
override val fileSize: Long?,
|
||||
override val caption: String?,
|
||||
override val formattedCaption: CharSequence?,
|
||||
override val htmlCaption: String? = null,
|
||||
override val isEdited: Boolean,
|
||||
val duration: Duration,
|
||||
override val mediaSource: MediaSource,
|
||||
|
||||
+27
@@ -989,6 +989,33 @@ class MessagesPresenterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle action edit caption starts from the formatted caption`() = runTest {
|
||||
val messageEvent = aMessageEvent(
|
||||
content = aTimelineItemImageContent(
|
||||
caption = "Hello world",
|
||||
htmlCaption = "<b>Hello</b> world",
|
||||
)
|
||||
)
|
||||
val composerRecorder = EventsRecorder<MessageComposerEvent>()
|
||||
val presenter = createMessagesPresenter(
|
||||
messageComposerPresenter = { aMessageComposerState(eventSink = composerRecorder, showTextFormatting = true) },
|
||||
)
|
||||
presenter.testWithLifecycleOwner {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.EditCaption, messageEvent))
|
||||
awaitItem()
|
||||
composerRecorder.assertSingle(
|
||||
MessageComposerEvent.SetMode(
|
||||
composerMode = MessageComposerMode.EditCaption(
|
||||
eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(),
|
||||
content = "<b>Hello</b> world",
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - handle action add caption`() = runTest {
|
||||
val composerRecorder = EventsRecorder<MessageComposerEvent>()
|
||||
|
||||
+2
-2
@@ -51,7 +51,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
||||
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
|
||||
@@ -69,6 +68,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
|
||||
@@ -880,7 +880,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()),
|
||||
)
|
||||
}
|
||||
),
|
||||
|
||||
+79
@@ -12,15 +12,21 @@ 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.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
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.api.timeline.item.ThreadSummary
|
||||
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.test.A_THREAD_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
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
|
||||
@@ -87,6 +93,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))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a key verification request is not rendered`() = runTest {
|
||||
val factory = aTimelineItemsFactory(
|
||||
@@ -131,6 +173,43 @@ class TimelineItemsFactoryTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a deleted message keeps the summary of the thread it heads`() = 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,
|
||||
content = aRedactedContent(
|
||||
threadInfo = EventThreadInfo.ThreadRoot(
|
||||
summary = ThreadSummary(latestEvent = AsyncData.Uninitialized, numberOfReplies = 3),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
factory.timelineItems.test {
|
||||
factory.replaceWith(
|
||||
timelineItems = items,
|
||||
roomMembers = emptyList(),
|
||||
renderReadReceipts = false,
|
||||
)
|
||||
val threadInfo = awaitItem()
|
||||
.filterIsInstance<TimelineItem.Event>()
|
||||
.single()
|
||||
.threadInfo
|
||||
assertThat(threadInfo).isInstanceOf(TimelineItemThreadInfo.ThreadRoot::class.java)
|
||||
assertThat((threadInfo as TimelineItemThreadInfo.ThreadRoot).summary.numberOfReplies).isEqualTo(3)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a sender the timeline knows nothing about is named from the room member list`() = runTest {
|
||||
val factory = aTimelineItemsFactory(
|
||||
|
||||
+2
@@ -297,6 +297,7 @@ class TimelineItemContentMessageFactoryTest : RobolectricTest() {
|
||||
fileSize = 555L,
|
||||
caption = "body.mp4 caption",
|
||||
formattedCaption = SpannedString("formatted"),
|
||||
htmlCaption = "formatted",
|
||||
isEdited = true,
|
||||
duration = 1.minutes,
|
||||
mediaSource = MediaSource(url = "url", json = null),
|
||||
@@ -539,6 +540,7 @@ class TimelineItemContentMessageFactoryTest : RobolectricTest() {
|
||||
fileSize = 888L,
|
||||
caption = "body.jpg caption",
|
||||
formattedCaption = SpannedString("formatted"),
|
||||
htmlCaption = "formatted",
|
||||
isEdited = true,
|
||||
mediaSource = MediaSource(url = "url", json = null),
|
||||
thumbnailSource = MediaSource("url_thumbnail"),
|
||||
|
||||
+19
@@ -15,11 +15,14 @@ import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
|
||||
import io.element.android.features.messages.impl.timeline.model.ReadReceiptData
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent
|
||||
import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.components.avatar.anAvatarData
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.ThreadSummary
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
@@ -190,6 +193,22 @@ class TimelineItemGrouperTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a deleted message that still heads a thread is left out of the removed messages group`() {
|
||||
val threadRoot = aGroupableItem.copy(
|
||||
id = UniqueId("r1"),
|
||||
content = TimelineItemRedactedContent,
|
||||
threadInfo = TimelineItemThreadInfo.ThreadRoot(
|
||||
summary = ThreadSummary(latestEvent = AsyncData.Uninitialized, numberOfReplies = 2),
|
||||
latestEventText = null,
|
||||
),
|
||||
)
|
||||
val r0 = aGroupableItem.copy(id = UniqueId("r0"), content = TimelineItemRedactedContent)
|
||||
val r2 = aGroupableItem.copy(id = UniqueId("r2"), content = TimelineItemRedactedContent)
|
||||
val result = sut.group(listOf(r0, threadRoot, r2))
|
||||
assertThat(result).isEqualTo(listOf(r0, threadRoot, r2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a run of fewer than three redacted events is left untouched`() {
|
||||
val r0 = aGroupableItem.copy(id = UniqueId("r0"), content = TimelineItemRedactedContent)
|
||||
|
||||
+2
-2
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user