mirror of
https://github.com/FluxaMedia/fluxa.git
synced 2026-08-17 04:35:46 +00:00
Add skeleton loading rows to home screens on both platforms
While the home catalog is loading and no rows are available yet, both the mobile and TV home screens now show 4 animated skeleton shelf placeholders instead of a blank screen. The pulse animation matches the explore screen skeleton.
This commit is contained in:
parent
462de853ed
commit
88d7e22ec2
4 changed files with 103 additions and 0 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
Loading…
Reference in a new issue