Workaround to not invalidate shared element modifier chain (#1944)

* Try to fix SET

* Drop check

---------

Co-authored-by: Dude so hot <djohn@fbi.gov>
This commit is contained in:
renovate[bot]andDude so hot authored and GitHub committed 2024-11-18 21:58:45 +08:00
1 parent 900da46090
commit 7dd87c1ff6
3 files changed
+50 -55

No files matched your search

@@ -14,10 +14,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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 as CoilImage
import coil3.compose.AsyncImage
import coil3.compose.AsyncImagePainter
import coil3.compose.ConstraintsSizeResolver
@@ -27,8 +25,8 @@ import coil3.request.ImageRequest
import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.ehviewer.ktbuilder.imageRequest
import com.hippo.ehviewer.ui.tools.SETNodeGenerator
import com.hippo.ehviewer.ui.tools.SharedElementBox
import com.hippo.ehviewer.ui.tools.TransitionsVisibilityScope
import com.hippo.ehviewer.ui.tools.sharedBounds
import com.hippo.ehviewer.ui.tools.shouldCrop
import com.hippo.ehviewer.ui.tools.thenIf
@@ -57,35 +55,17 @@ fun Modifier.imageRequest(request: ImageRequest): Modifier {
}
}
context(SharedTransitionScope, TransitionsVisibilityScope, SETNodeGenerator)
@Composable
@NonRestartableComposable
fun EhAsyncThumb(
model: GalleryInfo,
modifier: Modifier = Modifier,
onSuccess: ((CoilImage) -> Unit)? = null,
contentScale: ContentScale = ContentScale.Fit,
) = AsyncImage(
model = requestOf(model),
contentDescription = null,
modifier = modifier.sharedBounds(key = "${model.gid}").clip(ShapeDefaults.Medium),
onSuccess = onSuccess?.let { callback ->
{ callback(it.result.image) }
},
contentScale = contentScale,
)
context(SharedTransitionScope, TransitionsVisibilityScope, SETNodeGenerator)
@Composable
fun EhAsyncCropThumb(
key: GalleryInfo,
modifier: Modifier = Modifier,
) {
) = SharedElementBox(key = "${key.gid}", shape = ShapeDefaults.Medium) {
var contentScale by remember(key) { mutableStateOf(ContentScale.Fit) }
AsyncImage(
model = requestOf(key),
contentDescription = null,
modifier = modifier.sharedBounds(key = "${key.gid}").clip(ShapeDefaults.Medium),
modifier = modifier,
onSuccess = {
if (it.result.image.shouldCrop) {
contentScale = ContentScale.Crop
@@ -100,28 +80,26 @@ context(SharedTransitionScope, TransitionsVisibilityScope, SETNodeGenerator)
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
}
},
)
val state by painter.state.collectAsState()
Card(modifier = modifier) {
) = Card(modifier = modifier) {
SharedElementBox(key = "${key.gid}", shape = ShapeDefaults.Medium) {
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
}
},
)
val state by painter.state.collectAsState()
Image(
painter = painter,
contentDescription = null,
modifier = Modifier.thenIf(state !is AsyncImagePainter.State.Success) {
// Keep applying this when state is `Loading` to avoid cutting off the ripple
clickable {
if (state is AsyncImagePainter.State.Error) painter.restart()
}
}.imageRequest(request).fillMaxSize().sharedBounds(key = "${key.gid}").clip(ShapeDefaults.Medium),
clickable { if (state is AsyncImagePainter.State.Error) painter.restart() }
}.imageRequest(request).fillMaxSize(),
contentScale = contentScale,
)
}
@@ -43,6 +43,7 @@ import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import androidx.constraintlayout.compose.Dimension
import arrow.core.Tuple7
import coil3.compose.AsyncImage
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.client.data.GalleryInfo
@@ -51,6 +52,7 @@ import com.hippo.ehviewer.download.DownloadManager
import com.hippo.ehviewer.ui.tools.CrystalCard
import com.hippo.ehviewer.ui.tools.ElevatedCard
import com.hippo.ehviewer.ui.tools.GalleryListCardRating
import com.hippo.ehviewer.ui.tools.SharedElementBox
import com.hippo.ehviewer.ui.tools.TransitionsVisibilityScope
import com.hippo.ehviewer.ui.tools.listThumbGenerator
import com.hippo.ehviewer.util.FavouriteStatusRouter
@@ -219,22 +221,25 @@ fun GalleryInfoGridItem(
interactionSource = interactionSource,
) {
Box {
var ratio by remember(info) {
val ratio = if (info.thumbHeight != 0) {
(info.thumbWidth.toFloat() / info.thumbHeight).coerceIn(MIN_RATIO, MAX_RATIO)
} else {
DEFAULT_RATIO
}
mutableFloatStateOf(ratio)
}
with(listThumbGenerator) {
EhAsyncThumb(
model = info,
modifier = Modifier.aspectRatio(ratio),
onSuccess = {
ratio = (it.width.toFloat() / it.height).coerceIn(MIN_RATIO, MAX_RATIO)
},
)
SharedElementBox(key = "${info.gid}", shape = ShapeDefaults.Medium) {
var ratio by remember(info) {
val ratio = if (info.thumbHeight != 0) {
(info.thumbWidth.toFloat() / info.thumbHeight).coerceIn(MIN_RATIO, MAX_RATIO)
} else {
DEFAULT_RATIO
}
mutableFloatStateOf(ratio)
}
AsyncImage(
model = requestOf(info),
contentDescription = null,
modifier = Modifier.aspectRatio(ratio),
onSuccess = {
ratio = (it.result.image.width.toFloat() / it.result.image.height).coerceIn(MIN_RATIO, MAX_RATIO)
},
)
}
}
val categoryColor = EhUtils.getCategoryColor(info.category)
Badge(
@@ -6,11 +6,16 @@ import androidx.compose.animation.ExitTransition
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Shape
import java.util.concurrent.atomic.AtomicInteger
val NoopTransitionsVisibilityScope = TransitionsVisibilityScope(emptySet())
@@ -45,6 +50,13 @@ fun Modifier.sharedBounds(
}
}
context(SharedTransitionScope, TransitionsVisibilityScope, SETNodeGenerator)
@Composable
inline fun SharedElementBox(key: String, shape: Shape, crossinline content: @Composable BoxScope.() -> Unit) {
val modifier = Modifier.sharedBounds(key = key).clip(shape)
CompositionLocalProvider { Box(modifier = modifier, content = content) }
}
data class SETNode(
val contentKey: String,
val uniqueID: String,