diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt index 24e48531f0..eadbd544fc 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt @@ -122,25 +122,11 @@ class MediaViewerDataSource( */ private fun buildMediaViewerPageList(groupedItems: List) = buildList { // Filter out DateSeparator items, we do not need them for the media viewer - val itemsNoDateSeparator = groupedItems.filterNot { it is MediaItem.DateSeparator } - // Separate loading indicators and media events - val loadingIndicators = itemsNoDateSeparator.filterIsInstance() - val mediaEvents = itemsNoDateSeparator.filterIsInstance() - // Determine backward and forward loading indicators - val backwardLoading = loadingIndicators.find { it.direction == Timeline.PaginationDirection.BACKWARDS } - val forwardLoading = loadingIndicators.find { it.direction == Timeline.PaginationDirection.FORWARDS } - // Build ordered list: backward loading, media events (oldest first), forward loading - // Media events are currently newest first, reverse to get oldest first - val orderedEvents = mediaEvents.reversed() - // Create new list of MediaItem in order: backwardLoading, orderedEvents, forwardLoading - val orderedItems = buildList { - backwardLoading?.let { add(it) } - addAll(orderedEvents) - forwardLoading?.let { add(it) } - } - pagerKeysHandler.accept(orderedItems) - orderedItems.forEach { mediaItem -> + val groupedItemsNoDateSeparator = groupedItems.filterNot { it is MediaItem.DateSeparator } + pagerKeysHandler.accept(groupedItemsNoDateSeparator) + groupedItemsNoDateSeparator.forEach { mediaItem -> when (mediaItem) { + is MediaItem.DateSeparator -> Unit is MediaItem.Event -> { val sourceUrl = mediaItem.mediaSource().safeUrl val localMedia = localMediaStates.getOrPut(sourceUrl) { @@ -164,7 +150,6 @@ class MediaViewerDataSource( pagerKey = pagerKeysHandler.getKey(mediaItem), ) ) - is MediaItem.DateSeparator -> Unit // already filtered out } } }.toImmutableList() diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt index 60f03bb1e0..138c73c383 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt @@ -177,21 +177,18 @@ class MediaViewerPresenter( currentIndex: IntState, data: State>, ) { + // With newest-first ordering, backward loading indicator is at the last index val isRenderingLoadingBackward by remember { derivedStateOf { - currentIndex.intValue == 0 && + currentIndex.intValue == data.value.lastIndex && data.value.size > 1 && - data.value.firstOrNull() is MediaViewerPageData.Loading && - (data.value.firstOrNull() as? MediaViewerPageData.Loading)?.direction == Timeline.PaginationDirection.BACKWARDS + data.value.lastOrNull() is MediaViewerPageData.Loading } } if (isRenderingLoadingBackward) { LaunchedEffect(Unit) { // Observe the loading data vanishing - snapshotFlow { - val first = data.value.firstOrNull() - first is MediaViewerPageData.Loading && first.direction == Timeline.PaginationDirection.BACKWARDS - } + snapshotFlow { data.value.lastOrNull() is MediaViewerPageData.Loading } .distinctUntilChanged() .filter { !it } .onEach { showNoMoreItemsSnackbar() } @@ -205,21 +202,18 @@ class MediaViewerPresenter( currentIndex: IntState, data: State>, ) { + // With newest-first ordering, forward loading indicator is at the first index val isRenderingLoadingForward by remember { derivedStateOf { - currentIndex.intValue == data.value.lastIndex && + currentIndex.intValue == 0 && data.value.size > 1 && - data.value.lastOrNull() is MediaViewerPageData.Loading && - (data.value.lastOrNull() as? MediaViewerPageData.Loading)?.direction == Timeline.PaginationDirection.FORWARDS + data.value.firstOrNull() is MediaViewerPageData.Loading } } if (isRenderingLoadingForward) { LaunchedEffect(Unit) { // Observe the loading data vanishing - snapshotFlow { - val last = data.value.lastOrNull() - last is MediaViewerPageData.Loading && last.direction == Timeline.PaginationDirection.FORWARDS - } + snapshotFlow { data.value.firstOrNull() is MediaViewerPageData.Loading } .distinctUntilChanged() .filter { !it } .onEach { showNoMoreItemsSnackbar() } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 738d940453..3738f6643b 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -182,6 +182,7 @@ fun MediaViewerView( // Pre-load previous and next pages beyondViewportPageCount = 1, key = { index -> state.listData[index].pagerKey }, + reverseLayout = true, ) { page -> when (val dataForPage = state.listData[page]) { is MediaViewerPageData.Failure -> { diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt index caacb03804..86689859ad 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt @@ -593,20 +593,20 @@ class MediaViewerPresenterTest { if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) { GroupedMediaItems( imageAndVideoItems = persistentListOf(), - fileItems = persistentListOf(aBackwardLoadingIndicator, anImage, aForwardLoadingIndicator), + fileItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), ) } else { GroupedMediaItems( - imageAndVideoItems = persistentListOf(aBackwardLoadingIndicator, anImage, aForwardLoadingIndicator), + imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), fileItems = persistentListOf(), ) } ) ) val updatedState = awaitItem() - // User navigate to the last item (forward loading indicator) + // User navigate to the first item (forward loading indicator) updatedState.eventSink( - MediaViewerEvent.OnNavigateTo(2) + MediaViewerEvent.OnNavigateTo(0) ) // data source claims that there is no more items to load forward mediaGalleryDataSource.emitGroupedMediaItems( @@ -614,21 +614,19 @@ class MediaViewerPresenterTest { if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) { GroupedMediaItems( imageAndVideoItems = persistentListOf(), - fileItems = persistentListOf(aBackwardLoadingIndicator, anImage), + fileItems = persistentListOf(anImage, aBackwardLoadingIndicator), ) } else { GroupedMediaItems( - imageAndVideoItems = persistentListOf(aBackwardLoadingIndicator, anImage), + imageAndVideoItems = persistentListOf(anImage, aBackwardLoadingIndicator), fileItems = persistentListOf(), ) } ) ) - var stateWithSnackbar = awaitItem() - while (stateWithSnackbar.snackbarMessage == null) { - stateWithSnackbar = awaitItem() - } - assertThat(stateWithSnackbar.snackbarMessage.messageResId).isEqualTo(expectedSnackbarResId) + skipItems(1) + val stateWithSnackbar = awaitItem() + assertThat(stateWithSnackbar.snackbarMessage!!.messageResId).isEqualTo(expectedSnackbarResId) } } @@ -667,42 +665,41 @@ class MediaViewerPresenterTest { if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) { GroupedMediaItems( imageAndVideoItems = persistentListOf(), - fileItems = persistentListOf(aBackwardLoadingIndicator, anImage, aForwardLoadingIndicator), + fileItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), ) } else { GroupedMediaItems( - imageAndVideoItems = persistentListOf(aBackwardLoadingIndicator, anImage, aForwardLoadingIndicator), + imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), fileItems = persistentListOf(), ) } ) ) val updatedState = awaitItem() - // User navigate to the first item (backward loading indicator) + // User navigate to the last item (backward loading indicator) updatedState.eventSink( - MediaViewerEvent.OnNavigateTo(0) + MediaViewerEvent.OnNavigateTo(2) ) + skipItems(1) // data source claims that there is no more items to load backward mediaGalleryDataSource.emitGroupedMediaItems( AsyncData.Success( if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) { GroupedMediaItems( imageAndVideoItems = persistentListOf(), - fileItems = persistentListOf(anImage, aForwardLoadingIndicator), + fileItems = persistentListOf(aForwardLoadingIndicator, anImage), ) } else { GroupedMediaItems( - imageAndVideoItems = persistentListOf(anImage, aForwardLoadingIndicator), + imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage), fileItems = persistentListOf(), ) } ) ) - var stateWithSnackbar = awaitItem() - while (stateWithSnackbar.snackbarMessage == null) { - stateWithSnackbar = awaitItem() - } - assertThat(stateWithSnackbar.snackbarMessage.messageResId).isEqualTo(expectedSnackbarResId) + skipItems(1) + val stateWithSnackbar = awaitItem() + assertThat(stateWithSnackbar.snackbarMessage!!.messageResId).isEqualTo(expectedSnackbarResId) } } @@ -720,7 +717,7 @@ class MediaViewerPresenterTest { mediaGalleryDataSource.emitGroupedMediaItems( AsyncData.Success( GroupedMediaItems( - imageAndVideoItems = persistentListOf(aBackwardLoadingIndicator, anImage, aForwardLoadingIndicator), + imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), fileItems = persistentListOf(), ) )