Add timestamp to trigger back pagination.

This commit is contained in:
Benoit Marty 2025-01-22 15:04:34 +01:00 committed by Benoit Marty
parent 5e506be062
commit 40a44cae75
7 changed files with 48 additions and 12 deletions

View file

@ -27,6 +27,7 @@ import io.element.android.libraries.mediaviewer.impl.gallery.eventId
import io.element.android.libraries.mediaviewer.impl.gallery.mediaInfo import io.element.android.libraries.mediaviewer.impl.gallery.mediaInfo
import io.element.android.libraries.mediaviewer.impl.gallery.mediaSource import io.element.android.libraries.mediaviewer.impl.gallery.mediaSource
import io.element.android.libraries.mediaviewer.impl.gallery.thumbnailSource import io.element.android.libraries.mediaviewer.impl.gallery.thumbnailSource
import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
@ -42,6 +43,7 @@ class MediaViewerDataSource(
private val galleryDataSource: MediaGalleryDataSource, private val galleryDataSource: MediaGalleryDataSource,
private val mediaLoader: MatrixMediaLoader, private val mediaLoader: MatrixMediaLoader,
private val localMediaFactory: LocalMediaFactory, private val localMediaFactory: LocalMediaFactory,
private val systemClock: SystemClock,
) { ) {
// List of media files that are currently being loaded // List of media files that are currently being loaded
private val mediaFiles: MutableList<MediaFile> = mutableListOf() private val mediaFiles: MutableList<MediaFile> = mutableListOf()
@ -108,12 +110,20 @@ class MediaViewerDataSource(
) )
} }
is MediaItem.LoadingIndicator -> add( is MediaItem.LoadingIndicator -> add(
MediaViewerPageData.Loading(mediaItem.direction) MediaViewerPageData.Loading(
direction = mediaItem.direction,
timestamp = systemClock.epochMillis(),
)
) )
} }
} }
if (isEmpty()) { if (isEmpty()) {
add(MediaViewerPageData.Loading(Timeline.PaginationDirection.BACKWARDS)) add(
MediaViewerPageData.Loading(
direction = Timeline.PaginationDirection.BACKWARDS,
timestamp = systemClock.epochMillis(),
)
)
} }
}.toPersistentList() }.toPersistentList()

View file

@ -27,6 +27,7 @@ import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory
import io.element.android.libraries.mediaviewer.impl.gallery.MediaGalleryMode import io.element.android.libraries.mediaviewer.impl.gallery.MediaGalleryMode
import io.element.android.libraries.mediaviewer.impl.gallery.SingleMediaGalleryDataSource import io.element.android.libraries.mediaviewer.impl.gallery.SingleMediaGalleryDataSource
import io.element.android.libraries.mediaviewer.impl.gallery.TimelineMediaGalleryDataSource import io.element.android.libraries.mediaviewer.impl.gallery.TimelineMediaGalleryDataSource
import io.element.android.services.toolbox.api.systemclock.SystemClock
@ContributesNode(RoomScope::class) @ContributesNode(RoomScope::class)
class MediaViewerNode @AssistedInject constructor( class MediaViewerNode @AssistedInject constructor(
@ -37,6 +38,7 @@ class MediaViewerNode @AssistedInject constructor(
mediaLoader: MatrixMediaLoader, mediaLoader: MatrixMediaLoader,
localMediaFactory: LocalMediaFactory, localMediaFactory: LocalMediaFactory,
coroutineDispatchers: CoroutineDispatchers, coroutineDispatchers: CoroutineDispatchers,
systemClock: SystemClock,
) : Node(buildContext, plugins = plugins), ) : Node(buildContext, plugins = plugins),
MediaViewerNavigator { MediaViewerNavigator {
private val inputs = inputs<MediaViewerEntryPoint.Params>() private val inputs = inputs<MediaViewerEntryPoint.Params>()
@ -77,7 +79,8 @@ class MediaViewerNode @AssistedInject constructor(
galleryMode = galleryMode, galleryMode = galleryMode,
galleryDataSource = mediaGallerySource, galleryDataSource = mediaGallerySource,
mediaLoader = mediaLoader, mediaLoader = mediaLoader,
localMediaFactory = localMediaFactory localMediaFactory = localMediaFactory,
systemClock = systemClock,
) )
) )

View file

