diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeScreen.kt index 87879839..e549850b 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeScreen.kt @@ -70,6 +70,7 @@ import org.jetbrains.compose.resources.stringResource @Composable fun HomeScreen( modifier: Modifier = Modifier, + animateCollectionGifs: Boolean = true, onCatalogClick: ((HomeCatalogSection) -> Unit)? = null, onPosterClick: ((MetaPreview) -> Unit)? = null, onPosterLongClick: ((MetaPreview) -> Unit)? = null, @@ -560,6 +561,7 @@ fun HomeScreen( collection = collection, modifier = Modifier.padding(bottom = 12.dp), sectionPadding = homeSectionPadding, + animateGifs = animateCollectionGifs, onFolderClick = onFolderClick, ) } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeCollectionRowSection.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeCollectionRowSection.kt index 2c3121aa..37d3fced 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeCollectionRowSection.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeCollectionRowSection.kt @@ -37,6 +37,7 @@ fun HomeCollectionRowSection( collection: Collection, modifier: Modifier = Modifier, sectionPadding: Dp? = null, + animateGifs: Boolean = true, onFolderClick: ((collectionId: String, folderId: String) -> Unit)? = null, ) { if (collection.folders.isEmpty()) return @@ -46,6 +47,7 @@ fun HomeCollectionRowSection( collection = collection, modifier = modifier.fillMaxWidth(), sectionPadding = sectionPadding, + animateGifs = animateGifs, onFolderClick = onFolderClick, ) } else { @@ -54,6 +56,7 @@ fun HomeCollectionRowSection( collection = collection, modifier = Modifier.fillMaxWidth(), sectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value), + animateGifs = animateGifs, onFolderClick = onFolderClick, ) } @@ -65,6 +68,7 @@ private fun HomeCollectionRowSectionContent( collection: Collection, modifier: Modifier, sectionPadding: Dp, + animateGifs: Boolean, onFolderClick: ((collectionId: String, folderId: String) -> Unit)?, ) { NuvioShelfSection( @@ -77,6 +81,7 @@ private fun HomeCollectionRowSectionContent( ) { folder -> CollectionFolderCard( folder = folder, + animateGifs = animateGifs, onClick = onFolderClick?.let { { it(collection.id, folder.id) } }, ) } @@ -86,6 +91,7 @@ private fun HomeCollectionRowSectionContent( private fun CollectionFolderCard( folder: CollectionFolder, modifier: Modifier = Modifier, + animateGifs: Boolean = true, onClick: (() -> Unit)? = null, ) { val posterCardStyle = rememberPosterCardStyleUiState() @@ -138,7 +144,7 @@ private fun CollectionFolderCard( contentDescription = folder.title, modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Crop, - animateIfPossible = isAnimatedCollectionFolderImage(folder, imageUrl), + animateIfPossible = animateGifs && isAnimatedCollectionFolderImage(folder, imageUrl), ) } !folder.coverEmoji.isNullOrBlank() -> { diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/home/components/CollectionCardRemoteImage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/home/components/CollectionCardRemoteImage.ios.kt index 11d9fe42..7f1e5c69 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/home/components/CollectionCardRemoteImage.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/home/components/CollectionCardRemoteImage.ios.kt @@ -2,6 +2,7 @@ package com.nuvio.app.features.home.components import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -51,6 +52,16 @@ private data class ExpandedGifFrames( val tickCentiseconds: Int, ) +private class GifImageViewHolder { + var imageView: UIImageView? = null + + fun clear() { + imageView?.stopAnimating() + imageView?.image = null + imageView = null + } +} + @OptIn(ExperimentalForeignApi::class) @Composable internal actual fun CollectionCardRemoteImage( @@ -76,6 +87,13 @@ internal actual fun CollectionCardRemoteImage( gifImage = loadGifImage(imageUrl) } + val imageViewHolder = remember(imageUrl) { GifImageViewHolder() } + DisposableEffect(imageUrl) { + onDispose { + imageViewHolder.clear() + } + } + UIKitView( modifier = modifier, factory = { @@ -83,19 +101,31 @@ internal actual fun CollectionCardRemoteImage( contentMode = UIViewContentMode.UIViewContentModeScaleAspectFill clipsToBounds = true userInteractionEnabled = false - image = gifImage tag = imageUrl.hashCode().toLong() + imageViewHolder.imageView = this + updateGifImage(gifImage) } }, update = { imageView -> + imageViewHolder.imageView = imageView if (imageView.tag != imageUrl.hashCode().toLong()) { imageView.tag = imageUrl.hashCode().toLong() } - imageView.image = gifImage + imageView.updateGifImage(gifImage) }, ) } +private fun UIImageView.updateGifImage(image: UIImage?) { + if (this.image != image) { + stopAnimating() + this.image = image + } + if (image != null) { + startAnimating() + } +} + private fun cachedGifImage(imageUrl: String): UIImage? { val image = gifImageCache[imageUrl] ?: return null gifImageCacheOrder.remove(imageUrl) @@ -311,4 +341,4 @@ private fun ByteArray.readUnsignedShort(startIndex: Int): Int { return this[startIndex].unsignedInt() or (this[startIndex + 1].unsignedInt() shl 8) } -private fun Byte.unsignedInt(): Int = toInt() and 0xFF \ No newline at end of file +private fun Byte.unsignedInt(): Int = toInt() and 0xFF