Timeline : add a mode to differentiate between live/focused/pinned
This commit is contained in:
parent
0c5d10031d
commit
0832fb9f2f
6 changed files with 31 additions and 22 deletions
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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…
Add table
Add a link
Reference in a new issue