mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-07-29 23:59:32 +00:00
refactor: improve HomeScreen and related components for better layout handling
This commit is contained in:
parent
7a76d9d38c
commit
4baa0504dd
5 changed files with 339 additions and 241 deletions
|
|
@ -1,7 +1,6 @@
|
|||
package com.nuvio.app
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
|
|
@ -12,7 +11,6 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
|
@ -43,6 +41,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.draw.alpha
|
||||
|
|
@ -1427,53 +1426,54 @@ private fun AppTabHost(
|
|||
onFolderClick: ((collectionId: String, folderId: String) -> Unit)? = null,
|
||||
onInitialHomeContentRendered: () -> Unit = {},
|
||||
) {
|
||||
val tabStateHolder = rememberSaveableStateHolder()
|
||||
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
keepAliveTab(
|
||||
selected = selectedTab == AppScreenTab.Home,
|
||||
) {
|
||||
HomeScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onCatalogClick = onCatalogClick,
|
||||
onPosterClick = onPosterClick,
|
||||
onPosterLongClick = onPosterLongClick,
|
||||
onContinueWatchingClick = onContinueWatchingClick,
|
||||
onContinueWatchingLongPress = onContinueWatchingLongPress,
|
||||
onFolderClick = onFolderClick,
|
||||
onFirstCatalogRendered = onInitialHomeContentRendered,
|
||||
)
|
||||
}
|
||||
keepAliveTab(
|
||||
selected = selectedTab == AppScreenTab.Search,
|
||||
) {
|
||||
SearchScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onPosterClick = onPosterClick,
|
||||
onPosterLongClick = onPosterLongClick,
|
||||
)
|
||||
}
|
||||
keepAliveTab(
|
||||
selected = selectedTab == AppScreenTab.Library,
|
||||
) {
|
||||
LibraryScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onPosterClick = onLibraryPosterClick,
|
||||
onSectionViewAllClick = onLibrarySectionViewAllClick,
|
||||
)
|
||||
}
|
||||
keepAliveTab(
|
||||
selected = selectedTab == AppScreenTab.Settings,
|
||||
) {
|
||||
SettingsScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onSwitchProfile = onSwitchProfile,
|
||||
onHomescreenClick = onHomescreenSettingsClick,
|
||||
onMetaScreenClick = onMetaScreenSettingsClick,
|
||||
onContinueWatchingClick = onContinueWatchingSettingsClick,
|
||||
onAddonsClick = onAddonsSettingsClick,
|
||||
onPluginsClick = onPluginsSettingsClick,
|
||||
onAccountClick = onAccountSettingsClick,
|
||||
onCollectionsClick = onCollectionsSettingsClick,
|
||||
)
|
||||
tabStateHolder.SaveableStateProvider(selectedTab.name) {
|
||||
when (selectedTab) {
|
||||
AppScreenTab.Home -> {
|
||||
HomeScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onCatalogClick = onCatalogClick,
|
||||
onPosterClick = onPosterClick,
|
||||
onPosterLongClick = onPosterLongClick,
|
||||
onContinueWatchingClick = onContinueWatchingClick,
|
||||
onContinueWatchingLongPress = onContinueWatchingLongPress,
|
||||
onFolderClick = onFolderClick,
|
||||
onFirstCatalogRendered = onInitialHomeContentRendered,
|
||||
)
|
||||
}
|
||||
|
||||
AppScreenTab.Search -> {
|
||||
SearchScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onPosterClick = onPosterClick,
|
||||
onPosterLongClick = onPosterLongClick,
|
||||
)
|
||||
}
|
||||
|
||||
AppScreenTab.Library -> {
|
||||
LibraryScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onPosterClick = onLibraryPosterClick,
|
||||
onSectionViewAllClick = onLibrarySectionViewAllClick,
|
||||
)
|
||||
}
|
||||
|
||||
AppScreenTab.Settings -> {
|
||||
SettingsScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onSwitchProfile = onSwitchProfile,
|
||||
onHomescreenClick = onHomescreenSettingsClick,
|
||||
onMetaScreenClick = onMetaScreenSettingsClick,
|
||||
onContinueWatchingClick = onContinueWatchingSettingsClick,
|
||||
onAddonsClick = onAddonsSettingsClick,
|
||||
onPluginsClick = onPluginsSettingsClick,
|
||||
onAccountClick = onAccountSettingsClick,
|
||||
onCollectionsClick = onCollectionsSettingsClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1650,24 +1650,3 @@ private fun AppLaunchOverlay(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BoxScope.keepAliveTab(
|
||||
selected: Boolean,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val contentAlpha by animateFloatAsState(
|
||||
targetValue = if (selected) 1f else 0f,
|
||||
animationSpec = tween(durationMillis = 220),
|
||||
label = "tab_content_alpha",
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.alpha(contentAlpha)
|
||||
.zIndex(if (selected) 1f else 0f),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.nuvio.app.features.home
|
||||
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
|
@ -45,6 +47,8 @@ import kotlinx.coroutines.async
|
|||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import com.nuvio.app.features.home.components.homeSectionHorizontalPaddingForWidth
|
||||
import com.nuvio.app.features.home.components.rememberContinueWatchingLayout
|
||||
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
|
|
@ -286,127 +290,148 @@ fun HomeScreen(
|
|||
onFirstCatalogRendered?.invoke()
|
||||
}
|
||||
|
||||
NuvioScreen(
|
||||
modifier = modifier,
|
||||
horizontalPadding = 0.dp,
|
||||
topPadding = if (showHeroSlot) 0.dp else null,
|
||||
) {
|
||||
if (showHeroSlot) {
|
||||
item {
|
||||
when {
|
||||
showHeroSkeleton -> HomeSkeletonHero(
|
||||
modifier = Modifier,
|
||||
)
|
||||
val visibleCollections = remember(collections) {
|
||||
collections.filter { it.folders.isNotEmpty() }
|
||||
}
|
||||
val collectionsMap = remember(visibleCollections) {
|
||||
visibleCollections.associateBy { "collection_${it.id}" }
|
||||
}
|
||||
val sectionsMap = remember(homeUiState.sections) {
|
||||
homeUiState.sections.associateBy(HomeCatalogSection::key)
|
||||
}
|
||||
val enabledHomeItems = remember(homeSettingsUiState.items) {
|
||||
homeSettingsUiState.items.filter { it.enabled }
|
||||
}
|
||||
|
||||
homeUiState.heroItems.isNotEmpty() -> HomeHeroSection(
|
||||
items = homeUiState.heroItems,
|
||||
modifier = Modifier,
|
||||
onItemClick = onPosterClick,
|
||||
)
|
||||
BoxWithConstraints(modifier = modifier.fillMaxSize()) {
|
||||
val homeSectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value)
|
||||
val continueWatchingLayout = rememberContinueWatchingLayout(maxWidth.value)
|
||||
|
||||
else -> HomeHeroReservedSpace(modifier = Modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
addonsUiState.addons.none { it.manifest != null } -> {
|
||||
if (continueWatchingPreferences.isVisible && continueWatchingItems.isNotEmpty()) {
|
||||
item {
|
||||
HomeContinueWatchingSection(
|
||||
items = continueWatchingItems,
|
||||
style = continueWatchingPreferences.style,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
onItemClick = onContinueWatchingClick,
|
||||
onItemLongPress = onContinueWatchingLongPress,
|
||||
)
|
||||
}
|
||||
}
|
||||
NuvioScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalPadding = 0.dp,
|
||||
topPadding = if (showHeroSlot) 0.dp else null,
|
||||
) {
|
||||
if (showHeroSlot) {
|
||||
item {
|
||||
HomeEmptyStateCard(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
title = "No active addons",
|
||||
message = "Install and validate at least one addon before loading catalog rows on Home.",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
homeUiState.isLoading && homeUiState.sections.isEmpty() -> {
|
||||
if (continueWatchingPreferences.isVisible && continueWatchingItems.isNotEmpty()) {
|
||||
item {
|
||||
HomeContinueWatchingSection(
|
||||
items = continueWatchingItems,
|
||||
style = continueWatchingPreferences.style,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
onItemClick = onContinueWatchingClick,
|
||||
onItemLongPress = onContinueWatchingLongPress,
|
||||
when {
|
||||
showHeroSkeleton -> HomeSkeletonHero(
|
||||
modifier = Modifier,
|
||||
)
|
||||
|
||||
homeUiState.heroItems.isNotEmpty() -> HomeHeroSection(
|
||||
items = homeUiState.heroItems,
|
||||
modifier = Modifier,
|
||||
onItemClick = onPosterClick,
|
||||
)
|
||||
|
||||
else -> HomeHeroReservedSpace(modifier = Modifier)
|
||||
}
|
||||
}
|
||||
items(3) {
|
||||
HomeSkeletonRow(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
homeUiState.sections.isEmpty() && homeUiState.heroItems.isEmpty() &&
|
||||
(!continueWatchingPreferences.isVisible || continueWatchingItems.isEmpty()) -> {
|
||||
item {
|
||||
HomeEmptyStateCard(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
title = "No home rows available",
|
||||
message = homeUiState.errorMessage
|
||||
?: "Installed addons do not currently expose board-compatible catalogs without required extras.",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (continueWatchingPreferences.isVisible && continueWatchingItems.isNotEmpty()) {
|
||||
item {
|
||||
HomeContinueWatchingSection(
|
||||
items = continueWatchingItems,
|
||||
style = continueWatchingPreferences.style,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
onItemClick = onContinueWatchingClick,
|
||||
onItemLongPress = onContinueWatchingLongPress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val collectionsMap = collections.filter { it.folders.isNotEmpty() }
|
||||
.associateBy { "collection_${it.id}" }
|
||||
val sectionsMap = homeUiState.sections.associateBy { it.key }
|
||||
val orderedItems = homeSettingsUiState.items.filter { it.enabled }
|
||||
|
||||
orderedItems.forEach { settingsItem ->
|
||||
if (settingsItem.isCollection) {
|
||||
val collection = collectionsMap[settingsItem.key]
|
||||
if (collection != null) {
|
||||
item(key = settingsItem.key) {
|
||||
HomeCollectionRowSection(
|
||||
collection = collection,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
onFolderClick = onFolderClick,
|
||||
)
|
||||
}
|
||||
when {
|
||||
addonsUiState.addons.none { it.manifest != null } -> {
|
||||
if (continueWatchingPreferences.isVisible && continueWatchingItems.isNotEmpty()) {
|
||||
item {
|
||||
HomeContinueWatchingSection(
|
||||
items = continueWatchingItems,
|
||||
style = continueWatchingPreferences.style,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
sectionPadding = homeSectionPadding,
|
||||
layout = continueWatchingLayout,
|
||||
onItemClick = onContinueWatchingClick,
|
||||
onItemLongPress = onContinueWatchingLongPress,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val section = sectionsMap[settingsItem.key]
|
||||
if (section != null && section.items.isNotEmpty()) {
|
||||
item(key = settingsItem.key) {
|
||||
HomeCatalogRowSection(
|
||||
section = section,
|
||||
entries = section.items.take(HOME_CATALOG_PREVIEW_LIMIT),
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
onViewAllClick = if (section.canOpenCatalog(HOME_CATALOG_PREVIEW_LIMIT)) {
|
||||
onCatalogClick?.let { { it(section) } }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
watchedKeys = watchedUiState.watchedKeys,
|
||||
onPosterClick = onPosterClick,
|
||||
onPosterLongClick = onPosterLongClick,
|
||||
)
|
||||
}
|
||||
item {
|
||||
HomeEmptyStateCard(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
title = "No active addons",
|
||||
message = "Install and validate at least one addon before loading catalog rows on Home.",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
homeUiState.isLoading && homeUiState.sections.isEmpty() -> {
|
||||
if (continueWatchingPreferences.isVisible && continueWatchingItems.isNotEmpty()) {
|
||||
item {
|
||||
HomeContinueWatchingSection(
|
||||
items = continueWatchingItems,
|
||||
style = continueWatchingPreferences.style,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
sectionPadding = homeSectionPadding,
|
||||
layout = continueWatchingLayout,
|
||||
onItemClick = onContinueWatchingClick,
|
||||
onItemLongPress = onContinueWatchingLongPress,
|
||||
)
|
||||
}
|
||||
}
|
||||
items(3) {
|
||||
HomeSkeletonRow(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
homeUiState.sections.isEmpty() && homeUiState.heroItems.isEmpty() &&
|
||||
(!continueWatchingPreferences.isVisible || continueWatchingItems.isEmpty()) -> {
|
||||
item {
|
||||
HomeEmptyStateCard(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
title = "No home rows available",
|
||||
message = homeUiState.errorMessage
|
||||
?: "Installed addons do not currently expose board-compatible catalogs without required extras.",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (continueWatchingPreferences.isVisible && continueWatchingItems.isNotEmpty()) {
|
||||
item {
|
||||
HomeContinueWatchingSection(
|
||||
items = continueWatchingItems,
|
||||
style = continueWatchingPreferences.style,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
sectionPadding = homeSectionPadding,
|
||||
layout = continueWatchingLayout,
|
||||
onItemClick = onContinueWatchingClick,
|
||||
onItemLongPress = onContinueWatchingLongPress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enabledHomeItems.forEach { settingsItem ->
|
||||
if (settingsItem.isCollection) {
|
||||
val collection = collectionsMap[settingsItem.key]
|
||||
if (collection != null) {
|
||||
item(key = settingsItem.key) {
|
||||
HomeCollectionRowSection(
|
||||
collection = collection,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
sectionPadding = homeSectionPadding,
|
||||
onFolderClick = onFolderClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val section = sectionsMap[settingsItem.key]
|
||||
if (section != null && section.items.isNotEmpty()) {
|
||||
item(key = settingsItem.key) {
|
||||
HomeCatalogRowSection(
|
||||
section = section,
|
||||
entries = section.items.take(HOME_CATALOG_PREVIEW_LIMIT),
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
sectionPadding = homeSectionPadding,
|
||||
onViewAllClick = if (section.canOpenCatalog(HOME_CATALOG_PREVIEW_LIMIT)) {
|
||||
onCatalogClick?.let { { it(section) } }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
watchedKeys = watchedUiState.watchedKeys,
|
||||
onPosterClick = onPosterClick,
|
||||
onPosterLongClick = onPosterLongClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.nuvio.app.core.ui.NuvioShelfSection
|
||||
import com.nuvio.app.core.ui.NuvioViewAllPillSize
|
||||
import com.nuvio.app.features.home.HomeCatalogSection
|
||||
|
|
@ -18,31 +19,67 @@ fun HomeCatalogRowSection(
|
|||
modifier: Modifier = Modifier,
|
||||
entries: List<MetaPreview> = section.items,
|
||||
watchedKeys: Set<String> = emptySet(),
|
||||
sectionPadding: Dp? = null,
|
||||
onViewAllClick: (() -> Unit)? = null,
|
||||
onPosterClick: ((MetaPreview) -> Unit)? = null,
|
||||
onPosterLongClick: ((MetaPreview) -> Unit)? = null,
|
||||
) {
|
||||
BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
|
||||
val sectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value)
|
||||
NuvioShelfSection(
|
||||
title = section.title,
|
||||
if (sectionPadding != null) {
|
||||
HomeCatalogRowSectionContent(
|
||||
section = section,
|
||||
entries = entries,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
headerHorizontalPadding = sectionPadding,
|
||||
rowContentPadding = PaddingValues(horizontal = sectionPadding),
|
||||
watchedKeys = watchedKeys,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
sectionPadding = sectionPadding,
|
||||
onViewAllClick = onViewAllClick,
|
||||
viewAllPillSize = NuvioViewAllPillSize.Compact,
|
||||
key = { item -> item.stableKey() },
|
||||
) { item ->
|
||||
HomePosterCard(
|
||||
item = item,
|
||||
isWatched = WatchingState.isPosterWatched(
|
||||
watchedKeys = watchedKeys,
|
||||
item = item,
|
||||
),
|
||||
onClick = onPosterClick?.let { { it(item) } },
|
||||
onLongClick = onPosterLongClick?.let { { it(item) } },
|
||||
onPosterClick = onPosterClick,
|
||||
onPosterLongClick = onPosterLongClick,
|
||||
)
|
||||
} else {
|
||||
BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
|
||||
HomeCatalogRowSectionContent(
|
||||
section = section,
|
||||
entries = entries,
|
||||
watchedKeys = watchedKeys,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
sectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value),
|
||||
onViewAllClick = onViewAllClick,
|
||||
onPosterClick = onPosterClick,
|
||||
onPosterLongClick = onPosterLongClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HomeCatalogRowSectionContent(
|
||||
section: HomeCatalogSection,
|
||||
entries: List<MetaPreview>,
|
||||
watchedKeys: Set<String>,
|
||||
modifier: Modifier,
|
||||
sectionPadding: Dp,
|
||||
onViewAllClick: (() -> Unit)?,
|
||||
onPosterClick: ((MetaPreview) -> Unit)?,
|
||||
onPosterLongClick: ((MetaPreview) -> Unit)?,
|
||||
) {
|
||||
NuvioShelfSection(
|
||||
title = section.title,
|
||||
entries = entries,
|
||||
modifier = modifier,
|
||||
headerHorizontalPadding = sectionPadding,
|
||||
rowContentPadding = PaddingValues(horizontal = sectionPadding),
|
||||
onViewAllClick = onViewAllClick,
|
||||
viewAllPillSize = NuvioViewAllPillSize.Compact,
|
||||
key = { item -> item.stableKey() },
|
||||
) { item ->
|
||||
HomePosterCard(
|
||||
item = item,
|
||||
isWatched = WatchingState.isPosterWatched(
|
||||
watchedKeys = watchedKeys,
|
||||
item = item,
|
||||
),
|
||||
onClick = onPosterClick?.let { { it(item) } },
|
||||
onLongClick = onPosterLongClick?.let { { it(item) } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,28 +34,52 @@ import com.nuvio.app.features.home.PosterShape
|
|||
fun HomeCollectionRowSection(
|
||||
collection: Collection,
|
||||
modifier: Modifier = Modifier,
|
||||
sectionPadding: Dp? = null,
|
||||
onFolderClick: ((collectionId: String, folderId: String) -> Unit)? = null,
|
||||
) {
|
||||
if (collection.folders.isEmpty()) return
|
||||
|
||||
BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
|
||||
val sectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value)
|
||||
NuvioShelfSection(
|
||||
title = collection.title,
|
||||
entries = collection.folders,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
headerHorizontalPadding = sectionPadding,
|
||||
rowContentPadding = PaddingValues(horizontal = sectionPadding),
|
||||
key = { folder -> "collection_${collection.id}_folder_${folder.id}" },
|
||||
) { folder ->
|
||||
CollectionFolderCard(
|
||||
folder = folder,
|
||||
onClick = onFolderClick?.let { { it(collection.id, folder.id) } },
|
||||
if (sectionPadding != null) {
|
||||
HomeCollectionRowSectionContent(
|
||||
collection = collection,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
sectionPadding = sectionPadding,
|
||||
onFolderClick = onFolderClick,
|
||||
)
|
||||
} else {
|
||||
BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
|
||||
HomeCollectionRowSectionContent(
|
||||
collection = collection,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
sectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value),
|
||||
onFolderClick = onFolderClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HomeCollectionRowSectionContent(
|
||||
collection: Collection,
|
||||
modifier: Modifier,
|
||||
sectionPadding: Dp,
|
||||
onFolderClick: ((collectionId: String, folderId: String) -> Unit)?,
|
||||
) {
|
||||
NuvioShelfSection(
|
||||
title = collection.title,
|
||||
entries = collection.folders,
|
||||
modifier = modifier,
|
||||
headerHorizontalPadding = sectionPadding,
|
||||
rowContentPadding = PaddingValues(horizontal = sectionPadding),
|
||||
key = { folder -> "collection_${collection.id}_folder_${folder.id}" },
|
||||
) { folder ->
|
||||
CollectionFolderCard(
|
||||
folder = folder,
|
||||
onClick = onFolderClick?.let { { it(collection.id, folder.id) } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CollectionFolderCard(
|
||||
folder: CollectionFolder,
|
||||
|
|
|
|||
|
|
@ -45,41 +45,74 @@ import com.nuvio.app.features.watchprogress.ContinueWatchingSectionStyle
|
|||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun HomeContinueWatchingSection(
|
||||
internal fun HomeContinueWatchingSection(
|
||||
items: List<ContinueWatchingItem>,
|
||||
style: ContinueWatchingSectionStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
sectionPadding: Dp? = null,
|
||||
layout: ContinueWatchingLayout? = null,
|
||||
onItemClick: ((ContinueWatchingItem) -> Unit)? = null,
|
||||
onItemLongPress: ((ContinueWatchingItem) -> Unit)? = null,
|
||||
) {
|
||||
if (items.isEmpty()) return
|
||||
|
||||
BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
|
||||
val layout = rememberContinueWatchingLayout(maxWidth.value)
|
||||
val sectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value)
|
||||
NuvioShelfSection(
|
||||
title = "Continue Watching",
|
||||
entries = items,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
headerHorizontalPadding = sectionPadding,
|
||||
rowContentPadding = PaddingValues(horizontal = sectionPadding),
|
||||
itemSpacing = layout.itemGap,
|
||||
key = { item -> item.videoId },
|
||||
) { item ->
|
||||
when (style) {
|
||||
ContinueWatchingSectionStyle.Wide -> ContinueWatchingWideCard(
|
||||
item = item,
|
||||
layout = layout,
|
||||
onClick = onItemClick?.let { { it(item) } },
|
||||
onLongClick = onItemLongPress?.let { { it(item) } },
|
||||
)
|
||||
ContinueWatchingSectionStyle.Poster -> ContinueWatchingPosterCard(
|
||||
item = item,
|
||||
layout = layout,
|
||||
onClick = onItemClick?.let { { it(item) } },
|
||||
onLongClick = onItemLongPress?.let { { it(item) } },
|
||||
)
|
||||
}
|
||||
if (sectionPadding != null && layout != null) {
|
||||
HomeContinueWatchingSectionContent(
|
||||
items = items,
|
||||
style = style,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
sectionPadding = sectionPadding,
|
||||
layout = layout,
|
||||
onItemClick = onItemClick,
|
||||
onItemLongPress = onItemLongPress,
|
||||
)
|
||||
} else {
|
||||
BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
|
||||
HomeContinueWatchingSectionContent(
|
||||
items = items,
|
||||
style = style,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
sectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value),
|
||||
layout = rememberContinueWatchingLayout(maxWidth.value),
|
||||
onItemClick = onItemClick,
|
||||
onItemLongPress = onItemLongPress,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HomeContinueWatchingSectionContent(
|
||||
items: List<ContinueWatchingItem>,
|
||||
style: ContinueWatchingSectionStyle,
|
||||
modifier: Modifier,
|
||||
sectionPadding: Dp,
|
||||
layout: ContinueWatchingLayout,
|
||||
onItemClick: ((ContinueWatchingItem) -> Unit)?,
|
||||
onItemLongPress: ((ContinueWatchingItem) -> Unit)?,
|
||||
) {
|
||||
NuvioShelfSection(
|
||||
title = "Continue Watching",
|
||||
entries = items,
|
||||
modifier = modifier,
|
||||
headerHorizontalPadding = sectionPadding,
|
||||
rowContentPadding = PaddingValues(horizontal = sectionPadding),
|
||||
itemSpacing = layout.itemGap,
|
||||
key = { item -> item.videoId },
|
||||
) { item ->
|
||||
when (style) {
|
||||
ContinueWatchingSectionStyle.Wide -> ContinueWatchingWideCard(
|
||||
item = item,
|
||||
layout = layout,
|
||||
onClick = onItemClick?.let { { it(item) } },
|
||||
onLongClick = onItemLongPress?.let { { it(item) } },
|
||||
)
|
||||
ContinueWatchingSectionStyle.Poster -> ContinueWatchingPosterCard(
|
||||
item = item,
|
||||
layout = layout,
|
||||
onClick = onItemClick?.let { { it(item) } },
|
||||
onLongClick = onItemLongPress?.let { { it(item) } },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -460,7 +493,7 @@ private fun UpNextBadge(
|
|||
}
|
||||
}
|
||||
|
||||
private data class ContinueWatchingLayout(
|
||||
internal data class ContinueWatchingLayout(
|
||||
val itemGap: Dp,
|
||||
val wideCardWidth: Dp,
|
||||
val wideCardHeight: Dp,
|
||||
|
|
@ -479,7 +512,7 @@ private data class ContinueWatchingLayout(
|
|||
val posterBadgeTextSize: androidx.compose.ui.unit.TextUnit,
|
||||
)
|
||||
|
||||
private fun rememberContinueWatchingLayout(maxWidthDp: Float): ContinueWatchingLayout =
|
||||
internal fun rememberContinueWatchingLayout(maxWidthDp: Float): ContinueWatchingLayout =
|
||||
when {
|
||||
maxWidthDp >= 1440f -> ContinueWatchingLayout(
|
||||
itemGap = 20.dp,
|
||||
|
|
|
|||
Loading…
Reference in a new issue