Browse Source

Timeline : add a mode to differentiate between live/focused/pinned

pull/3392/head
ganfra 2 months ago
parent
commit
741c3679ea
  1. 6
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt
  2. 10
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
  3. 12
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt
  4. 7
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/LastForwardIndicatorsPostProcessor.kt
  5. 4
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt
  6. 14
      libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt

6
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt

@ -46,6 +46,12 @@ interface Timeline : AutoCloseable {
FORWARDS FORWARDS
} }
enum class Mode {
LIVE,
FOCUSED_ON_EVENT,
FOCUSED_ON_PINNED_EVENTS
}
val membershipChangeEventReceived: Flow<Unit> val membershipChangeEventReceived: Flow<Unit>
suspend fun sendReadReceipt(eventId: EventId, receiptType: ReceiptType): Result<Unit> suspend fun sendReadReceipt(eventId: EventId, receiptType: ReceiptType): Result<Unit>
suspend fun paginate(direction: PaginationDirection): Result<Boolean> suspend fun paginate(direction: PaginationDirection): Result<Boolean>

10
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>(MatrixRoomNotificationSettingsState.Unknown) private val _roomNotificationSettingsStateFlow = MutableStateFlow<MatrixRoomNotificationSettingsState>(MatrixRoomNotificationSettingsState.Unknown)
override val roomNotificationSettingsStateFlow: StateFlow<MatrixRoomNotificationSettingsState> = _roomNotificationSettingsStateFlow override val roomNotificationSettingsStateFlow: StateFlow<MatrixRoomNotificationSettingsState> = _roomNotificationSettingsStateFlow
override val liveTimeline = createTimeline(innerTimeline, isLive = true) { override val liveTimeline = createTimeline(innerTimeline, mode = Timeline.Mode.LIVE) {
_syncUpdateFlow.value = systemClock.epochMillis() _syncUpdateFlow.value = systemClock.epochMillis()
} }
@ -181,7 +181,7 @@ class RustMatrixRoom(
numContextEvents = 50u, numContextEvents = 50u,
internalIdPrefix = "focus_$eventId", internalIdPrefix = "focus_$eventId",
).let { inner -> ).let { inner ->
createTimeline(inner, isLive = false) createTimeline(inner, mode = Timeline.Mode.FOCUSED_ON_EVENT)
} }
}.mapFailure { }.mapFailure {
it.toFocusEventException() it.toFocusEventException()
@ -198,7 +198,7 @@ class RustMatrixRoom(
internalIdPrefix = "pinned_events", internalIdPrefix = "pinned_events",
maxEventsToLoad = 100u, maxEventsToLoad = 100u,
).let { inner -> ).let { inner ->
createTimeline(inner, isLive = false) createTimeline(inner, mode = Timeline.Mode.FOCUSED_ON_PINNED_EVENTS)
} }
}.onFailure { }.onFailure {
if (it is CancellationException) { if (it is CancellationException) {
@ -655,13 +655,13 @@ class RustMatrixRoom(
private fun createTimeline( private fun createTimeline(
timeline: InnerTimeline, timeline: InnerTimeline,
isLive: Boolean, mode: Timeline.Mode,
onNewSyncedEvent: () -> Unit = {}, onNewSyncedEvent: () -> Unit = {},
): Timeline { ): Timeline {
val timelineCoroutineScope = roomCoroutineScope.childScope(coroutineDispatchers.main, "TimelineScope-$roomId-$timeline") val timelineCoroutineScope = roomCoroutineScope.childScope(coroutineDispatchers.main, "TimelineScope-$roomId-$timeline")
return RustTimeline( return RustTimeline(
isKeyBackupEnabled = isKeyBackupEnabled, isKeyBackupEnabled = isKeyBackupEnabled,
isLive = isLive, mode = mode,
matrixRoom = this, matrixRoom = this,
systemClock = systemClock, systemClock = systemClock,
coroutineScope = timelineCoroutineScope, coroutineScope = timelineCoroutineScope,

12
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( class RustTimeline(
private val inner: InnerTimeline, private val inner: InnerTimeline,
private val isLive: Boolean, mode: Timeline.Mode,
systemClock: SystemClock, systemClock: SystemClock,
isKeyBackupEnabled: Boolean, isKeyBackupEnabled: Boolean,
private val matrixRoom: MatrixRoom, private val matrixRoom: MatrixRoom,
@ -131,21 +131,21 @@ class RustTimeline(
onNewSyncedEvent = onNewSyncedEvent, onNewSyncedEvent = onNewSyncedEvent,
) )
private val roomBeginningPostProcessor = RoomBeginningPostProcessor() private val roomBeginningPostProcessor = RoomBeginningPostProcessor(mode)
private val loadingIndicatorsPostProcessor = LoadingIndicatorsPostProcessor(systemClock) private val loadingIndicatorsPostProcessor = LoadingIndicatorsPostProcessor(systemClock)
private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(isLive) private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(mode)
private val backPaginationStatus = MutableStateFlow( 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( private val forwardPaginationStatus = MutableStateFlow(
Timeline.PaginationStatus(isPaginating = false, hasMoreToLoad = !isLive) Timeline.PaginationStatus(isPaginating = false, hasMoreToLoad = mode == Timeline.Mode.FOCUSED_ON_EVENT)
) )
init { init {
coroutineScope.fetchMembers() coroutineScope.fetchMembers()
if (isLive) { if (mode == Timeline.Mode.LIVE) {
// When timeline is live, we need to listen to the back pagination status as // When timeline is live, we need to listen to the back pagination status as
// sdk can automatically paginate backwards. // sdk can automatically paginate backwards.
coroutineScope.registerBackPaginationStatusListener() coroutineScope.registerBackPaginationStatusListener()

7
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 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.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem 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. * This post processor is responsible for adding virtual items to indicate all the previous last forward item.
*/ */
class LastForwardIndicatorsPostProcessor( class LastForwardIndicatorsPostProcessor(
private val isTimelineLive: Boolean, private val mode: Timeline.Mode,
) { ) {
private val lastForwardIdentifiers = LinkedHashSet<String>() private val lastForwardIdentifiers = LinkedHashSet<String>()
fun process( fun process(
items: List<MatrixTimelineItem>, items: List<MatrixTimelineItem>,
): List<MatrixTimelineItem> { ): List<MatrixTimelineItem> {
// If the timeline is live, we don't have any last forward indicator to display // We don't need to add the last forward indicator if we are not in the FOCUSED_ON_EVENT mode
if (isTimelineLive) { if (mode != Timeline.Mode.FOCUSED_ON_EVENT) {
return items return items
} else { } else {
return buildList { return buildList {

4
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 androidx.annotation.VisibleForTesting
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.item.event.MembershipChange 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.OtherState
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent 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 * 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. * or add the RoomBeginning item for non DM room.
*/ */
class RoomBeginningPostProcessor { class RoomBeginningPostProcessor(private val mode: Timeline.Mode) {
fun process( fun process(
items: List<MatrixTimelineItem>, items: List<MatrixTimelineItem>,
isDm: Boolean, isDm: Boolean,
hasMoreToLoadBackwards: Boolean hasMoreToLoadBackwards: Boolean
): List<MatrixTimelineItem> { ): List<MatrixTimelineItem> {
return when { return when {
mode == Timeline.Mode.FOCUSED_ON_PINNED_EVENTS -> items
hasMoreToLoadBackwards -> items hasMoreToLoadBackwards -> items
isDm -> processForDM(items) isDm -> processForDM(items)
else -> processForRoom(items) else -> processForRoom(items)

14
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.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))),
MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), 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) val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = false)
assertThat(processedItems).isEmpty() 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.member_other", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED))),
MatrixTimelineItem.Event("m.room.message", anEventTimelineItem(content = aMessageContent("hi"))), 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) val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = false)
assertThat(processedItems).isEqualTo(expected) 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.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))),
MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), 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) val processedItems = processor.process(timelineItems, isDm = false, hasMoreToLoadBackwards = false)
assertThat(processedItems).isEqualTo( assertThat(processedItems).isEqualTo(
listOf(processor.createRoomBeginningItem()) + timelineItems listOf(processor.createRoomBeginningItem()) + timelineItems
@ -76,7 +76,7 @@ class RoomBeginningPostProcessorTest {
val timelineItems = listOf( val timelineItems = listOf(
MatrixTimelineItem.Virtual("EncryptedHistoryBanner", VirtualTimelineItem.EncryptedHistoryBanner), MatrixTimelineItem.Virtual("EncryptedHistoryBanner", VirtualTimelineItem.EncryptedHistoryBanner),
) )
val processor = RoomBeginningPostProcessor() val processor = RoomBeginningPostProcessor(mode)
val processedItems = processor.process(timelineItems, isDm = false, hasMoreToLoadBackwards = false) val processedItems = processor.process(timelineItems, isDm = false, hasMoreToLoadBackwards = false)
assertThat(processedItems).isEqualTo(timelineItems) 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.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))),
MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), 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) val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true)
assertThat(processedItems).isEqualTo(timelineItems) assertThat(processedItems).isEqualTo(timelineItems)
} }
@ -97,7 +97,7 @@ class RoomBeginningPostProcessorTest {
val timelineItems = listOf( val timelineItems = listOf(
MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))), 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) val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true)
assertThat(processedItems).isEqualTo(timelineItems) 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.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))), 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) val processedItems = processor.process(timelineItems, isDm = true, hasMoreToLoadBackwards = true)
assertThat(processedItems).isEqualTo(timelineItems) assertThat(processedItems).isEqualTo(timelineItems)
} }

Loading…
Cancel
Save