diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsRepository.kt index 94b078067..a2605c4db 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsRepository.kt @@ -17,9 +17,25 @@ object MetaDetailsRepository { private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) private val _uiState = MutableStateFlow(MetaDetailsUiState()) val uiState: StateFlow = _uiState.asStateFlow() + private var activeRequestKey: String? = null fun load(type: String, id: String) { log.d { "load() called — type=$type id=$id" } + val requestKey = "$type:$id" + val currentState = _uiState.value + + if (currentState.meta?.type == type && currentState.meta.id == id && !currentState.isLoading) { + log.d { "Skipping reload for cached meta — type=$type id=$id" } + activeRequestKey = requestKey + return + } + + if (currentState.isLoading && activeRequestKey == requestKey) { + log.d { "Request already in flight — type=$type id=$id" } + return + } + + activeRequestKey = requestKey _uiState.value = MetaDetailsUiState(isLoading = true) scope.launch { @@ -38,6 +54,7 @@ object MetaDetailsRepository { _uiState.value = MetaDetailsUiState( errorMessage = "No addon provides meta for this content.", ) + activeRequestKey = null return@launch } @@ -45,6 +62,7 @@ object MetaDetailsRepository { val result = tryFetchMeta(manifest, type, id) if (result != null) { _uiState.value = MetaDetailsUiState(meta = result) + activeRequestKey = requestKey return@launch } } @@ -52,10 +70,12 @@ object MetaDetailsRepository { _uiState.value = MetaDetailsUiState( errorMessage = "Could not load details from any addon.", ) + activeRequestKey = null } } fun clear() { + activeRequestKey = null _uiState.value = MetaDetailsUiState() } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt index 1e186d702..888dc1bc9 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt @@ -1,5 +1,7 @@ package com.nuvio.app.features.details +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -29,8 +31,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.nuvio.app.core.ui.nuvioPlatformExtraBottomPadding @@ -49,18 +53,31 @@ fun MetaDetailsScreen( modifier: Modifier = Modifier, ) { val uiState by MetaDetailsRepository.uiState.collectAsStateWithLifecycle() + val screenAlpha = remember(type, id) { Animatable(0f) } + val requestedMeta = uiState.meta?.takeIf { it.type == type && it.id == id } + val needsFreshLoad = requestedMeta == null && !uiState.isLoading - LaunchedEffect(type, id) { + LaunchedEffect(type, id, needsFreshLoad) { + if (!needsFreshLoad) { + screenAlpha.snapTo(1f) + return@LaunchedEffect + } + screenAlpha.snapTo(0f) + screenAlpha.animateTo( + targetValue = 1f, + animationSpec = tween(durationMillis = 220), + ) MetaDetailsRepository.load(type, id) } Box( modifier = modifier .fillMaxSize() + .alpha(screenAlpha.value) .background(MaterialTheme.colorScheme.background), ) { when { - uiState.isLoading -> { + uiState.isLoading || (uiState.meta != null && requestedMeta == null) -> { CircularProgressIndicator( modifier = Modifier.align(Alignment.Center), color = MaterialTheme.colorScheme.primary, @@ -88,8 +105,8 @@ fun MetaDetailsScreen( } } - uiState.meta != null -> { - val meta = uiState.meta!! + requestedMeta != null -> { + val meta = requestedMeta val scrollState = rememberScrollState() Column( modifier = Modifier