Media timeline: improve pagination logic.

This commit is contained in:
Benoit Marty 2024-12-10 14:39:10 +01:00 committed by Benoit Marty
parent d86430d56a
commit a4a8fc2268
5 changed files with 23 additions and 73 deletions

View file

@ -56,6 +56,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.flow.launchIn
@ -64,8 +65,6 @@ import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.EditedContent
import org.matrix.rustcomponents.sdk.FormattedBody
@ -172,36 +171,26 @@ class RustTimeline(
}
}
private val backwardsPaginationMutex = Mutex()
private val forwardsPaginationMutex = Mutex()
private fun getPaginationMutex(direction: Timeline.PaginationDirection) = when (direction) {
Timeline.PaginationDirection.BACKWARDS -> backwardsPaginationMutex
Timeline.PaginationDirection.FORWARDS -> forwardsPaginationMutex
}
// Use NonCancellable to avoid breaking the timeline when the coroutine is cancelled.
override suspend fun paginate(direction: Timeline.PaginationDirection): Result<Boolean> = withContext(NonCancellable) {
withContext(dispatcher) {
initLatch.await()
getPaginationMutex(direction).withLock {
runCatching {
if (!canPaginate(direction)) throw TimelineException.CannotPaginate
updatePaginationStatus(direction) { it.copy(isPaginating = true) }
when (direction) {
Timeline.PaginationDirection.BACKWARDS -> inner.paginateBackwards(PAGINATION_SIZE.toUShort())
Timeline.PaginationDirection.FORWARDS -> inner.focusedPaginateForwards(PAGINATION_SIZE.toUShort())
}
}.onFailure { error ->
updatePaginationStatus(direction) { it.copy(isPaginating = false) }
if (error is TimelineException.CannotPaginate) {
Timber.d("Can't paginate $direction on room ${matrixRoom.roomId} with paginationStatus: ${backPaginationStatus.value}")
} else {
Timber.e(error, "Error paginating $direction on room ${matrixRoom.roomId}")
}
}.onSuccess { hasReachedEnd ->
updatePaginationStatus(direction) { it.copy(isPaginating = false, hasMoreToLoad = !hasReachedEnd) }
runCatching {
if (!canPaginate(direction)) throw TimelineException.CannotPaginate
updatePaginationStatus(direction) { it.copy(isPaginating = true) }
when (direction) {
Timeline.PaginationDirection.BACKWARDS -> inner.paginateBackwards(PAGINATION_SIZE.toUShort())
Timeline.PaginationDirection.FORWARDS -> inner.focusedPaginateForwards(PAGINATION_SIZE.toUShort())
}
}.onFailure { error ->
if (error is TimelineException.CannotPaginate) {
Timber.d("Can't paginate $direction on room ${matrixRoom.roomId} with paginationStatus: ${backPaginationStatus.value}")
} else {
updatePaginationStatus(direction) { it.copy(isPaginating = false) }
Timber.e(error, "Error paginating $direction on room ${matrixRoom.roomId}")
}
}.onSuccess { hasReachedEnd ->
updatePaginationStatus(direction) { it.copy(isPaginating = false, hasMoreToLoad = !hasReachedEnd) }
}
}
}
@ -223,13 +212,13 @@ class RustTimeline(
override val timelineItems: Flow<List<MatrixTimelineItem>> = combine(
_timelineItems,
backPaginationStatus.map { it.hasMoreToLoad }.distinctUntilChanged(),
forwardPaginationStatus.map { it.hasMoreToLoad }.distinctUntilChanged(),
backPaginationStatus.filter { !it.isPaginating }.distinctUntilChanged(),
forwardPaginationStatus.filter { !it.isPaginating }.distinctUntilChanged(),
matrixRoom.roomInfoFlow.map { it.creator },
isTimelineInitialized,
) { timelineItems,
hasMoreToLoadBackward,
hasMoreToLoadForward,
backwardPaginationStatus,
forwardPaginationStatus,
roomCreator,
isTimelineInitialized ->
withContext(dispatcher) {
@ -239,15 +228,15 @@ class RustTimeline(
items = items,
isDm = matrixRoom.isDm,
roomCreator = roomCreator,
hasMoreToLoadBackwards = hasMoreToLoadBackward,
hasMoreToLoadBackwards = backwardPaginationStatus.hasMoreToLoad,
)
}
.let { items ->
loadingIndicatorsPostProcessor.process(
items = items,
isTimelineInitialized = isTimelineInitialized,
hasMoreToLoadBackward = hasMoreToLoadBackward,
hasMoreToLoadForward = hasMoreToLoadForward
hasMoreToLoadBackward = backwardPaginationStatus.hasMoreToLoad,
hasMoreToLoadForward = forwardPaginationStatus.hasMoreToLoad,
)
}
.let { items ->