feat(home): Refactor HomeCatalogRowSection and HomeContinueWatchingSection for dynamic padding and layout adjustments

This commit is contained in:
tapframe 2026-04-01 19:23:00 +05:30
parent fc5a078f73
commit ae95c9d004
4 changed files with 134 additions and 70 deletions

View file

@ -1,8 +1,10 @@
package com.nuvio.app.features.home.components package com.nuvio.app.features.home.components
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.nuvio.app.core.ui.NuvioShelfSection import com.nuvio.app.core.ui.NuvioShelfSection
import com.nuvio.app.core.ui.NuvioViewAllPillSize import com.nuvio.app.core.ui.NuvioViewAllPillSize
import com.nuvio.app.features.home.HomeCatalogSection import com.nuvio.app.features.home.HomeCatalogSection
@ -20,24 +22,27 @@ fun HomeCatalogRowSection(
onPosterClick: ((MetaPreview) -> Unit)? = null, onPosterClick: ((MetaPreview) -> Unit)? = null,
onPosterLongClick: ((MetaPreview) -> Unit)? = null, onPosterLongClick: ((MetaPreview) -> Unit)? = null,
) { ) {
NuvioShelfSection( BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
title = section.title, val sectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value)
entries = entries, NuvioShelfSection(
modifier = modifier, title = section.title,
headerHorizontalPadding = 16.dp, entries = entries,
rowContentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 16.dp), modifier = Modifier.fillMaxWidth(),
onViewAllClick = onViewAllClick, headerHorizontalPadding = sectionPadding,
viewAllPillSize = NuvioViewAllPillSize.Compact, rowContentPadding = PaddingValues(horizontal = sectionPadding),
key = { item -> item.stableKey() }, onViewAllClick = onViewAllClick,
) { item -> viewAllPillSize = NuvioViewAllPillSize.Compact,
HomePosterCard( key = { item -> item.stableKey() },
item = item, ) { item ->
isWatched = WatchingState.isPosterWatched( HomePosterCard(
watchedKeys = watchedKeys,
item = item, item = item,
), isWatched = WatchingState.isPosterWatched(
onClick = onPosterClick?.let { { it(item) } }, watchedKeys = watchedKeys,
onLongClick = onPosterLongClick?.let { { it(item) } }, item = item,
) ),
onClick = onPosterClick?.let { { it(item) } },
onLongClick = onPosterLongClick?.let { { it(item) } },
)
}
} }
} }

View file

