mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-07-31 08:39:24 +00:00
feat: layout change for desktop home hero
This commit is contained in:
parent
84cc669bd5
commit
e1fcc8750d
3 changed files with 393 additions and 130 deletions
|
|
@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.nuvio.app.isDesktop
|
||||
import com.nuvio.app.core.network.NetworkCondition
|
||||
import com.nuvio.app.core.network.NetworkStatusRepository
|
||||
import com.nuvio.app.core.ui.LocalNuvioBottomNavigationOverlayPadding
|
||||
|
|
@ -646,6 +647,7 @@ fun HomeScreen(
|
|||
modifier = Modifier,
|
||||
viewportHeight = maxHeight,
|
||||
mobileBelowSectionHeightHint = mobileHeroBelowSectionHeightHint,
|
||||
sectionPadding = if (isDesktop) homeSectionPadding else null,
|
||||
)
|
||||
|
||||
homeUiState.heroItems.isNotEmpty() -> HomeHeroSection(
|
||||
|
|
@ -653,6 +655,7 @@ fun HomeScreen(
|
|||
modifier = Modifier,
|
||||
viewportHeight = maxHeight,
|
||||
mobileBelowSectionHeightHint = mobileHeroBelowSectionHeightHint,
|
||||
sectionPadding = if (isDesktop) homeSectionPadding else null,
|
||||
listState = homeListState,
|
||||
onItemClick = onPosterClick,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
|
|
@ -47,6 +48,7 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.nuvio.app.isDesktop
|
||||
import com.nuvio.app.core.ui.NuvioDesktopImageScaling
|
||||
import com.nuvio.app.core.ui.NuvioAsyncImage as AsyncImage
|
||||
import com.nuvio.app.core.format.formatReleaseDateForDisplay
|
||||
|
|
@ -87,6 +89,7 @@ fun HomeHeroSection(
|
|||
modifier: Modifier = Modifier,
|
||||
viewportHeight: Dp? = null,
|
||||
mobileBelowSectionHeightHint: Dp? = null,
|
||||
sectionPadding: Dp? = null,
|
||||
listState: LazyListState? = null,
|
||||
onItemClick: ((MetaPreview) -> Unit)? = null,
|
||||
) {
|
||||
|
|
@ -109,6 +112,7 @@ fun HomeHeroSection(
|
|||
maxWidthDp = maxWidth.value,
|
||||
viewportHeightDp = viewportHeight?.value,
|
||||
mobileBelowSectionHeightHintDp = mobileBelowSectionHeightHint?.value,
|
||||
preferDesktopLayout = isDesktop,
|
||||
)
|
||||
val heroWidthPx = with(LocalDensity.current) { maxWidth.toPx() }
|
||||
val heroHeightPx = with(LocalDensity.current) { layout.heroHeight.toPx() }
|
||||
|
|
@ -164,137 +168,32 @@ fun HomeHeroSection(
|
|||
Box(modifier = Modifier.fillMaxSize())
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
visiblePages.forEach { layer ->
|
||||
AsyncImage(
|
||||
model = items[layer.page].banner ?: items[layer.page].poster,
|
||||
contentDescription = items[layer.page].name,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.graphicsLayer {
|
||||
alpha = layer.visibility
|
||||
translationX = -layer.offset * heroWidthPx * HERO_BACKGROUND_PARALLAX
|
||||
translationY = heroScrollTranslationY
|
||||
scaleX = HERO_BACKGROUND_SCALE * heroScrollScale
|
||||
scaleY = HERO_BACKGROUND_SCALE * heroScrollScale
|
||||
},
|
||||
alignment = if (layout.isTablet) Alignment.TopCenter else Alignment.Center,
|
||||
contentScale = ContentScale.Crop,
|
||||
desktopImageScaling = NuvioDesktopImageScaling.Disabled,
|
||||
)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.02f),
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.12f),
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.34f),
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.78f),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isDesktop) {
|
||||
DesktopHomeHeroFrame(
|
||||
items = items,
|
||||
visiblePages = visiblePages,
|
||||
layout = layout,
|
||||
heroWidthPx = heroWidthPx,
|
||||
heroScrollScale = heroScrollScale,
|
||||
heroScrollTranslationY = heroScrollTranslationY,
|
||||
contentHorizontalPadding = sectionPadding ?: layout.contentHorizontalPadding,
|
||||
pagerState = pagerState,
|
||||
coroutineScope = coroutineScope,
|
||||
onItemClick = onItemClick,
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(layout.bottomFadeHeight)
|
||||
.align(Alignment.BottomCenter)
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0f),
|
||||
MaterialTheme.colorScheme.background,
|
||||
),
|
||||
),
|
||||
),
|
||||
} else {
|
||||
DefaultHomeHeroFrame(
|
||||
items = items,
|
||||
visiblePages = visiblePages,
|
||||
currentItem = currentItem,
|
||||
layout = layout,
|
||||
heroWidthPx = heroWidthPx,
|
||||
heroScrollScale = heroScrollScale,
|
||||
heroScrollTranslationY = heroScrollTranslationY,
|
||||
pagerState = pagerState,
|
||||
coroutineScope = coroutineScope,
|
||||
onItemClick = onItemClick,
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = layout.contentHorizontalPadding,
|
||||
vertical = layout.contentVerticalPadding,
|
||||
),
|
||||
horizontalAlignment = if (layout.isTablet) Alignment.Start else Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(layout.contentWidthFraction)
|
||||
.widthIn(max = layout.contentMaxWidth),
|
||||
contentAlignment = if (layout.isTablet) Alignment.CenterStart else Alignment.Center,
|
||||
) {
|
||||
visiblePages.forEach { layer ->
|
||||
Box(
|
||||
modifier = Modifier.graphicsLayer {
|
||||
alpha = layer.visibility
|
||||
translationX = -layer.offset * heroWidthPx * HERO_CONTENT_PARALLAX
|
||||
},
|
||||
) {
|
||||
HeroContentBlock(
|
||||
item = items[layer.page],
|
||||
layout = layout,
|
||||
onItemClick = onItemClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!layout.isTablet) {
|
||||
Spacer(modifier = Modifier.height(14.dp))
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.clickable(enabled = onItemClick != null) {
|
||||
onItemClick?.invoke(currentItem)
|
||||
},
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
contentColor = MaterialTheme.colorScheme.background,
|
||||
shape = RoundedCornerShape(40.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.home_view_details),
|
||||
modifier = Modifier.padding(horizontal = 28.dp, vertical = 12.dp),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (items.size > 1) {
|
||||
Spacer(modifier = Modifier.height(if (layout.isTablet) 14.dp else 12.dp))
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
items.forEachIndexed { index, _ ->
|
||||
val activeFraction = heroPageVisibility(pagerState, index)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
coroutineScope.launch {
|
||||
pagerState.animateScrollToPage(index)
|
||||
}
|
||||
}
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.onBackground)
|
||||
.graphicsLayer {
|
||||
alpha = 0.35f + (0.57f * activeFraction)
|
||||
}
|
||||
.width(8.dp + (24.dp * activeFraction))
|
||||
.height(8.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -306,6 +205,283 @@ private data class HeroPageLayer(
|
|||
val offset: Float,
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun DefaultHomeHeroFrame(
|
||||
items: List<MetaPreview>,
|
||||
visiblePages: List<HeroPageLayer>,
|
||||
currentItem: MetaPreview,
|
||||
layout: HomeHeroLayout,
|
||||
heroWidthPx: Float,
|
||||
heroScrollScale: Float,
|
||||
heroScrollTranslationY: Float,
|
||||
pagerState: PagerState,
|
||||
coroutineScope: CoroutineScope,
|
||||
onItemClick: ((MetaPreview) -> Unit)?,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
visiblePages.forEach { layer ->
|
||||
AsyncImage(
|
||||
model = items[layer.page].banner ?: items[layer.page].poster,
|
||||
contentDescription = items[layer.page].name,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.graphicsLayer {
|
||||
alpha = layer.visibility
|
||||
translationX = -layer.offset * heroWidthPx * HERO_BACKGROUND_PARALLAX
|
||||
translationY = heroScrollTranslationY
|
||||
scaleX = HERO_BACKGROUND_SCALE * heroScrollScale
|
||||
scaleY = HERO_BACKGROUND_SCALE * heroScrollScale
|
||||
},
|
||||
alignment = if (layout.isTablet) Alignment.TopCenter else Alignment.Center,
|
||||
contentScale = ContentScale.Crop,
|
||||
desktopImageScaling = NuvioDesktopImageScaling.Disabled,
|
||||
)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.02f),
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.12f),
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.34f),
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.78f),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(layout.bottomFadeHeight)
|
||||
.align(Alignment.BottomCenter)
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0f),
|
||||
MaterialTheme.colorScheme.background,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = layout.contentHorizontalPadding,
|
||||
vertical = layout.contentVerticalPadding,
|
||||
),
|
||||
horizontalAlignment = if (layout.isTablet) Alignment.Start else Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(layout.contentWidthFraction)
|
||||
.widthIn(max = layout.contentMaxWidth),
|
||||
contentAlignment = if (layout.isTablet) Alignment.CenterStart else Alignment.Center,
|
||||
) {
|
||||
visiblePages.forEach { layer ->
|
||||
Box(
|
||||
modifier = Modifier.graphicsLayer {
|
||||
alpha = layer.visibility
|
||||
translationX = -layer.offset * heroWidthPx * HERO_CONTENT_PARALLAX
|
||||
},
|
||||
) {
|
||||
HeroContentBlock(
|
||||
item = items[layer.page],
|
||||
layout = layout,
|
||||
onItemClick = onItemClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!layout.isTablet) {
|
||||
Spacer(modifier = Modifier.height(14.dp))
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.clickable(enabled = onItemClick != null) {
|
||||
onItemClick?.invoke(currentItem)
|
||||
},
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
contentColor = MaterialTheme.colorScheme.background,
|
||||
shape = RoundedCornerShape(40.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.home_view_details),
|
||||
modifier = Modifier.padding(horizontal = 28.dp, vertical = 12.dp),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
HeroPageIndicatorRow(
|
||||
itemCount = items.size,
|
||||
pagerState = pagerState,
|
||||
coroutineScope = coroutineScope,
|
||||
modifier = Modifier.padding(top = if (layout.isTablet) 14.dp else 12.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DesktopHomeHeroFrame(
|
||||
items: List<MetaPreview>,
|
||||
visiblePages: List<HeroPageLayer>,
|
||||
layout: HomeHeroLayout,
|
||||
heroWidthPx: Float,
|
||||
heroScrollScale: Float,
|
||||
heroScrollTranslationY: Float,
|
||||
contentHorizontalPadding: Dp,
|
||||
pagerState: PagerState,
|
||||
coroutineScope: CoroutineScope,
|
||||
onItemClick: ((MetaPreview) -> Unit)?,
|
||||
) {
|
||||
val backgroundColor = MaterialTheme.colorScheme.background
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(backgroundColor),
|
||||
) {
|
||||
visiblePages.forEach { layer ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.fillMaxWidth(0.64f)
|
||||
.graphicsLayer {
|
||||
alpha = layer.visibility
|
||||
translationX = -layer.offset * heroWidthPx * HERO_BACKGROUND_PARALLAX
|
||||
translationY = heroScrollTranslationY
|
||||
scaleX = 1.02f * heroScrollScale
|
||||
scaleY = 1.02f * heroScrollScale
|
||||
},
|
||||
) {
|
||||
AsyncImage(
|
||||
model = items[layer.page].banner ?: items[layer.page].poster,
|
||||
contentDescription = items[layer.page].name,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
alignment = Alignment.Center,
|
||||
contentScale = ContentScale.Crop,
|
||||
desktopImageScaling = NuvioDesktopImageScaling.Disabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
Brush.horizontalGradient(
|
||||
colorStops = arrayOf(
|
||||
0f to backgroundColor,
|
||||
0.34f to backgroundColor,
|
||||
0.58f to backgroundColor.copy(alpha = 0.78f),
|
||||
0.78f to backgroundColor.copy(alpha = 0.18f),
|
||||
1f to backgroundColor.copy(alpha = 0f),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(layout.bottomFadeHeight)
|
||||
.align(Alignment.BottomCenter)
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
backgroundColor.copy(alpha = 0f),
|
||||
backgroundColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = contentHorizontalPadding, end = contentHorizontalPadding)
|
||||
.fillMaxWidth(layout.contentWidthFraction)
|
||||
.widthIn(max = layout.contentMaxWidth),
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
) {
|
||||
visiblePages.forEach { layer ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.graphicsLayer {
|
||||
alpha = layer.visibility
|
||||
translationX = -layer.offset * heroWidthPx * HERO_CONTENT_PARALLAX
|
||||
},
|
||||
) {
|
||||
DesktopHeroContentBlock(
|
||||
item = items[layer.page],
|
||||
onItemClick = onItemClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HeroPageIndicatorRow(
|
||||
itemCount = items.size,
|
||||
pagerState = pagerState,
|
||||
coroutineScope = coroutineScope,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.padding(
|
||||
start = contentHorizontalPadding,
|
||||
bottom = layout.contentVerticalPadding,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HeroPageIndicatorRow(
|
||||
itemCount: Int,
|
||||
pagerState: PagerState,
|
||||
coroutineScope: CoroutineScope,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (itemCount <= 1) return
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
repeat(itemCount) { index ->
|
||||
val activeFraction = heroPageVisibility(pagerState, index)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
coroutineScope.launch {
|
||||
pagerState.animateScrollToPage(index)
|
||||
}
|
||||
}
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.onBackground)
|
||||
.graphicsLayer {
|
||||
alpha = 0.35f + (0.57f * activeFraction)
|
||||
}
|
||||
.width(8.dp + (24.dp * activeFraction))
|
||||
.height(8.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun heroPageOffset(
|
||||
pagerState: PagerState,
|
||||
page: Int,
|
||||
|
|
@ -333,6 +509,7 @@ fun HomeHeroReservedSpace(
|
|||
maxWidthDp = maxWidth.value,
|
||||
viewportHeightDp = viewportHeight?.value,
|
||||
mobileBelowSectionHeightHintDp = mobileBelowSectionHeightHint?.value,
|
||||
preferDesktopLayout = isDesktop,
|
||||
)
|
||||
|
||||
Spacer(
|
||||
|
|
@ -410,6 +587,74 @@ private fun HeroContentBlock(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DesktopHeroContentBlock(
|
||||
item: MetaPreview,
|
||||
onItemClick: ((MetaPreview) -> Unit)?,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(enabled = onItemClick != null) {
|
||||
onItemClick?.invoke(item)
|
||||
},
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
if (item.logo != null) {
|
||||
AsyncImage(
|
||||
model = item.logo,
|
||||
contentDescription = item.name,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(0.74f)
|
||||
.aspectRatio(2.7f),
|
||||
alignment = Alignment.CenterStart,
|
||||
contentScale = ContentScale.Fit,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = item.name,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = MaterialTheme.typography.displayMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
fontWeight = FontWeight.Black,
|
||||
textAlign = TextAlign.Start,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
val genreText = desktopHeroGenreText(item)
|
||||
if (genreText.isNotBlank()) {
|
||||
Spacer(modifier = Modifier.height(14.dp))
|
||||
Text(
|
||||
text = genreText,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.76f),
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
item.description?.takeIf { it.isNotBlank() }?.let { description ->
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = description,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.82f),
|
||||
maxLines = 4,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun desktopHeroGenreText(item: MetaPreview): String =
|
||||
item.genres
|
||||
.take(3)
|
||||
.joinToString(" • ")
|
||||
.ifBlank { item.type.replaceFirstChar(Char::uppercase) }
|
||||
|
||||
@Composable
|
||||
private fun HeroMetaText(text: String) {
|
||||
Text(
|
||||
|
|
@ -426,6 +671,7 @@ internal fun homeHeroLayout(
|
|||
maxWidthDp: Float,
|
||||
viewportHeightDp: Float? = null,
|
||||
mobileBelowSectionHeightHintDp: Float? = null,
|
||||
preferDesktopLayout: Boolean = false,
|
||||
): HomeHeroLayout =
|
||||
when {
|
||||
maxWidthDp >= 1200f -> HomeHeroLayout(
|
||||
|
|
@ -458,6 +704,16 @@ internal fun homeHeroLayout(
|
|||
bottomFadeHeight = 170.dp,
|
||||
logoWidthFraction = 0.54f,
|
||||
)
|
||||
preferDesktopLayout -> HomeHeroLayout(
|
||||
isTablet = true,
|
||||
heroHeight = (maxWidthDp * 0.68f).dp.coerceIn(300.dp, 360.dp),
|
||||
contentMaxWidth = 360.dp,
|
||||
contentWidthFraction = 0.56f,
|
||||
contentHorizontalPadding = 16.dp,
|
||||
contentVerticalPadding = 18.dp,
|
||||
bottomFadeHeight = 150.dp,
|
||||
logoWidthFraction = 0.64f,
|
||||
)
|
||||
else -> HomeHeroLayout(
|
||||
isTablet = false,
|
||||
heroHeight = mobileHeroHeight(
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import androidx.compose.ui.geometry.Offset
|
|||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.nuvio.app.isDesktop
|
||||
import com.nuvio.app.core.ui.landscapePosterHeightForWidth
|
||||
import com.nuvio.app.core.ui.landscapePosterWidth
|
||||
import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
|
||||
|
|
@ -65,6 +66,7 @@ fun HomeSkeletonHero(
|
|||
modifier: Modifier = Modifier,
|
||||
viewportHeight: Dp? = null,
|
||||
mobileBelowSectionHeightHint: Dp? = null,
|
||||
sectionPadding: Dp? = null,
|
||||
) {
|
||||
val brush = rememberHomeSkeletonBrush()
|
||||
|
||||
|
|
@ -77,8 +79,10 @@ fun HomeSkeletonHero(
|
|||
maxWidthDp = maxWidth.value,
|
||||
viewportHeightDp = viewportHeight?.value,
|
||||
mobileBelowSectionHeightHintDp = mobileBelowSectionHeightHint?.value,
|
||||
preferDesktopLayout = isDesktop,
|
||||
)
|
||||
val containerWidth = maxWidth
|
||||
val contentHorizontalPadding = sectionPadding ?: layout.contentHorizontalPadding
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
|
@ -121,7 +125,7 @@ fun HomeSkeletonHero(
|
|||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = layout.contentHorizontalPadding,
|
||||
horizontal = contentHorizontalPadding,
|
||||
vertical = layout.contentVerticalPadding,
|
||||
),
|
||||
horizontalAlignment = if (layout.isTablet) Alignment.Start else Alignment.CenterHorizontally,
|
||||
|
|
|
|||
Loading…
Reference in a new issue