Reimplement "Natural media viewer swiping order" (#6715)
This commit is contained in:
parent
2d203e83b9
commit
28e1062eed
4 changed files with 33 additions and 56 deletions
|
|
@ -122,25 +122,11 @@ class MediaViewerDataSource(
|
|||
*/
|
||||
private fun buildMediaViewerPageList(groupedItems: List<MediaItem>) = 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<MediaItem.LoadingIndicator>()
|
||||
val mediaEvents = itemsNoDateSeparator.filterIsInstance<MediaItem.Event>()
|
||||
// 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()
|
||||
|
|
|
|||
|
|
@ -177,21 +177,18 @@ class MediaViewerPresenter(
|
|||
currentIndex: IntState,
|
||||
data: State<ImmutableList<MediaViewerPageData>>,
|
||||
) {
|
||||
// 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<ImmutableList<MediaViewerPageData>>,
|
||||
) {
|
||||
// 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() }
|
||||
|
|
|
|||
|
|
@ -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 -> {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue