MediaViewer: add error case in the UI.
This commit is contained in:
committed by
Benoit Marty
parent
b0bfea9cc4
commit
2267a4b787
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -154,6 +154,11 @@ open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState>
|
||||
MediaViewerPageData.Loading(Timeline.PaginationDirection.BACKWARDS)
|
||||
),
|
||||
),
|
||||
aMediaViewerState(
|
||||
listOf(
|
||||
MediaViewerPageData.Failure(Exception("error"))
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user