Merge pull request #731 from vector-im/feature/fga/timeline_back_pagination

Feature/fga/timeline back pagination
This commit is contained in:
Benoit Marty 2023-06-30 09:27:36 +02:00 committed by GitHub
commit a2a5d251d1
8 changed files with 43 additions and 69 deletions

View file

@ -59,21 +59,16 @@ class TimelinePresenter @Inject constructor(
var lastReadMarkerIndex by rememberSaveable { mutableStateOf(Int.MAX_VALUE) }
var lastReadMarkerId by rememberSaveable { mutableStateOf<EventId?>(null) }
val timelineItems = timelineItemsFactory
.flow()
.collectAsState()
val paginationState = timeline
.paginationState()
.collectAsState()
val timelineItems by timelineItemsFactory.collectItemsAsState()
val paginationState by timeline.paginationState.collectAsState()
fun handleEvents(event: TimelineEvents) {
when (event) {
TimelineEvents.LoadMore -> localCoroutineScope.loadMore(paginationState.value)
TimelineEvents.LoadMore -> localCoroutineScope.loadMore(paginationState)
is TimelineEvents.SetHighlightedEvent -> highlightedEventId.value = event.eventId
is TimelineEvents.OnScrollFinished -> {
// Get last valid EventId seen by the user, as the first index might refer to a Virtual item
val eventId = getLastEventIdBeforeOrAt(event.firstIndex, timelineItems.value) ?: return
val eventId = getLastEventIdBeforeOrAt(event.firstIndex, timelineItems) ?: return
if (event.firstIndex <= lastReadMarkerIndex && eventId != lastReadMarkerId) {
lastReadMarkerIndex = event.firstIndex
lastReadMarkerId = eventId
@ -85,11 +80,11 @@ class TimelinePresenter @Inject constructor(
LaunchedEffect(Unit) {
timeline
.timelineItems()
.timelineItems
.onEach(timelineItemsFactory::replaceWith)
.onEach { timelineItems ->
if (timelineItems.isEmpty()) {
loadMore(paginationState.value)
loadMore(paginationState)
}
}
.launchIn(this)
@ -97,8 +92,8 @@ class TimelinePresenter @Inject constructor(
return TimelineState(
highlightedEventId = highlightedEventId.value,
paginationState = paginationState.value,
timelineItems = timelineItems.value,
paginationState = paginationState,
timelineItems = timelineItems,
eventSink = ::handleEvents
)
}

View file

@ -39,7 +39,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.pluralStringResource
@ -62,7 +61,6 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
@Composable
@ -123,8 +121,7 @@ fun TimelineView(
TimelineScrollHelper(
lazyListState = lazyListState,
timelineItems = state.timelineItems,
onLoadMore = ::onReachedLoadMore
timelineItems = state.timelineItems
)
}
}
@ -222,7 +219,6 @@ fun TimelineItemRow(
internal fun BoxScope.TimelineScrollHelper(
lazyListState: LazyListState,
timelineItems: ImmutableList<TimelineItem>,
onLoadMore: () -> Unit = {},
) {
val coroutineScope = rememberCoroutineScope()
val firstVisibleItemIndex by remember { derivedStateOf { lazyListState.firstVisibleItemIndex } }
@ -236,24 +232,6 @@ internal fun BoxScope.TimelineScrollHelper(
}
}
// Handle load more preloading
val loadMore by remember {
derivedStateOf {
val layoutInfo = lazyListState.layoutInfo
val totalItemsNumber = layoutInfo.totalItemsCount
val lastVisibleItemIndex = (layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0) + 1
lastVisibleItemIndex > (totalItemsNumber - 30)
}
}
LaunchedEffect(loadMore) {
snapshotFlow { loadMore }
.distinctUntilChanged()
.collect {
onLoadMore()
}
}
// Jump to bottom button
if (firstVisibleItemIndex > 2) {
FloatingActionButton(

View file

@ -16,6 +16,9 @@
package io.element.android.features.messages.impl.timeline.factories
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.recyclerview.widget.DiffUtil
import io.element.android.features.messages.impl.timeline.diff.CacheInvalidator
import io.element.android.features.messages.impl.timeline.diff.MatrixTimelineItemsDiffCallback
@ -27,11 +30,8 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
@ -55,7 +55,10 @@ class TimelineItemsFactory @Inject constructor(
private val lock = Mutex()
private val cacheInvalidator = CacheInvalidator(timelineItemsCache)
fun flow(): StateFlow<ImmutableList<TimelineItem>> = timelineItems.asStateFlow()
@Composable
fun collectItemsAsState(): State<ImmutableList<TimelineItem>> {
return timelineItems.collectAsState()
}
suspend fun replaceWith(
timelineItems: List<MatrixTimelineItem>,