Extract snackbar displayer to its own methods.

This commit is contained in:
Benoit Marty 2025-01-27 15:07:17 +01:00
parent 5a517ca849
commit 0a534fd05b

View file

@ -10,7 +10,9 @@ package io.element.android.libraries.mediaviewer.impl.viewer
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.IntState
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
@ -38,6 +40,7 @@ import io.element.android.libraries.mediaviewer.impl.R
import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState
import io.element.android.libraries.mediaviewer.impl.local.LocalMediaActions import io.element.android.libraries.mediaviewer.impl.local.LocalMediaActions
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.PersistentList
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
@ -68,40 +71,12 @@ class MediaViewerPresenter @AssistedInject constructor(
@Composable @Composable
override fun present(): MediaViewerState { override fun present(): MediaViewerState {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val data by dataSource.collectAsState() val data = dataSource.collectAsState()
var currentIndex by remember { mutableIntStateOf(searchIndex(data, inputs.eventId)) } val currentIndex = remember { mutableIntStateOf(searchIndex(data.value, inputs.eventId)) }
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
val isRenderingLoadingBackward by remember { NoMoreItemsBackwardSnackBarDisplayer(currentIndex, data)
derivedStateOf { NoMoreItemsForwardSnackBarDisplayer(currentIndex, data)
currentIndex == data.lastIndex && data.lastOrNull() is MediaViewerPageData.Loading
}
}
if (isRenderingLoadingBackward) {
LaunchedEffect(Unit) {
// Observe the loading data vanishing
snapshotFlow { data.lastOrNull() is MediaViewerPageData.Loading }
.distinctUntilChanged()
.filter { !it }
.onEach { showNoMoreItemsSnackbar() }
.launchIn(this)
}
}
val isRenderingLoadingForward by remember {
derivedStateOf {
currentIndex == 0 && data.firstOrNull() is MediaViewerPageData.Loading
}
}
if (isRenderingLoadingForward) {
LaunchedEffect(Unit) {
// Observe the loading data vanishing
snapshotFlow { data.firstOrNull() is MediaViewerPageData.Loading }
.distinctUntilChanged()
.filter { !it }
.onEach { showNoMoreItemsSnackbar() }
.launchIn(this)
}
}
var mediaBottomSheetState by remember { mutableStateOf<MediaBottomSheetState>(MediaBottomSheetState.Hidden) } var mediaBottomSheetState by remember { mutableStateOf<MediaBottomSheetState>(MediaBottomSheetState.Hidden) }
@ -164,7 +139,7 @@ class MediaViewerPresenter @AssistedInject constructor(
mediaBottomSheetState = MediaBottomSheetState.Hidden mediaBottomSheetState = MediaBottomSheetState.Hidden
} }
is MediaViewerEvents.OnNavigateTo -> { is MediaViewerEvents.OnNavigateTo -> {
currentIndex = event.index currentIndex.intValue = event.index
} }
is MediaViewerEvents.LoadMore -> coroutineScope.launch { is MediaViewerEvents.LoadMore -> coroutineScope.launch {
dataSource.loadMore(event.direction) dataSource.loadMore(event.direction)
@ -173,8 +148,8 @@ class MediaViewerPresenter @AssistedInject constructor(
} }
return MediaViewerState( return MediaViewerState(
listData = data, listData = data.value,
currentIndex = currentIndex, currentIndex = currentIndex.intValue,
snackbarMessage = snackbarMessage, snackbarMessage = snackbarMessage,
canShowInfo = inputs.canShowInfo, canShowInfo = inputs.canShowInfo,
mediaBottomSheetState = mediaBottomSheetState, mediaBottomSheetState = mediaBottomSheetState,
@ -182,6 +157,50 @@ class MediaViewerPresenter @AssistedInject constructor(
) )
} }
@Composable
private fun NoMoreItemsBackwardSnackBarDisplayer(
currentIndex: IntState,
data: State<PersistentList<MediaViewerPageData>>,
) {
val isRenderingLoadingBackward by remember {
derivedStateOf {
currentIndex.intValue == data.value.lastIndex && data.value.lastOrNull() is MediaViewerPageData.Loading
}
}
if (isRenderingLoadingBackward) {
LaunchedEffect(Unit) {
// Observe the loading data vanishing
snapshotFlow { data.value.lastOrNull() is MediaViewerPageData.Loading }
.distinctUntilChanged()
.filter { !it }
.onEach { showNoMoreItemsSnackbar() }
.launchIn(this)
}
}
}
@Composable
private fun NoMoreItemsForwardSnackBarDisplayer(
currentIndex: IntState,
data: State<PersistentList<MediaViewerPageData>>,
) {
val isRenderingLoadingForward by remember {
derivedStateOf {
currentIndex.intValue == 0 && data.value.firstOrNull() is MediaViewerPageData.Loading
}
}
if (isRenderingLoadingForward) {
LaunchedEffect(Unit) {
// Observe the loading data vanishing
snapshotFlow { data.value.firstOrNull() is MediaViewerPageData.Loading }
.distinctUntilChanged()
.filter { !it }
.onEach { showNoMoreItemsSnackbar() }
.launchIn(this)
}
}
}
private fun showNoMoreItemsSnackbar() { private fun showNoMoreItemsSnackbar() {
val messageResId = when (inputs.mode) { val messageResId = when (inputs.mode) {
MediaViewerEntryPoint.MediaViewerMode.SingleMedia, MediaViewerEntryPoint.MediaViewerMode.SingleMedia,