@ -56,12 +56,13 @@ fun HomeContinueWatchingSection(
BoxWithConstraints(modifier = modifier.fillMaxWidth()) { BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
val layout = rememberContinueWatchingLayout(maxWidth.value) val layout = rememberContinueWatchingLayout(maxWidth.value)
val sectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value)
NuvioShelfSection( NuvioShelfSection(
title = "Continue Watching", title = "Continue Watching",
entries = items, entries = items,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
headerHorizontalPadding = layout.sectionPadding, headerHorizontalPadding = sectionPadding,
rowContentPadding = PaddingValues(horizontal = layout.sectionPadding), rowContentPadding = PaddingValues(horizontal = sectionPadding),
itemSpacing = layout.itemGap, itemSpacing = layout.itemGap,
key = { item -> item.videoId }, key = { item -> item.videoId },
) { item -> ) { item ->
@ -460,7 +461,6 @@ private fun UpNextBadge(
} }
private data class ContinueWatchingLayout( private data class ContinueWatchingLayout(
val sectionPadding: Dp,
val itemGap: Dp, val itemGap: Dp,
val wideCardWidth: Dp, val wideCardWidth: Dp,
val wideCardHeight: Dp, val wideCardHeight: Dp,
@ -482,7 +482,6 @@ private data class ContinueWatchingLayout(
private fun rememberContinueWatchingLayout(maxWidthDp: Float): ContinueWatchingLayout = private fun rememberContinueWatchingLayout(maxWidthDp: Float): ContinueWatchingLayout =
when { when {
maxWidthDp >= 1440f -> ContinueWatchingLayout( maxWidthDp >= 1440f -> ContinueWatchingLayout(
sectionPadding = 32.dp,
itemGap = 20.dp, itemGap = 20.dp,
wideCardWidth = 400.dp, wideCardWidth = 400.dp,
wideCardHeight = 160.dp, wideCardHeight = 160.dp,
@ -501,7 +500,6 @@ private fun rememberContinueWatchingLayout(maxWidthDp: Float): ContinueWatchingL
posterBadgeTextSize = 12.sp, posterBadgeTextSize = 12.sp,
) )
maxWidthDp >= 1024f -> ContinueWatchingLayout( maxWidthDp >= 1024f -> ContinueWatchingLayout(
sectionPadding = 28.dp,
itemGap = 18.dp, itemGap = 18.dp,
wideCardWidth = 350.dp, wideCardWidth = 350.dp,
wideCardHeight = 140.dp, wideCardHeight = 140.dp,
@ -520,7 +518,6 @@ private fun rememberContinueWatchingLayout(maxWidthDp: Float): ContinueWatchingL
posterBadgeTextSize = 10.sp, posterBadgeTextSize = 10.sp,
) )
maxWidthDp >= 768f -> ContinueWatchingLayout( maxWidthDp >= 768f -> ContinueWatchingLayout(
sectionPadding = 24.dp,
itemGap = 16.dp, itemGap = 16.dp,
wideCardWidth = 320.dp, wideCardWidth = 320.dp,
wideCardHeight = 130.dp, wideCardHeight = 130.dp,
@ -539,7 +536,6 @@ private fun rememberContinueWatchingLayout(maxWidthDp: Float): ContinueWatchingL
posterBadgeTextSize = 10.sp, posterBadgeTextSize = 10.sp,
) )
else -> ContinueWatchingLayout( else -> ContinueWatchingLayout(
sectionPadding = 16.dp,
itemGap = 16.dp, itemGap = 16.dp,
wideCardWidth = 280.dp, wideCardWidth = 280.dp,
wideCardHeight = 120.dp, wideCardHeight = 120.dp,

View file

@ -0,0 +1,12 @@
package com.nuvio.app.features.home.components
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
internal fun homeSectionHorizontalPaddingForWidth(maxWidthDp: Float): Dp =
when {
maxWidthDp >= 1440f -> 32.dp
maxWidthDp >= 1024f -> 28.dp
maxWidthDp >= 768f -> 24.dp
else -> 16.dp
}

View file

@ -10,6 +10,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@ -22,6 +23,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@ -89,7 +91,7 @@ fun ProfileSelectionScreen(
val statusBarTop = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() val statusBarTop = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
Box( BoxWithConstraints(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.background( .background(
@ -103,14 +105,23 @@ fun ProfileSelectionScreen(
) )
.padding(top = statusBarTop), .padding(top = statusBarTop),
) { ) {
val isTabletLayout = maxWidth >= 768.dp
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .then(
if (isTabletLayout) {
Modifier
} else {
Modifier.verticalScroll(rememberScrollState())
},
)
.padding(horizontal = 24.dp), .padding(horizontal = 24.dp),
verticalArrangement = if (isTabletLayout) Arrangement.Center else Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Spacer(modifier = Modifier.height(60.dp)) Spacer(modifier = Modifier.height(if (isTabletLayout) 0.dp else 60.dp))
Text( Text(
text = "Who's watching?", text = "Who's watching?",
@ -126,52 +137,92 @@ fun ProfileSelectionScreen(
}, },
) )
Spacer(modifier = Modifier.height(48.dp)) Spacer(modifier = Modifier.height(if (isTabletLayout) 28.dp else 48.dp))
val profiles = profileState.profiles val profiles = profileState.profiles
val items = profiles.size + if (profiles.size < 4) 1 else 0 val items = profiles.size + if (profiles.size < 4) 1 else 0
Column( if (isTabletLayout) {
verticalArrangement = Arrangement.spacedBy(20.dp), Box(
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center,
) { ) {
var index = 0
while (index < items) {
Row( Row(
horizontalArrangement = Arrangement.spacedBy(20.dp, Alignment.CenterHorizontally), modifier = Modifier
modifier = Modifier.fillMaxWidth(), .horizontalScroll(rememberScrollState())
.padding(horizontal = 4.dp),
horizontalArrangement = Arrangement.spacedBy(20.dp),
) { ) {
for (col in 0..1) { for (currentIndex in 0 until items) {
if (index < items) { if (currentIndex < profiles.size) {
val currentIndex = index val profile = profiles[currentIndex]
if (currentIndex < profiles.size) { ProfileAvatarCard(
val profile = profiles[currentIndex] profile = profile,
ProfileAvatarCard( isEditMode = isEditMode,
profile = profile, animDelay = currentIndex * 80,
isEditMode = isEditMode, onClick = {
animDelay = currentIndex * 80, if (isEditMode) {
onClick = { onEditProfile(profile)
if (isEditMode) { } else if (profile.pinEnabled) {
onEditProfile(profile) pinDialogProfile = profile
} else if (profile.pinEnabled) { } else {
pinDialogProfile = profile ProfileRepository.selectProfile(profile.profileIndex)
} else { onProfileSelected(profile)
ProfileRepository.selectProfile(profile.profileIndex) }
onProfileSelected(profile) },
} )
},
)
} else {
AddProfileCard(
animDelay = currentIndex * 80,
onClick = onAddProfile,
)
}
index++
} else { } else {
if (profiles.isNotEmpty()) { AddProfileCard(
Spacer(modifier = Modifier.width(150.dp)) animDelay = currentIndex * 80,
onClick = onAddProfile,
)
}
}
}
}
} else {
Column(
verticalArrangement = Arrangement.spacedBy(20.dp),
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth(),
) {
var index = 0
while (index < items) {
Row(
horizontalArrangement = Arrangement.spacedBy(20.dp, Alignment.CenterHorizontally),
modifier = Modifier.fillMaxWidth(),
) {
for (col in 0..1) {
if (index < items) {
val currentIndex = index
if (currentIndex < profiles.size) {
val profile = profiles[currentIndex]
ProfileAvatarCard(
profile = profile,
isEditMode = isEditMode,
animDelay = currentIndex * 80,
onClick = {
if (isEditMode) {
onEditProfile(profile)
} else if (profile.pinEnabled) {
pinDialogProfile = profile
} else {
ProfileRepository.selectProfile(profile.profileIndex)
onProfileSelected(profile)
}
},
)
} else {
AddProfileCard(
animDelay = currentIndex * 80,
onClick = onAddProfile,
)
}
index++
} else {
if (profiles.isNotEmpty()) {
Spacer(modifier = Modifier.width(150.dp))
}
} }
} }
} }
@ -179,7 +230,7 @@ fun ProfileSelectionScreen(
} }
} }
Spacer(modifier = Modifier.height(48.dp)) Spacer(modifier = Modifier.height(if (isTabletLayout) 28.dp else 48.dp))
Box( Box(
modifier = Modifier modifier = Modifier
@ -207,7 +258,7 @@ fun ProfileSelectionScreen(
) )
} }
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(if (isTabletLayout) 0.dp else 32.dp))
} }
} }