Merge pull request #3449 from element-hq/feature/bma/dmRoomBeginning

Fix events blinking at the beginning of DM
This commit is contained in:
Benoit Marty 2024-09-12 16:29:18 +02:00 committed by GitHub
commit 1c92f1f3f7
11 changed files with 179 additions and 67 deletions

View file

@ -162,7 +162,7 @@ jsoup = "org.jsoup:jsoup:1.18.1"
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0" molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
timber = "com.jakewharton.timber:timber:5.0.1" timber = "com.jakewharton.timber:timber:5.0.1"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.43" matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.44"
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }

View file

@ -44,5 +44,6 @@ data class MatrixRoomInfo(
val hasRoomCall: Boolean, val hasRoomCall: Boolean,
val activeRoomCallParticipants: ImmutableList<String>, val activeRoomCallParticipants: ImmutableList<String>,
val heroes: ImmutableList<MatrixUser>, val heroes: ImmutableList<MatrixUser>,
val pinnedEventIds: ImmutableList<EventId> val pinnedEventIds: ImmutableList<EventId>,
val creator: UserId?,
) )

View file

@ -28,6 +28,7 @@ class MatrixRoomInfoMapper {
fun map(rustRoomInfo: RustRoomInfo): MatrixRoomInfo = rustRoomInfo.let { fun map(rustRoomInfo: RustRoomInfo): MatrixRoomInfo = rustRoomInfo.let {
return MatrixRoomInfo( return MatrixRoomInfo(
id = RoomId(it.id), id = RoomId(it.id),
creator = it.creator?.let(::UserId),
name = it.displayName, name = it.displayName,
rawName = it.rawName, rawName = it.rawName,
topic = it.topic, topic = it.topic,

View file

@ -86,7 +86,7 @@ class RustTimeline(
onNewSyncedEvent: () -> Unit, onNewSyncedEvent: () -> Unit,
) : Timeline { ) : Timeline {
private val initLatch = CompletableDeferred<Unit>() private val initLatch = CompletableDeferred<Unit>()
private val isInit = MutableStateFlow(false) private val isTimelineInitialized = MutableStateFlow(false)
private val _timelineItems: MutableStateFlow<List<MatrixTimelineItem>> = private val _timelineItems: MutableStateFlow<List<MatrixTimelineItem>> =
MutableStateFlow(emptyList()) MutableStateFlow(emptyList())
@ -110,7 +110,7 @@ class RustTimeline(
timelineCoroutineScope = coroutineScope, timelineCoroutineScope = coroutineScope,
timelineDiffProcessor = timelineDiffProcessor, timelineDiffProcessor = timelineDiffProcessor,
initLatch = initLatch, initLatch = initLatch,
isInit = isInit, isTimelineInitialized = isTimelineInitialized,
dispatcher = dispatcher, dispatcher = dispatcher,
onNewSyncedEvent = onNewSyncedEvent, onNewSyncedEvent = onNewSyncedEvent,
) )
@ -189,7 +189,7 @@ class RustTimeline(
} }
private fun canPaginate(direction: Timeline.PaginationDirection): Boolean { private fun canPaginate(direction: Timeline.PaginationDirection): Boolean {
if (!isInit.value) return false if (!isTimelineInitialized.value) return false
return when (direction) { return when (direction) {
Timeline.PaginationDirection.BACKWARDS -> backPaginationStatus.value.canPaginate Timeline.PaginationDirection.BACKWARDS -> backPaginationStatus.value.canPaginate
Timeline.PaginationDirection.FORWARDS -> forwardPaginationStatus.value.canPaginate Timeline.PaginationDirection.FORWARDS -> forwardPaginationStatus.value.canPaginate
@ -207,23 +207,37 @@ class RustTimeline(
_timelineItems, _timelineItems,
backPaginationStatus.map { it.hasMoreToLoad }.distinctUntilChanged(), backPaginationStatus.map { it.hasMoreToLoad }.distinctUntilChanged(),
forwardPaginationStatus.map { it.hasMoreToLoad }.distinctUntilChanged(), forwardPaginationStatus.map { it.hasMoreToLoad }.distinctUntilChanged(),
isInit, matrixRoom.roomInfoFlow.map { it.creator },
) { timelineItems, hasMoreToLoadBackward, hasMoreToLoadForward, isInit -> isTimelineInitialized,
) { timelineItems,
hasMoreToLoadBackward,
hasMoreToLoadForward,
roomCreator,
isTimelineInitialized ->
withContext(dispatcher) { withContext(dispatcher) {
timelineItems timelineItems
.process { items -> .let { items ->
roomBeginningPostProcessor.process( roomBeginningPostProcessor.process(
items = items, items = items,
isDm = matrixRoom.isDm, isDm = matrixRoom.isDm,
hasMoreToLoadBackwards = hasMoreToLoadBackward roomCreator = roomCreator,
hasMoreToLoadBackwards = hasMoreToLoadBackward,
) )
} }
.process(predicate = isInit) { items -> .let { items ->
loadingIndicatorsPostProcessor.process(items, hasMoreToLoadBackward, hasMoreToLoadForward) loadingIndicatorsPostProcessor.process(
items = items,
isTimelineInitialized = isTimelineInitialized,
hasMoreToLoadBackward = hasMoreToLoadBackward,
hasMoreToLoadForward = hasMoreToLoadForward
)
} }
// Keep lastForwardIndicatorsPostProcessor last // Keep lastForwardIndicatorsPostProcessor last
.process(predicate = isInit) { items -> .let { items ->
lastForwardIndicatorsPostProcessor.process(items) lastForwardIndicatorsPostProcessor.process(
items = items,
isTimelineInitialized = isTimelineInitialized,
)
} }
} }
}.onStart { }.onStart {
@ -542,14 +556,3 @@ class RustTimeline(
} }
} }
} }
private suspend fun List<MatrixTimelineItem>.process(
predicate: Boolean = true,
processor: suspend (List<MatrixTimelineItem>) -> List<MatrixTimelineItem>
): List<MatrixTimelineItem> {
return if (predicate) {
processor(this)
} else {
this
}
}

View file

@ -38,7 +38,7 @@ internal class TimelineItemsSubscriber(
private val timeline: Timeline, private val timeline: Timeline,
private val timelineDiffProcessor: MatrixTimelineDiffProcessor, private val timelineDiffProcessor: MatrixTimelineDiffProcessor,
private val initLatch: CompletableDeferred<Unit>, private val initLatch: CompletableDeferred<Unit>,
private val isInit: MutableStateFlow<Boolean>, private val isTimelineInitialized: MutableStateFlow<Boolean>,
private val onNewSyncedEvent: () -> Unit, private val onNewSyncedEvent: () -> Unit,
) { ) {
private var subscriptionCount = 0 private var subscriptionCount = 0
@ -85,13 +85,13 @@ internal class TimelineItemsSubscriber(
ensureActive() ensureActive()
timelineDiffProcessor.postItems(it) timelineDiffProcessor.postItems(it)
} }
isInit.value = true isTimelineInitialized.value = true
initLatch.complete(Unit) initLatch.complete(Unit)
} }
private suspend fun postDiffs(diffs: List<TimelineDiff>) { private suspend fun postDiffs(diffs: List<TimelineDiff>) {
val diffsToProcess = diffs.toMutableList() val diffsToProcess = diffs.toMutableList()
if (!isInit.value) { if (!isTimelineInitialized.value) {
val resetDiff = diffsToProcess.firstOrNull { it.change() == TimelineChange.RESET } val resetDiff = diffsToProcess.firstOrNull { it.change() == TimelineChange.RESET }
if (resetDiff != null) { if (resetDiff != null) {
// Keep using the postItems logic so we can post the timelineItems asap. // Keep using the postItems logic so we can post the timelineItems asap.

View file

@ -22,7 +22,9 @@ class LastForwardIndicatorsPostProcessor(
fun process( fun process(
items: List<MatrixTimelineItem>, items: List<MatrixTimelineItem>,
isTimelineInitialized: Boolean,
): List<MatrixTimelineItem> { ): List<MatrixTimelineItem> {
if (!isTimelineInitialized) return items
// We don't need to add the last forward indicator if we are not in the FOCUSED_ON_EVENT mode // We don't need to add the last forward indicator if we are not in the FOCUSED_ON_EVENT mode
if (mode != Timeline.Mode.FOCUSED_ON_EVENT) { if (mode != Timeline.Mode.FOCUSED_ON_EVENT) {
return items return items

View file

@ -16,9 +16,11 @@ import io.element.android.services.toolbox.api.systemclock.SystemClock
class LoadingIndicatorsPostProcessor(private val systemClock: SystemClock) { class LoadingIndicatorsPostProcessor(private val systemClock: SystemClock) {
fun process( fun process(
items: List<MatrixTimelineItem>, items: List<MatrixTimelineItem>,
isTimelineInitialized: Boolean,
hasMoreToLoadBackward: Boolean, hasMoreToLoadBackward: Boolean,
hasMoreToLoadForward: Boolean, hasMoreToLoadForward: Boolean,
): List<MatrixTimelineItem> { ): List<MatrixTimelineItem> {
if (!isTimelineInitialized) return items
val shouldAddForwardLoadingIndicator = hasMoreToLoadForward && items.isNotEmpty() val shouldAddForwardLoadingIndicator = hasMoreToLoadForward && items.isNotEmpty()
val currentTimestamp = systemClock.epochMillis() val currentTimestamp = systemClock.epochMillis()
return buildList { return buildList {

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UniqueId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
@ -25,12 +26,14 @@ class RoomBeginningPostProcessor(private val mode: Timeline.Mode) {
fun process( fun process(
items: List<MatrixTimelineItem>, items: List<MatrixTimelineItem>,
isDm: Boolean, isDm: Boolean,
hasMoreToLoadBackwards: Boolean roomCreator: UserId?,
hasMoreToLoadBackwards: Boolean,
): List<MatrixTimelineItem> { ): List<MatrixTimelineItem> {
return when { return when {
items.isEmpty() -> items
mode == Timeline.Mode.PINNED_EVENTS -> items mode == Timeline.Mode.PINNED_EVENTS -> items
isDm -> processForDM(items, roomCreator)
hasMoreToLoadBackwards -> items hasMoreToLoadBackwards -> items
isDm -> processForDM(items)
else -> processForRoom(items) else -> processForRoom(items)
} }
} }
@ -40,15 +43,18 @@ class RoomBeginningPostProcessor(private val mode: Timeline.Mode) {
return listOf(roomBeginningItem) + items return listOf(roomBeginningItem) + items
} }
private fun processForDM(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> { private fun processForDM(items: List<MatrixTimelineItem>, roomCreator: UserId?): List<MatrixTimelineItem> {
// Find room creation event. This is usually index 0 // Find room creation event.
// This is usually the first MatrixTimelineItem.Event (so index 1, index 0 is a date)
val roomCreationEventIndex = items.indexOfFirst { val roomCreationEventIndex = items.indexOfFirst {
val stateEventContent = (it as? MatrixTimelineItem.Event)?.event?.content as? StateContent val stateEventContent = (it as? MatrixTimelineItem.Event)?.event?.content as? StateContent
stateEventContent?.content is OtherState.RoomCreate stateEventContent?.content is OtherState.RoomCreate
} }
// Find self-join event for room creator. This is usually index 1 // If the parameter roomCreator is null, the creator is the sender of the RoomCreate Event.
val roomCreatorUserId = (items.getOrNull(roomCreationEventIndex) as? MatrixTimelineItem.Event)?.event?.sender val roomCreatorUserId = roomCreator ?: (items.getOrNull(roomCreationEventIndex) as? MatrixTimelineItem.Event)?.event?.sender
// Find self-join event for the room creator.
// This is usually the second MatrixTimelineItem.Event (so index 2)
val selfUserJoinedEventIndex = roomCreatorUserId?.let { creatorUserId -> val selfUserJoinedEventIndex = roomCreatorUserId?.let { creatorUserId ->
items.indexOfFirst { items.indexOfFirst {
val stateEventContent = (it as? MatrixTimelineItem.Event)?.event?.content as? RoomMembershipContent val stateEventContent = (it as? MatrixTimelineItem.Event)?.event?.content as? RoomMembershipContent
@ -56,6 +62,9 @@ class RoomBeginningPostProcessor(private val mode: Timeline.Mode) {
} }
} ?: -1 } ?: -1
if (roomCreationEventIndex == -1 && selfUserJoinedEventIndex == -1) {
return items
}
// Remove items at the indices we found // Remove items at the indices we found
val newItems = items.toMutableList() val newItems = items.toMutableList()
if (selfUserJoinedEventIndex in newItems.indices) { if (selfUserJoinedEventIndex in newItems.indices) {

View file

@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.roomlist
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import com.sun.jna.Pointer import com.sun.jna.Pointer
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID_2 import io.element.android.libraries.matrix.test.A_ROOM_ID_2
@ -215,6 +216,7 @@ private fun aRustRoomInfo(
numUnreadNotifications: ULong = 0uL, numUnreadNotifications: ULong = 0uL,
numUnreadMentions: ULong = 0uL, numUnreadMentions: ULong = 0uL,
pinnedEventIds: List<String> = listOf(), pinnedEventIds: List<String> = listOf(),
roomCreator: UserId? = null,
) = RoomInfo( ) = RoomInfo(
id = id, id = id,
displayName = displayName, displayName = displayName,
@ -245,6 +247,7 @@ private fun aRustRoomInfo(
numUnreadNotifications = numUnreadNotifications, numUnreadNotifications = numUnreadNotifications,
numUnreadMentions = numUnreadMentions, numUnreadMentions = numUnreadMentions,
pinnedEventIds = pinnedEventIds, pinnedEventIds = pinnedEventIds,
creator = roomCreator?.value,
) )
class FakeRoomListItem( class FakeRoomListItem(

View file

@ -22,85 +22,174 @@ import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
import org.junit.Test import org.junit.Test
class RoomBeginningPostProcessorTest { class RoomBeginningPostProcessorTest {
private val roomCreateEvent = MatrixTimelineItem.Event(
uniqueId = UniqueId("m.room.create"),
event = anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))
)
private val roomCreatorJoinEvent = MatrixTimelineItem.Event(
uniqueId = UniqueId("m.room.member"),
event = anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))
)
private val otherMemberJoinEvent = MatrixTimelineItem.Event(
uniqueId = UniqueId("m.room.member_other"),
event = anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED))
)
private val messageEvent = MatrixTimelineItem.Event(
uniqueId = UniqueId("m.room.message"),
event = anEventTimelineItem(content = aMessageContent("hi"))
)
@Test
fun `processor returns empty list when empty list is provided`() {
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
val processedItems = processor.process(
items = emptyList(),
isDm = true,
roomCreator = A_USER_ID,
hasMoreToLoadBackwards = false,
)
assertThat(processedItems).isEmpty()
}
@Test
fun `processor returns the provided list when it only contains a message`() {
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
val processedItems = processor.process(
items = listOf(messageEvent),
isDm = true,
roomCreator = A_USER_ID,
hasMoreToLoadBackwards = false,
)
assertThat(processedItems).isEqualTo(listOf(messageEvent))
}
@Test
fun `processor returns the provided list when it only contains a message and the roomCreator is not provided`() {
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
val processedItems = processor.process(
items = listOf(messageEvent),
isDm = true,
roomCreator = null,
hasMoreToLoadBackwards = false,
)
assertThat(processedItems).isEqualTo(listOf(messageEvent))
}
@Test @Test
fun `processor removes room creation event and self-join event from DM timeline`() { fun `processor removes room creation event and self-join event from DM timeline`() {
val timelineItems = listOf( val timelineItems = listOf(
MatrixTimelineItem.Event(UniqueId("m.room.create"), anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), roomCreateEvent,
MatrixTimelineItem.Event(UniqueId("m.room.member"), anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), roomCreatorJoinEvent,
) )
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = false) val processedItems = processor.process(
items = timelineItems,
isDm = true,
roomCreator = A_USER_ID,
hasMoreToLoadBackwards = false,
)
assertThat(processedItems).isEmpty() assertThat(processedItems).isEmpty()
} }
@Test
fun `processor does not remove anything with PINNED_EVENTS mode`() {
val timelineItems = listOf(
roomCreateEvent,
roomCreatorJoinEvent,
)
val processor = RoomBeginningPostProcessor(Timeline.Mode.PINNED_EVENTS)
val processedItems = processor.process(
items = timelineItems,
isDm = true,
roomCreator = A_USER_ID,
hasMoreToLoadBackwards = false,
)
assertThat(processedItems).isEqualTo(timelineItems)
}
@Test @Test
fun `processor removes room creation event and self-join event from DM timeline even if they're not the first items`() { fun `processor removes room creation event and self-join event from DM timeline even if they're not the first items`() {
val timelineItems = listOf( val timelineItems = listOf(
MatrixTimelineItem.Event( otherMemberJoinEvent,
UniqueId("m.room.member_other"), roomCreateEvent,
anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED)) messageEvent,
), roomCreatorJoinEvent,
MatrixTimelineItem.Event(UniqueId("m.room.create"), anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))),
MatrixTimelineItem.Event(UniqueId("m.room.message"), anEventTimelineItem(content = aMessageContent("hi"))),
MatrixTimelineItem.Event(UniqueId("m.room.member"), anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))),
) )
val expected = listOf( val expected = listOf(
MatrixTimelineItem.Event( otherMemberJoinEvent,
UniqueId("m.room.member_other"), messageEvent,
anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED))
),
MatrixTimelineItem.Event(UniqueId("m.room.message"), anEventTimelineItem(content = aMessageContent("hi"))),
) )
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = false) val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = false)
assertThat(processedItems).isEqualTo(expected) assertThat(processedItems).isEqualTo(expected)
} }
@Test @Test
fun `processor will add beginning of room item if it's not a DM`() { fun `processor will add beginning of room item if it's not a DM`() {
val timelineItems = listOf( val timelineItems = listOf(
MatrixTimelineItem.Event(UniqueId("m.room.create"), anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), roomCreateEvent,
MatrixTimelineItem.Event(UniqueId("m.room.member"), anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), roomCreatorJoinEvent,
) )
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
val processedItems = processor.process(timelineItems, isDm = false, hasMoreToLoadBackwards = false) val processedItems = processor.process(timelineItems, isDm = false, roomCreator = A_USER_ID, hasMoreToLoadBackwards = false)
assertThat(processedItems).isEqualTo( assertThat(processedItems).isEqualTo(
listOf(processor.createRoomBeginningItem()) + timelineItems listOf(processor.createRoomBeginningItem()) + timelineItems
) )
} }
@Test @Test
fun `processor won't remove items if it's not at the start of the timeline`() { fun `processor will not add beginning of room item if it's not a DM but the room has more to load`() {
val timelineItems = listOf( val timelineItems = listOf(
MatrixTimelineItem.Event(UniqueId("m.room.create"), anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), roomCreateEvent,
MatrixTimelineItem.Event(UniqueId("m.room.member"), anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), roomCreatorJoinEvent,
) )
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true) val processedItems = processor.process(timelineItems, isDm = false, roomCreator = A_USER_ID, hasMoreToLoadBackwards = true)
assertThat(processedItems).isEqualTo(timelineItems) assertThat(processedItems).isEqualTo(timelineItems)
} }
@Test @Test
fun `processor won't remove the first member join event if it can't find the room creation event`() { fun `processor will add beginning of room item if it's not a DM, when the parameter roomCreator is null`() {
val timelineItems = listOf( val timelineItems = listOf(
MatrixTimelineItem.Event(UniqueId("m.room.member"), anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), roomCreateEvent,
roomCreatorJoinEvent,
) )
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true) val processedItems = processor.process(timelineItems, isDm = false, roomCreator = null, hasMoreToLoadBackwards = false)
assertThat(processedItems).isEqualTo(timelineItems) assertThat(processedItems).isEqualTo(
listOf(processor.createRoomBeginningItem()) + timelineItems
)
}
@Test
fun `processor removes items event it's not at the start of the timeline`() {
val timelineItems = listOf(
roomCreateEvent,
roomCreatorJoinEvent,
)
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = true)
assertThat(processedItems).isEmpty()
}
@Test
fun `processor removes the first member join event if it matches the roomCreator parameter`() {
val timelineItems = listOf(
roomCreatorJoinEvent,
)
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = true)
assertThat(processedItems).isEmpty()
} }
@Test @Test
fun `processor won't remove the first member join event if it's not from the room creator`() { fun `processor won't remove the first member join event if it's not from the room creator`() {
val timelineItems = listOf( val timelineItems = listOf(
MatrixTimelineItem.Event(UniqueId("m.room.create"), anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), roomCreateEvent,
MatrixTimelineItem.Event( otherMemberJoinEvent,
UniqueId("m.room.member"),
anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED))
),
) )
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE) val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true) val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = true)
assertThat(processedItems).isEqualTo(timelineItems) assertThat(processedItems).isEqualTo(listOf(otherMemberJoinEvent))
} }
} }

View file

@ -523,6 +523,7 @@ fun aRoomInfo(
activeRoomCallParticipants: List<String> = emptyList(), activeRoomCallParticipants: List<String> = emptyList(),
heroes: List<MatrixUser> = emptyList(), heroes: List<MatrixUser> = emptyList(),
pinnedEventIds: List<EventId> = emptyList(), pinnedEventIds: List<EventId> = emptyList(),
roomCreator: UserId? = null,
) = MatrixRoomInfo( ) = MatrixRoomInfo(
id = id, id = id,
name = name, name = name,
@ -549,6 +550,7 @@ fun aRoomInfo(
activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(), activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(),
heroes = heroes.toImmutableList(), heroes = heroes.toImmutableList(),
pinnedEventIds = pinnedEventIds.toImmutableList(), pinnedEventIds = pinnedEventIds.toImmutableList(),
creator = roomCreator,
) )
fun defaultRoomPowerLevels() = MatrixRoomPowerLevels( fun defaultRoomPowerLevels() = MatrixRoomPowerLevels(