MediaViewer: add error case in the UI.

This commit is contained in:
Benoit Marty
2025-01-22 11:15:53 +01:00
committed by Benoit Marty
parent b0bfea9cc4
commit 2267a4b787
5 changed files with 71 additions and 10 deletions

View File

@@ -28,6 +28,7 @@ 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.thumbnailSource
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -68,9 +69,15 @@ class MediaViewerDataSource(
fun dataFlow(): Flow<PersistentList<MediaViewerPageData>> {
return galleryDataSource.groupedMediaItemsFlow()
.map { groupedItems ->
val mediaItems = groupedItems.dataOrNull()?.getItems(galleryMode).orEmpty()
withContext(dispatcher) {
buildMediaViewerPageList(mediaItems)
if (groupedItems is AsyncData.Failure) {
persistentListOf(
MediaViewerPageData.Failure(groupedItems.error),
)
} else {
val mediaItems = groupedItems.dataOrNull()?.getItems(galleryMode).orEmpty()
withContext(dispatcher) {
buildMediaViewerPageList(mediaItems)
}
}
}
}

View File

@@ -28,6 +28,10 @@ data class MediaViewerState(
)
sealed interface MediaViewerPageData {
data class Failure(
val throwable: Throwable,
) : MediaViewerPageData
data class Loading(
val direction: Timeline.PaginationDirection,
) : MediaViewerPageData

View File

@@ -154,6 +154,11 @@ open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState>
MediaViewerPageData.Loading(Timeline.PaginationDirection.BACKWARDS)
),
),
aMediaViewerState(
listOf(
MediaViewerPageData.Failure(Exception("error"))
),
),
)
}

View File

@@ -55,6 +55,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo
import io.element.android.libraries.designsystem.components.async.AsyncFailure
import io.element.android.libraries.designsystem.components.async.AsyncLoading
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
@@ -123,6 +124,14 @@ fun MediaViewerView(
beyondViewportPageCount = 1,
) { page ->
when (val dataForPage = state.listData[page]) {
is MediaViewerPageData.Failure -> {
MediaViewerErrorPage(
throwable = dataForPage.throwable,
onDismiss = {
onBackClick()
},
)
}
is MediaViewerPageData.Loading -> {
LaunchedEffect(Unit) {
state.eventSink(MediaViewerEvents.LoadMore(dataForPage.direction))
@@ -200,11 +209,13 @@ fun MediaViewerView(
else -> {
TopAppBar(
title = {
Text(
text = stringResource(id = CommonStrings.common_loading_more),
style = ElementTheme.typography.fontBodyMdMedium,
color = ElementTheme.colors.textPrimary,
)
if (currentData is MediaViewerPageData.Loading) {
Text(
text = stringResource(id = CommonStrings.common_loading_more),
style = ElementTheme.typography.fontBodyMdMedium,
color = ElementTheme.colors.textPrimary,
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent.copy(0.6f),
@@ -386,6 +397,41 @@ private fun MediaViewerLoadingPage(
}
}
@Composable
private fun MediaViewerErrorPage(
throwable: Throwable,
onDismiss: () -> Unit,
modifier: Modifier = Modifier,
) {
val flickState = rememberFlickToDismissState(dismissThresholdRatio = 0.1f, rotateOnDrag = false)
DismissFlickEffects(
flickState = flickState,
onDismissing = { animationDuration ->
delay(animationDuration / 3)
onDismiss()
},
onDragging = {},
)
FlickToDismiss(
state = flickState,
modifier = modifier.background(backgroundColorFor(flickState))
) {
Box(
modifier = Modifier
.fillMaxSize()
.navigationBarsPadding(),
contentAlignment = Alignment.Center
) {
AsyncFailure(
throwable = throwable,
onRetry = null
)
}
}
}
@Composable
private fun DismissFlickEffects(
flickState: FlickToDismissState,

View File

@@ -69,8 +69,7 @@ class MediaViewerDataSourceTest {
galleryDataSource.emitGroupedMediaItems(AsyncData.Loading())
assertThat(awaitItem().first()).isInstanceOf(MediaViewerPageData.Loading::class.java)
galleryDataSource.emitGroupedMediaItems(AsyncData.Failure(AN_EXCEPTION))
// TODO Add an error screen in the ui
assertThat(awaitItem().first()).isInstanceOf(MediaViewerPageData.Loading::class.java)
assertThat(awaitItem().first()).isEqualTo(MediaViewerPageData.Failure(AN_EXCEPTION))
}
}