From 4052007de8d2fc9d1dd2d1e4c674d3cfa86f69d2 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sat, 9 Nov 2024 00:04:23 +0800 Subject: [PATCH] Implement retry on click for thumbnails --- .../hippo/ehviewer/ui/main/EhAsyncThumb.kt | 63 ++++++++++++++++++- .../hippo/ehviewer/ui/main/GalleryDetail.kt | 18 +++--- .../com/hippo/ehviewer/ui/main/GalleryInfo.kt | 13 ++-- .../hippo/ehviewer/ui/main/GalleryPreview.kt | 53 ++++++++++------ 4 files changed, 108 insertions(+), 39 deletions(-) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/EhAsyncThumb.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/EhAsyncThumb.kt index 7eded398d..0301b2edc 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/EhAsyncThumb.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/EhAsyncThumb.kt @@ -1,6 +1,9 @@ package com.hippo.ehviewer.ui.main import androidx.compose.animation.SharedTransitionScope +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Card import androidx.compose.material3.ShapeDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.NonRestartableComposable @@ -12,8 +15,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import coil3.Image +import coil3.Image as CoilImage import coil3.compose.AsyncImage +import coil3.compose.AsyncImagePainter +import coil3.compose.ConstraintsSizeResolver +import coil3.compose.rememberAsyncImagePainter +import coil3.compose.rememberConstraintsSizeResolver import coil3.request.ImageRequest import com.hippo.ehviewer.client.data.GalleryInfo import com.hippo.ehviewer.ktbuilder.imageRequest @@ -25,7 +32,25 @@ import com.hippo.ehviewer.ui.tools.shouldCrop @NonRestartableComposable fun requestOf(model: GalleryInfo): ImageRequest { val context = LocalContext.current - return remember(model) { context.imageRequest(model) } + return remember(model) { context.imageRequest(model) }.withSizeResolver() +} + +@Composable +@NonRestartableComposable +fun ImageRequest.withSizeResolver() = if (defined.sizeResolver != null) { + this +} else { + val sizeResolver = rememberConstraintsSizeResolver() + remember(this, sizeResolver) { newBuilder().size(sizeResolver).build() } +} + +fun Modifier.imageRequest(request: ImageRequest): Modifier { + val sizeResolver = request.sizeResolver + return if (sizeResolver is ConstraintsSizeResolver) { + then(sizeResolver) + } else { + this + } } context(SharedTransitionScope, TransitionsVisibilityScope, SETNodeGenerator) @@ -34,7 +59,7 @@ context(SharedTransitionScope, TransitionsVisibilityScope, SETNodeGenerator) fun EhAsyncThumb( model: GalleryInfo, modifier: Modifier = Modifier, - onSuccess: ((Image) -> Unit)? = null, + onSuccess: ((CoilImage) -> Unit)? = null, contentScale: ContentScale = ContentScale.Fit, ) = AsyncImage( model = requestOf(model), @@ -69,3 +94,35 @@ fun EhAsyncCropThumb( contentScale = contentScale, ) } + +@Composable +fun EhThumbCard( + key: GalleryInfo, + modifier: Modifier = Modifier, +) { + var contentScale by remember(key) { mutableStateOf(ContentScale.Fit) } + val request = requestOf(key) + val painter = rememberAsyncImagePainter( + model = request, + onSuccess = { + if (it.result.image.shouldCrop) { + contentScale = ContentScale.Crop + } + }, + ) + Card( + onClick = { + if (painter.state.value is AsyncImagePainter.State.Error) { + painter.restart() + } + }, + modifier = modifier, + ) { + Image( + painter = painter, + contentDescription = null, + modifier = Modifier.imageRequest(request).fillMaxSize().clip(ShapeDefaults.Medium), + contentScale = contentScale, + ) + } +} diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryDetail.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryDetail.kt index f7ccaf4e0..175cdcfd2 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryDetail.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryDetail.kt @@ -109,16 +109,14 @@ fun GalleryDetailHeaderCard( modifier: Modifier = Modifier, ) = ElevatedCard(modifier = modifier) { Row { - Card { - with(detailThumbGenerator) { - EhAsyncCropThumb( - key = remember(info.gid) { info }, - modifier = Modifier.size( - dimensionResource(id = R.dimen.gallery_detail_thumb_width), - dimensionResource(id = R.dimen.gallery_detail_thumb_height), - ), - ) - } + with(detailThumbGenerator) { + EhThumbCard( + key = remember(info.gid) { info }, + modifier = Modifier.size( + dimensionResource(id = R.dimen.gallery_detail_thumb_width), + dimensionResource(id = R.dimen.gallery_detail_thumb_height), + ), + ) } Spacer(modifier = Modifier.weight(0.5F)) Column( diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryInfo.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryInfo.kt index 0ff149e7c..40b4a1cbd 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryInfo.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryInfo.kt @@ -18,7 +18,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.Favorite import androidx.compose.material3.Badge -import androidx.compose.material3.Card import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProvideTextStyle @@ -108,13 +107,11 @@ fun GalleryInfoListItem( interactionSource = interactionSource, ) { Row { - Card { - with(listThumbGenerator) { - EhAsyncCropThumb( - key = info, - modifier = Modifier.aspectRatio(DEFAULT_RATIO).fillMaxSize(), - ) - } + with(listThumbGenerator) { + EhThumbCard( + key = info, + modifier = Modifier.aspectRatio(DEFAULT_RATIO), + ) } ConstraintLayout( modifier = Modifier.padding(start = 8.dp, top = 2.dp, end = 4.dp, bottom = 4.dp).fillMaxSize(), diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryPreview.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryPreview.kt index 9e30aee9e..9dad63a16 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryPreview.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryPreview.kt @@ -1,5 +1,6 @@ package com.hippo.ehviewer.ui.main +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.aspectRatio @@ -20,8 +21,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import coil3.BitmapImage -import coil3.compose.AsyncImage import coil3.compose.AsyncImagePainter.State +import coil3.compose.rememberAsyncImagePainter import coil3.request.ImageRequest import com.hippo.ehviewer.client.data.GalleryPreview import com.hippo.ehviewer.client.data.V2GalleryPreview @@ -33,19 +34,19 @@ import com.hippo.ehviewer.ui.tools.shouldCrop @NonRestartableComposable fun requestOf(model: GalleryPreview): ImageRequest { val context = LocalContext.current - return remember(model) { context.imageRequest(model) } + return remember(model) { context.imageRequest(model) }.withSizeResolver() } @Composable -fun EhAsyncPreview( +fun EhPreviewCard( model: GalleryPreview, + onClick: () -> Unit, modifier: Modifier = Modifier, ) { var contentScale by remember(model) { mutableStateOf(ContentScale.Fit) } - AsyncImage( - model = requestOf(model), - contentDescription = null, - modifier = modifier, + val request = requestOf(model) + val painter = rememberAsyncImagePainter( + model = request, transform = { if (it is State.Success && model is V2GalleryPreview) { with(model) { @@ -74,8 +75,23 @@ fun EhAsyncPreview( } } }, - contentScale = contentScale, ) + CrystalCard( + onClick = onClick, + onLongClick = { + if (painter.state.value is State.Error) { + painter.restart() + } + }, + modifier = modifier, + ) { + Image( + painter = painter, + contentDescription = null, + modifier = Modifier.imageRequest(request).fillMaxSize(), + contentScale = contentScale, + ) + } } @Composable @@ -86,16 +102,17 @@ fun EhPreviewItem( onClick: () -> Unit, ) = Column(horizontalAlignment = Alignment.CenterHorizontally) { Box(contentAlignment = Alignment.Center) { - CrystalCard( - onClick = onClick, - modifier = Modifier.aspectRatio(DEFAULT_RATIO), - ) { - if (galleryPreview != null) { - EhAsyncPreview( - model = galleryPreview, - modifier = Modifier.fillMaxSize(), - ) - } + if (galleryPreview != null) { + EhPreviewCard( + model = galleryPreview, + onClick = onClick, + modifier = Modifier.aspectRatio(DEFAULT_RATIO), + ) + } else { + CrystalCard( + onClick = onClick, + modifier = Modifier.aspectRatio(DEFAULT_RATIO), + ) {} } } Text(text = "${position + 1}")