diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 24ee21fc..d45039e4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ composeGradle = "1.2.0-alpha01-dev753" composeAndroid = "1.2.0" voyager = "1.0.0-beta16" accompanist = "0.25.0" -kamel = "0.4.0" +kamel = "0.4.1" materialDialogs = "0.8.0" # Android diff --git a/ui-core/src/commonMain/kotlin/ca/gosyer/jui/uicore/image/KamelImage.kt b/ui-core/src/commonMain/kotlin/ca/gosyer/jui/uicore/image/KamelImage.kt index ae0ed34c..cfcba837 100644 --- a/ui-core/src/commonMain/kotlin/ca/gosyer/jui/uicore/image/KamelImage.kt +++ b/ui-core/src/commonMain/kotlin/ca/gosyer/jui/uicore/image/KamelImage.kt @@ -6,10 +6,13 @@ package ca.gosyer.jui.uicore.image +import androidx.compose.animation.Crossfade import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.tween +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.material.Icon @@ -17,6 +20,10 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.BrokenImage import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -32,6 +39,12 @@ import io.kamel.image.KamelImage as BaseKamelImage private val log = logging() +private enum class KamelImageState { + Loading, + Success, + Failure, +} + @Composable fun KamelImage( resource: Resource, @@ -42,10 +55,10 @@ fun KamelImage( contentScale: ContentScale = ContentScale.Fit, alpha: Float = DefaultAlpha, colorFilter: ColorFilter? = null, - onLoading: @Composable (Float) -> Unit = { - LoadingScreen(progress = it, modifier = modifier then Modifier.fillMaxSize()) + onLoading: (@Composable BoxScope.(Float) -> Unit)? = { + LoadingScreen(progress = it.coerceIn(0.0F, 1.0F), modifier = modifier then Modifier.fillMaxSize()) }, - onFailure: @Composable (Throwable) -> Unit = { + onFailure: (@Composable BoxScope.(Throwable) -> Unit)? = { LaunchedEffect(it) { log.warn(it) { "Error loading image" } } @@ -62,20 +75,74 @@ fun KamelImage( ) } }, - crossfade: Boolean = true, - animationSpec: FiniteAnimationSpec = tween() + contentAlignment: Alignment = Alignment.Center, + animationSpec: FiniteAnimationSpec? = tween(), ) { - BaseKamelImage( - resource, - contentDescription, - modifier, - alignment, - contentScale, - alpha, - colorFilter, - onLoading, - onFailure, - crossfade, - animationSpec - ) + if (animationSpec != null) { + val progress = remember { mutableStateOf(-1F) } + val image = remember { mutableStateOf(null) } + val error = remember { mutableStateOf(null) } + val state by derivedStateOf { + when (resource) { + is Resource.Failure -> { + progress.value = -1F + error.value = resource.exception + KamelImageState.Failure + } + is Resource.Loading -> { + progress.value = resource.progress + KamelImageState.Loading + } + is Resource.Success -> { + progress.value = 1.0F + image.value = resource.value + KamelImageState.Success + } + } + } + Crossfade(state, animationSpec = animationSpec, modifier = modifier) { + Box(Modifier.fillMaxSize(), contentAlignment) { + when (it) { + KamelImageState.Loading -> if (onLoading != null) { + onLoading(progress.value) + } + KamelImageState.Success -> Box { + val value = image.value + if (value != null) { + Image( + value, + contentDescription, + modifier, + alignment, + contentScale, + alpha, + colorFilter + ) + } else if (onLoading != null) { + onLoading(1.0F) + } + } + KamelImageState.Failure -> { + if (onFailure != null) { + onFailure(error.value ?: return@Crossfade) + } + } + } + } + } + } else { + BaseKamelImage( + resource, + contentDescription, + modifier, + alignment, + contentScale, + alpha, + colorFilter, + onLoading, + onFailure, + contentAlignment, + null + ) + } }