MediaViewer: add error case in the UI.
This commit is contained in:
parent
b0bfea9cc4
commit
2267a4b787
5 changed files with 71 additions and 10 deletions
|
|
@ -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.mediaSource
|
||||||
import io.element.android.libraries.mediaviewer.impl.gallery.thumbnailSource
|
import io.element.android.libraries.mediaviewer.impl.gallery.thumbnailSource
|
||||||
import kotlinx.collections.immutable.PersistentList
|
import kotlinx.collections.immutable.PersistentList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
@ -68,9 +69,15 @@ class MediaViewerDataSource(
|
||||||
fun dataFlow(): Flow<PersistentList<MediaViewerPageData>> {
|
fun dataFlow(): Flow<PersistentList<MediaViewerPageData>> {
|
||||||
return galleryDataSource.groupedMediaItemsFlow()
|
return galleryDataSource.groupedMediaItemsFlow()
|
||||||
.map { groupedItems ->
|
.map { groupedItems ->
|
||||||
val mediaItems = groupedItems.dataOrNull()?.getItems(galleryMode).orEmpty()
|
if (groupedItems is AsyncData.Failure) {
|
||||||
withContext(dispatcher) {
|
persistentListOf(
|
||||||
buildMediaViewerPageList(mediaItems)
|
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 {
|
sealed interface MediaViewerPageData {
|
||||||
|
data class Failure(
|
||||||
|
val throwable: Throwable,
|
||||||
|
) : MediaViewerPageData
|
||||||
|
|
||||||
data class Loading(
|
data class Loading(
|
||||||
val direction: Timeline.PaginationDirection,
|
val direction: Timeline.PaginationDirection,
|
||||||
) : MediaViewerPageData
|
) : MediaViewerPageData
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,11 @@ open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState>
|
||||||
MediaViewerPageData.Loading(Timeline.PaginationDirection.BACKWARDS)
|
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.architecture.AsyncData
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo
|
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.async.AsyncLoading
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
|
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
|
||||||
|
|
@ -123,6 +124,14 @@ fun MediaViewerView(
|
||||||
beyondViewportPageCount = 1,
|
beyondViewportPageCount = 1,
|
||||||
) { page ->
|
) { page ->
|
||||||
when (val dataForPage = state.listData[page]) {
|
when (val dataForPage = state.listData[page]) {
|
||||||
|
is MediaViewerPageData.Failure -> {
|
||||||
|
MediaViewerErrorPage(
|
||||||
|
throwable = dataForPage.throwable,
|
||||||
|
onDismiss = {
|
||||||
|
onBackClick()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
is MediaViewerPageData.Loading -> {
|
is MediaViewerPageData.Loading -> {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
state.eventSink(MediaViewerEvents.LoadMore(dataForPage.direction))
|
state.eventSink(MediaViewerEvents.LoadMore(dataForPage.direction))
|
||||||
|
|
@ -200,11 +209,13 @@ fun MediaViewerView(
|
||||||
else -> {
|
else -> {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
Text(
|
if (currentData is MediaViewerPageData.Loading) {
|
||||||
text = stringResource(id = CommonStrings.common_loading_more),
|
Text(
|
||||||
style = ElementTheme.typography.fontBodyMdMedium,
|
text = stringResource(id = CommonStrings.common_loading_more),
|
||||||
color = ElementTheme.colors.textPrimary,
|
style = ElementTheme.typography.fontBodyMdMedium,
|
||||||
)
|
color = ElementTheme.colors.textPrimary,
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = Color.Transparent.copy(0.6f),
|
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
|
@Composable
|
||||||
private fun DismissFlickEffects(
|
private fun DismissFlickEffects(
|
||||||
flickState: FlickToDismissState,
|
flickState: FlickToDismissState,
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,7 @@ class MediaViewerDataSourceTest {
|
||||||
galleryDataSource.emitGroupedMediaItems(AsyncData.Loading())
|
galleryDataSource.emitGroupedMediaItems(AsyncData.Loading())
|
||||||
assertThat(awaitItem().first()).isInstanceOf(MediaViewerPageData.Loading::class.java)
|
assertThat(awaitItem().first()).isInstanceOf(MediaViewerPageData.Loading::class.java)
|
||||||
galleryDataSource.emitGroupedMediaItems(AsyncData.Failure(AN_EXCEPTION))
|
galleryDataSource.emitGroupedMediaItems(AsyncData.Failure(AN_EXCEPTION))
|
||||||
// TODO Add an error screen in the ui
|
assertThat(awaitItem().first()).isEqualTo(MediaViewerPageData.Failure(AN_EXCEPTION))
|
||||||
assertThat(awaitItem().first()).isInstanceOf(MediaViewerPageData.Loading::class.java)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue