mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-07 02:30:25 +00:00
feat(home): Refactor HomeCatalogRowSection and HomeContinueWatchingSection for dynamic padding and layout adjustments
This commit is contained in:
parent
fc5a078f73
commit
ae95c9d004
4 changed files with 134 additions and 70 deletions
|
|
@ -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) } },
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue