diff --git a/app/src/main/java/com/fluxa/app/ui/catalog/TvHomeShelfComponents.kt b/app/src/main/java/com/fluxa/app/ui/catalog/TvHomeShelfComponents.kt index dbb22d5..1547d00 100644 --- a/app/src/main/java/com/fluxa/app/ui/catalog/TvHomeShelfComponents.kt +++ b/app/src/main/java/com/fluxa/app/ui/catalog/TvHomeShelfComponents.kt @@ -6,6 +6,11 @@ import com.fluxa.app.common.AppStrings import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.snap import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn @@ -23,6 +28,7 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width @@ -386,6 +392,43 @@ private fun ShelfExpandedDescription(expandedMeta: Meta?, expandedOffsetX: Dp, r } } +@Composable +internal fun TvHomeShelfSkeleton(titleStartPadding: Dp) { + val transition = rememberInfiniteTransition(label = "shelf-skeleton") + val alpha by transition.animateFloat( + initialValue = 0.05f, + targetValue = 0.13f, + animationSpec = infiniteRepeatable( + animation = tween(FluxaDimensions.AnimDuration.ambientColor, easing = LinearEasing), + repeatMode = RepeatMode.Reverse + ), + label = "shelf-skeleton-alpha" + ) + Column(modifier = Modifier.padding(bottom = 28.dp)) { + Box( + modifier = Modifier + .padding(start = titleStartPadding, bottom = 12.dp) + .width(140.dp) + .height(18.dp) + .clip(androidx.compose.foundation.shape.RoundedCornerShape(5.dp)) + .background(Color.White.copy(alpha = alpha)) + ) + LazyRow( + horizontalArrangement = Arrangement.spacedBy(12.dp), + contentPadding = PaddingValues(horizontal = titleStartPadding) + ) { + items(5) { + Box( + modifier = Modifier + .size(200.dp, 120.dp) + .clip(androidx.compose.foundation.shape.RoundedCornerShape(10.dp)) + .background(Color.White.copy(alpha = alpha)) + ) + } + } + } +} + internal fun Meta.remainingWatchLabel(lang: String? = null): String? { val total = duration ?: return null val watched = timeOffset ?: return null diff --git a/app/src/mobile/java/com/fluxa/app/ui/catalog/HomeScreen.kt b/app/src/mobile/java/com/fluxa/app/ui/catalog/HomeScreen.kt index ce5dea5..4a0cce1 100644 --- a/app/src/mobile/java/com/fluxa/app/ui/catalog/HomeScreen.kt +++ b/app/src/mobile/java/com/fluxa/app/ui/catalog/HomeScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.lazy.LazyListPrefetchStrategy import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.Immutable @@ -95,6 +96,11 @@ fun HomeScreen( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(bottom = 120.dp) ) { + if (catalogState.isLoading && homeRowSpecs.isEmpty()) { + items(4, key = { "skeleton-$it" }, contentType = { "skeleton" }) { + MobileHomeShelfSkeleton(horizontalPadding) + } + } if (catalogState.showHeroSection) { item(key = "home-hero", contentType = "hero") { MobileHomeHeroItem( diff --git a/app/src/mobile/java/com/fluxa/app/ui/catalog/MobileHomeSections.kt b/app/src/mobile/java/com/fluxa/app/ui/catalog/MobileHomeSections.kt index 3b6a9f0..cb0704f 100644 --- a/app/src/mobile/java/com/fluxa/app/ui/catalog/MobileHomeSections.kt +++ b/app/src/mobile/java/com/fluxa/app/ui/catalog/MobileHomeSections.kt @@ -4,9 +4,17 @@ package com.fluxa.app.ui.catalog import com.fluxa.app.common.AppStrings +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -14,9 +22,12 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.runtime.getValue import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable @@ -469,3 +480,40 @@ private fun HomeRowHeader( } } } + +@Composable +internal fun MobileHomeShelfSkeleton(horizontalPadding: androidx.compose.ui.unit.Dp) { + val transition = rememberInfiniteTransition(label = "shelf-skeleton") + val alpha by transition.animateFloat( + initialValue = 0.05f, + targetValue = 0.13f, + animationSpec = infiniteRepeatable( + animation = tween(FluxaDimensions.AnimDuration.ambientColor, easing = LinearEasing), + repeatMode = RepeatMode.Reverse + ), + label = "shelf-skeleton-alpha" + ) + Column(modifier = Modifier.padding(bottom = 24.dp)) { + Box( + modifier = Modifier + .padding(start = horizontalPadding, bottom = 10.dp) + .width(120.dp) + .height(16.dp) + .clip(RoundedCornerShape(5.dp)) + .background(Color.White.copy(alpha = alpha)) + ) + LazyRow( + horizontalArrangement = Arrangement.spacedBy(10.dp), + contentPadding = PaddingValues(horizontal = horizontalPadding) + ) { + items(5) { + Box( + modifier = Modifier + .size(130.dp, 195.dp) + .clip(RoundedCornerShape(8.dp)) + .background(Color.White.copy(alpha = alpha)) + ) + } + } + } +} diff --git a/app/src/tv/java/com/fluxa/app/ui/catalog/HomeScreen.kt b/app/src/tv/java/com/fluxa/app/ui/catalog/HomeScreen.kt index 4245e35..8d0e407 100644 --- a/app/src/tv/java/com/fluxa/app/ui/catalog/HomeScreen.kt +++ b/app/src/tv/java/com/fluxa/app/ui/catalog/HomeScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.LazyListPrefetchStrategy import androidx.compose.foundation.lazy.rememberLazyListState @@ -228,6 +229,11 @@ fun HomeScreen( } } + if (catalogState.isLoading && rowSpecs.isEmpty()) { + items(4, key = { "skeleton-$it" }, contentType = { "skeleton" }) { + TvHomeShelfSkeleton(titleStartPadding = railGutter) + } + } itemsIndexed( rowSpecs, key = { _, row -> row.categoryId },