Fix loading initial items of non-live timelines (#6598)
This was done automatically by the SDK in the past by returning a `Reset` timeline update, but this behaviour changed and now we have to do it.
This commit is contained in:
parent
67c0e4c140
commit
c349f74ce8
3 changed files with 48 additions and 4 deletions
|
|
@ -84,6 +84,7 @@ import kotlinx.coroutines.flow.collectLatest
|
|||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
|
@ -262,11 +263,16 @@ private fun TimelinePrefetchingHelper(
|
|||
firstVisibleItemIndex + layoutInfo.visibleItemsInfo.size >= layoutInfo.totalItemsCount - 40
|
||||
}
|
||||
|
||||
// If we have no timeline items, we need to back paginate to load some messages. This usually happens on all timelines except for live ones.
|
||||
// This automatic pagination was previously done by the SDK, and we received a `Reset` update, but now we need to do it ourselves.
|
||||
val isEmptyTimelineFlow = layoutInfoFlow.map { it.totalItemsCount == 0 }
|
||||
|
||||
combine(
|
||||
isCloseToStartOfLoadedTimelineFlow.distinctUntilChanged(),
|
||||
isScrollingFlow.distinctUntilChanged(),
|
||||
) { needsPrefetch, isScrolling ->
|
||||
needsPrefetch && isScrolling
|
||||
isEmptyTimelineFlow,
|
||||
) { needsPrefetch, isScrolling, isEmptyAndNeedsBackPagination ->
|
||||
isEmptyAndNeedsBackPagination || needsPrefetch && isScrolling
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.collectLatest { needsPrefetch ->
|
||||
|
|
|
|||
|
|
@ -522,6 +522,9 @@ class MessagesViewTest {
|
|||
rule.setMessagesView(
|
||||
state = stateWithActionListState,
|
||||
)
|
||||
// Clear initial 'LoadMore' event emitted when setting the state
|
||||
eventsRecorder.clear()
|
||||
|
||||
val verifiedUserSendFailure = rule.activity.getString(CommonStrings.screen_timeline_item_menu_send_failure_changed_identity, "Alice")
|
||||
rule.onNodeWithText(verifiedUserSendFailure).performClick()
|
||||
// Give time for the close animation to complete
|
||||
|
|
@ -585,6 +588,9 @@ class MessagesViewTest {
|
|||
),
|
||||
)
|
||||
rule.setMessagesView(state = state)
|
||||
// Clear initial 'LoadMore' event emitted when setting the state
|
||||
eventsRecorder.clear()
|
||||
|
||||
rule.onNodeWithText("This is a pinned message").performClick()
|
||||
eventsRecorder.assertSingle(TimelineEvent.FocusOnEvent(AN_EVENT_ID, debounce = FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS.milliseconds))
|
||||
}
|
||||
|
|
@ -601,6 +607,9 @@ class MessagesViewTest {
|
|||
timelineState = aTimelineState(eventSink = eventsRecorder)
|
||||
)
|
||||
rule.setMessagesView(state = state)
|
||||
// Clear initial 'LoadMore' event emitted when setting the state
|
||||
eventsRecorder.clear()
|
||||
|
||||
val text = rule.activity.getString(R.string.screen_room_timeline_tombstoned_room_action)
|
||||
// The bottomsheet subcompose seems to make the node to appear twice
|
||||
rule.onAllNodesWithText(text).onFirst().performClick()
|
||||
|
|
|
|||
|
|
@ -67,24 +67,31 @@ class TimelineViewTest {
|
|||
|
||||
@Test
|
||||
fun `reaching the end of the timeline does not send a LoadMore event`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
rule.setTimelineView(
|
||||
state = aTimelineState(
|
||||
timelineItems = persistentListOf(aTimelineItemEvent(content = aTimelineItemImageContent())),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
eventsRecorder.assertSingle(TimelineEvent.OnScrollFinished(firstIndex = 0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `scroll to bottom on live timeline does not emit the Event`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
rule.setTimelineView(
|
||||
state = aTimelineState(
|
||||
timelineItems = persistentListOf(aTimelineItemEvent(content = aTimelineItemImageContent())),
|
||||
isLive = true,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
forceJumpToBottomVisibility = true,
|
||||
)
|
||||
|
||||
eventsRecorder.assertSingle(TimelineEvent.OnScrollFinished(firstIndex = 0))
|
||||
eventsRecorder.clear()
|
||||
|
||||
val contentDescription = rule.activity.getString(CommonStrings.a11y_jump_to_bottom)
|
||||
rule.onNodeWithContentDescription(contentDescription).performClick()
|
||||
}
|
||||
|
|
@ -94,15 +101,33 @@ class TimelineViewTest {
|
|||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
rule.setTimelineView(
|
||||
state = aTimelineState(
|
||||
timelineItems = persistentListOf(aTimelineItemEvent(content = aTimelineItemImageContent())),
|
||||
isLive = false,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
|
||||
eventsRecorder.assertSingle(TimelineEvent.OnScrollFinished(firstIndex = 0))
|
||||
eventsRecorder.clear()
|
||||
|
||||
val contentDescription = rule.activity.getString(CommonStrings.a11y_jump_to_bottom)
|
||||
rule.onNodeWithContentDescription(contentDescription).performClick()
|
||||
eventsRecorder.assertSingle(TimelineEvent.JumpToLive)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an empty timeline triggers a prefetch`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
rule.setTimelineView(
|
||||
state = aTimelineState(
|
||||
timelineItems = persistentListOf(),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
|
||||
eventsRecorder.assertSingle(TimelineEvent.LoadMore(Timeline.PaginationDirection.BACKWARDS))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `show shield dialog`() {
|
||||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
|
|
@ -133,11 +158,15 @@ class TimelineViewTest {
|
|||
val eventsRecorder = EventsRecorder<TimelineEvent>()
|
||||
rule.setTimelineView(
|
||||
state = aTimelineState(
|
||||
timelineItems = persistentListOf(aTimelineItemEvent(content = aTimelineItemImageContent())),
|
||||
isLive = false,
|
||||
eventSink = eventsRecorder,
|
||||
messageShield = aCriticalShield(),
|
||||
),
|
||||
)
|
||||
eventsRecorder.assertSingle(TimelineEvent.OnScrollFinished(firstIndex = 0))
|
||||
eventsRecorder.clear()
|
||||
|
||||
rule.clickOn(CommonStrings.action_ok)
|
||||
eventsRecorder.assertSingle(TimelineEvent.HideShieldDialog)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue