diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt index 9585b2521b..8fc1478bf7 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt @@ -46,6 +46,12 @@ interface Timeline : AutoCloseable { FORWARDS } + enum class Mode { + LIVE, + FOCUSED_ON_EVENT, + FOCUSED_ON_PINNED_EVENTS + } + val membershipChangeEventReceived: Flow suspend fun sendReadReceipt(eventId: EventId, receiptType: ReceiptType): Result suspend fun paginate(direction: PaginationDirection): Result diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 20dceeeb87..6d0dea6d03 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -153,7 +153,7 @@ class RustMatrixRoom( private val _roomNotificationSettingsStateFlow = MutableStateFlow(MatrixRoomNotificationSettingsState.Unknown) override val roomNotificationSettingsStateFlow: StateFlow = _roomNotificationSettingsStateFlow - override val liveTimeline = createTimeline(innerTimeline, isLive = true) { + override val liveTimeline = createTimeline(innerTimeline, mode = Timeline.Mode.LIVE) { _syncUpdateFlow.value = systemClock.epochMillis() } @@ -181,7 +181,7 @@ class RustMatrixRoom( numContextEvents = 50u, internalIdPrefix = "focus_$eventId", ).let { inner -> - createTimeline(inner, isLive = false) + createTimeline(inner, mode = Timeline.Mode.FOCUSED_ON_EVENT) } }.mapFailure { it.toFocusEventException() @@ -198,7 +198,7 @@ class RustMatrixRoom( internalIdPrefix = "pinned_events", maxEventsToLoad = 100u, ).let { inner -> - createTimeline(inner, isLive = false) + createTimeline(inner, mode = Timeline.Mode.FOCUSED_ON_PINNED_EVENTS) } }.onFailure { if (it is CancellationException) { @@ -655,13 +655,13 @@ class RustMatrixRoom( private fun createTimeline( timeline: InnerTimeline, - isLive: Boolean, + mode: Timeline.Mode, onNewSyncedEvent: () -> Unit = {}, ): Timeline { val timelineCoroutineScope = roomCoroutineScope.childScope(coroutineDispatchers.main, "TimelineScope-$roomId-$timeline") return RustTimeline( isKeyBackupEnabled = isKeyBackupEnabled, - isLive = isLive, + mode = mode, matrixRoom = this, systemClock = systemClock, coroutineScope = timelineCoroutineScope, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt index 8c774d2d29..3f9008ef28 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt @@ -85,7 +85,7 @@ private const val PAGINATION_SIZE = 50 class RustTimeline( private val inner: InnerTimeline, - private val isLive: Boolean, + mode: Timeline.Mode, systemClock: SystemClock, isKeyBackupEnabled: Boolean, private val matrixRoom: MatrixRoom, @@ -131,21 +131,21 @@ class RustTimeline( onNewSyncedEvent = onNewSyncedEvent, ) - private val roomBeginningPostProcessor = RoomBeginningPostProcessor() + private val roomBeginningPostProcessor = RoomBeginningPostProcessor(mode) private val loadingIndicatorsPostProcessor = LoadingIndicatorsPostProcessor(systemClock) - private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(isLive) + private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(mode) private val backPaginationStatus = MutableStateFlow( - Timeline.PaginationStatus(isPaginating = false, hasMoreToLoad = true) + Timeline.PaginationStatus(isPaginating = false, hasMoreToLoad = mode != Timeline.Mode.FOCUSED_ON_PINNED_EVENTS) ) private val forwardPaginationStatus = MutableStateFlow( - Timeline.PaginationStatus(isPaginating = false, hasMoreToLoad = !isLive) + Timeline.PaginationStatus(isPaginating = false, hasMoreToLoad = mode == Timeline.Mode.FOCUSED_ON_EVENT) ) init { coroutineScope.fetchMembers() - if (isLive) { + if (mode == Timeline.Mode.LIVE) { // When timeline is live, we need to listen to the back pagination status as // sdk can automatically paginate backwards. coroutineScope.registerBackPaginationStatusListener() diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LastForwardIndicatorsPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LastForwardIndicatorsPostProcessor.kt index d717fac00d..2dde2c7591 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LastForwardIndicatorsPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LastForwardIndicatorsPostProcessor.kt @@ -17,21 +17,22 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor 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.item.virtual.VirtualTimelineItem /** * This post processor is responsible for adding virtual items to indicate all the previous last forward item. */ class LastForwardIndicatorsPostProcessor( - private val isTimelineLive: Boolean, + private val mode: Timeline.Mode, ) { private val lastForwardIdentifiers = LinkedHashSet() fun process( items: List, ): List { - // If the timeline is live, we don't have any last forward indicator to display - if (isTimelineLive) { + // 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) { return items } else { return buildList { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt index e3fa9c8072..cec7a79219 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor import androidx.annotation.VisibleForTesting 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.item.event.MembershipChange import io.element.android.libraries.matrix.api.timeline.item.event.OtherState import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent @@ -28,13 +29,14 @@ import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTime * This timeline post-processor removes the room creation event and the self-join event from the timeline for DMs * or add the RoomBeginning item for non DM room. */ -class RoomBeginningPostProcessor { +class RoomBeginningPostProcessor(private val mode: Timeline.Mode) { fun process( items: List, isDm: Boolean, hasMoreToLoadBackwards: Boolean ): List { return when { + mode == Timeline.Mode.FOCUSED_ON_PINNED_EVENTS -> items hasMoreToLoadBackwards -> items isDm -> processForDM(items) else -> processForRoom(items) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt index 63b0664bc7..9700b70a77 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt @@ -36,7 +36,7 @@ class RoomBeginningPostProcessorTest { MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), ) - val processor = RoomBeginningPostProcessor() + val processor = RoomBeginningPostProcessor(mode) val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = false) assertThat(processedItems).isEmpty() } @@ -53,7 +53,7 @@ class RoomBeginningPostProcessorTest { MatrixTimelineItem.Event("m.room.member_other", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED))), MatrixTimelineItem.Event("m.room.message", anEventTimelineItem(content = aMessageContent("hi"))), ) - val processor = RoomBeginningPostProcessor() + val processor = RoomBeginningPostProcessor(mode) val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = false) assertThat(processedItems).isEqualTo(expected) } @@ -64,7 +64,7 @@ class RoomBeginningPostProcessorTest { MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), ) - val processor = RoomBeginningPostProcessor() + val processor = RoomBeginningPostProcessor(mode) val processedItems = processor.process(timelineItems, isDm = false, hasMoreToLoadBackwards = false) assertThat(processedItems).isEqualTo( listOf(processor.createRoomBeginningItem()) + timelineItems @@ -76,7 +76,7 @@ class RoomBeginningPostProcessorTest { val timelineItems = listOf( MatrixTimelineItem.Virtual("EncryptedHistoryBanner", VirtualTimelineItem.EncryptedHistoryBanner), ) - val processor = RoomBeginningPostProcessor() + val processor = RoomBeginningPostProcessor(mode) val processedItems = processor.process(timelineItems, isDm = false, hasMoreToLoadBackwards = false) assertThat(processedItems).isEqualTo(timelineItems) } @@ -87,7 +87,7 @@ class RoomBeginningPostProcessorTest { MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), ) - val processor = RoomBeginningPostProcessor() + val processor = RoomBeginningPostProcessor(mode) val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true) assertThat(processedItems).isEqualTo(timelineItems) } @@ -97,7 +97,7 @@ class RoomBeginningPostProcessorTest { val timelineItems = listOf( MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), ) - val processor = RoomBeginningPostProcessor() + val processor = RoomBeginningPostProcessor(mode) val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true) assertThat(processedItems).isEqualTo(timelineItems) } @@ -108,7 +108,7 @@ class RoomBeginningPostProcessorTest { MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))), MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED))), ) - val processor = RoomBeginningPostProcessor() + val processor = RoomBeginningPostProcessor(mode) val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true) assertThat(processedItems).isEqualTo(timelineItems) }