@ -34,6 +34,7 @@ sealed interface MediaViewerPageData {
data class Loading( data class Loading(
val direction: Timeline.PaginationDirection, val direction: Timeline.PaginationDirection,
val timestamp: Long,
) : MediaViewerPageData ) : MediaViewerPageData
data class MediaViewerData( data class MediaViewerData(

View file

@ -151,7 +151,7 @@ open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState>
}, },
aMediaViewerState( aMediaViewerState(
listOf( listOf(
MediaViewerPageData.Loading(Timeline.PaginationDirection.BACKWARDS) aMediaViewerPageDataLoading()
), ),
), ),
aMediaViewerState( aMediaViewerState(
@ -162,6 +162,16 @@ open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState>
) )
} }
fun aMediaViewerPageDataLoading(
direction: Timeline.PaginationDirection = Timeline.PaginationDirection.BACKWARDS,
timestamp: Long = 0L,
): MediaViewerPageData {
return MediaViewerPageData.Loading(
direction = direction,
timestamp = timestamp,
)
}
fun aMediaViewerPageData( fun aMediaViewerPageData(
downloadedMedia: AsyncData<LocalMedia> = AsyncData.Uninitialized, downloadedMedia: AsyncData<LocalMedia> = AsyncData.Uninitialized,
mediaInfo: MediaInfo = anImageMediaInfo(), mediaInfo: MediaInfo = anImageMediaInfo(),

View file

@ -128,7 +128,7 @@ fun MediaViewerView(
) )
} }
is MediaViewerPageData.Loading -> { is MediaViewerPageData.Loading -> {
LaunchedEffect(Unit) { LaunchedEffect(dataForPage.timestamp) {
state.eventSink(MediaViewerEvents.LoadMore(dataForPage.direction)) state.eventSink(MediaViewerEvents.LoadMore(dataForPage.direction))
} }
MediaViewerLoadingPage( MediaViewerLoadingPage(

View file

@ -27,6 +27,8 @@ import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemFile
import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemImage import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemImage
import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemLoadingIndicator import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemLoadingIndicator
import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory
import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.lambda.value
import io.element.android.tests.testutils.testCoroutineDispatchers import io.element.android.tests.testutils.testCoroutineDispatchers
@ -90,7 +92,12 @@ class MediaViewerDataSourceTest {
) )
val result = awaitItem() val result = awaitItem()
assertThat(result).hasSize(1) assertThat(result).hasSize(1)
assertThat(result.first()).isEqualTo(MediaViewerPageData.Loading(Timeline.PaginationDirection.BACKWARDS)) assertThat(result.first()).isEqualTo(
MediaViewerPageData.Loading(
direction = Timeline.PaginationDirection.BACKWARDS,
timestamp = A_FAKE_TIMESTAMP,
)
)
} }
} }
@ -118,8 +125,14 @@ class MediaViewerDataSourceTest {
) )
val result = awaitItem() val result = awaitItem()
assertThat(result).containsExactly( assertThat(result).containsExactly(
MediaViewerPageData.Loading(Timeline.PaginationDirection.BACKWARDS), MediaViewerPageData.Loading(
MediaViewerPageData.Loading(Timeline.PaginationDirection.FORWARDS), direction = Timeline.PaginationDirection.BACKWARDS,
timestamp = A_FAKE_TIMESTAMP,
),
MediaViewerPageData.Loading(
direction = Timeline.PaginationDirection.FORWARDS,
timestamp = A_FAKE_TIMESTAMP,
),
) )
} }
} }
@ -255,10 +268,6 @@ class MediaViewerDataSourceTest {
} }
} }
@Test
fun clearLoadingError() {
}
private fun TestScope.createMediaViewerDataSource( private fun TestScope.createMediaViewerDataSource(
galleryMode: MediaGalleryMode = MediaGalleryMode.Images, galleryMode: MediaGalleryMode = MediaGalleryMode.Images,
galleryDataSource: MediaGalleryDataSource = FakeMediaGalleryDataSource(), galleryDataSource: MediaGalleryDataSource = FakeMediaGalleryDataSource(),
@ -270,5 +279,6 @@ class MediaViewerDataSourceTest {
galleryDataSource = galleryDataSource, galleryDataSource = galleryDataSource,
mediaLoader = mediaLoader, mediaLoader = mediaLoader,
localMediaFactory = localMediaFactory, localMediaFactory = localMediaFactory,
systemClock = FakeSystemClock(),
) )
} }

View file

@ -37,6 +37,7 @@ import io.element.android.libraries.mediaviewer.impl.gallery.MediaGalleryMode
import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemImage import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemImage
import io.element.android.libraries.mediaviewer.test.FakeLocalMediaActions import io.element.android.libraries.mediaviewer.test.FakeLocalMediaActions
import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.lambda.value
@ -593,6 +594,7 @@ class MediaViewerPresenterTest {
galleryDataSource = mediaGalleryDataSource, galleryDataSource = mediaGalleryDataSource,
mediaLoader = matrixMediaLoader, mediaLoader = matrixMediaLoader,
localMediaFactory = localMediaFactory, localMediaFactory = localMediaFactory,
systemClock = FakeSystemClock(),
), ),
room = room, room = room,
localMediaActions = localMediaActions, localMediaActions = localMediaActions,