From c22fbc30be7f9008098a2603f720aeaacfa050cc Mon Sep 17 00:00:00 2001
From: tapframe <85391825+tapframe@users.noreply.github.com>
Date: Thu, 29 Jan 2026 13:43:12 +0530
Subject: [PATCH] homescreen fix
---
.idea/inspectionProfiles/Project_Default.xml | 50 +++++
.idea/vcs.xml | 6 +
.../tv/ui/components/CatalogRowSection.kt | 2 +-
.../tv/ui/screens/detail/MetaDetailsScreen.kt | 187 ++++++++++--------
.../nuvio/tv/ui/screens/home/HomeScreen.kt | 5 +-
.../nuvio/tv/ui/screens/home/HomeUiState.kt | 2 +-
.../nuvio/tv/ui/screens/home/HomeViewModel.kt | 22 ++-
7 files changed, 187 insertions(+), 87 deletions(-)
create mode 100644 .idea/inspectionProfiles/Project_Default.xml
create mode 100644 .idea/vcs.xml
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..f0c6ad08
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..94a25f7f
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/nuvio/tv/ui/components/CatalogRowSection.kt b/app/src/main/java/com/nuvio/tv/ui/components/CatalogRowSection.kt
index 36278a77..8a2d38b4 100644
--- a/app/src/main/java/com/nuvio/tv/ui/components/CatalogRowSection.kt
+++ b/app/src/main/java/com/nuvio/tv/ui/components/CatalogRowSection.kt
@@ -80,7 +80,7 @@ fun CatalogRowSection(
) {
items(
items = catalogRow.items,
- key = { "${catalogRow.catalogId}_${it.id}" }
+ key = { "${catalogRow.type}_${catalogRow.catalogId}_${it.id}" }
) { item ->
ContentCard(
item = item,
diff --git a/app/src/main/java/com/nuvio/tv/ui/screens/detail/MetaDetailsScreen.kt b/app/src/main/java/com/nuvio/tv/ui/screens/detail/MetaDetailsScreen.kt
index d43227da..76742890 100644
--- a/app/src/main/java/com/nuvio/tv/ui/screens/detail/MetaDetailsScreen.kt
+++ b/app/src/main/java/com/nuvio/tv/ui/screens/detail/MetaDetailsScreen.kt
@@ -122,41 +122,94 @@ private fun MetaDetailsContent(
val isSeries = meta.type == ContentType.SERIES || meta.videos.isNotEmpty()
val nextEpisode = episodesForSeason.firstOrNull()
- TvLazyColumn(
- modifier = Modifier.fillMaxSize(),
- contentPadding = PaddingValues(bottom = 48.dp)
- ) {
- // Hero section with backdrop
- item {
- HeroSection(
- meta = meta,
- nextEpisode = nextEpisode,
- onPlayClick = {
- val videoId = if (isSeries && nextEpisode != null) {
- nextEpisode.id
- } else {
- meta.id
- }
- onPlayClick(videoId)
- }
+ Box(modifier = Modifier.fillMaxSize()) {
+ // Sticky background image - stays fixed in place while content scrolls
+ Box(modifier = Modifier.fillMaxSize()) {
+ AsyncImage(
+ model = meta.background ?: meta.poster,
+ contentDescription = null,
+ modifier = Modifier.fillMaxSize(),
+ contentScale = ContentScale.Crop
+ )
+
+ // Light global dim so text remains readable; the main fade happens in the section below the hero.
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(NuvioColors.Background.copy(alpha = 0.08f))
+ )
+
+ // Left side gradient for better text readability
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(
+ Brush.horizontalGradient(
+ colors = listOf(
+ NuvioColors.Background.copy(alpha = 0.85f),
+ NuvioColors.Background.copy(alpha = 0.5f),
+ Color.Transparent
+ ),
+ startX = 0f,
+ endX = 600f
+ )
+ )
)
}
- // Season tabs and episodes for series
- if (isSeries && seasons.isNotEmpty()) {
+ // Scrollable content on top of the fixed background
+ TvLazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ contentPadding = PaddingValues(bottom = 48.dp)
+ ) {
+ // Hero content section (fills screen height, content at bottom)
item {
- SeasonTabs(
- seasons = seasons,
- selectedSeason = selectedSeason,
- onSeasonSelected = onSeasonSelected
+ HeroContentSection(
+ meta = meta,
+ nextEpisode = nextEpisode,
+ onPlayClick = {
+ val videoId = if (isSeries && nextEpisode != null) {
+ nextEpisode.id
+ } else {
+ meta.id
+ }
+ onPlayClick(videoId)
+ }
)
}
- item {
- EpisodesRow(
- episodes = episodesForSeason,
- onEpisodeClick = onEpisodeClick
- )
+ // Season tabs and episodes for series - fully transparent over backdrop
+ if (isSeries && seasons.isNotEmpty()) {
+ item {
+ // This section owns the fade-to-dark background (smooth at top, darker as you scroll down).
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ Brush.verticalGradient(
+ colors = listOf(
+ Color.Transparent,
+ NuvioColors.Background.copy(alpha = 0.55f),
+ NuvioColors.Background.copy(alpha = 0.85f),
+ NuvioColors.Background
+ )
+ )
+ )
+ ) {
+ Column(modifier = Modifier.fillMaxWidth()) {
+ SeasonTabs(
+ seasons = seasons,
+ selectedSeason = selectedSeason,
+ onSeasonSelected = onSeasonSelected
+ )
+ EpisodesRow(
+ episodes = episodesForSeason,
+ onEpisodeClick = onEpisodeClick
+ )
+ Spacer(modifier = Modifier.height(140.dp))
+ }
+ }
+ }
}
}
}
@@ -164,62 +217,22 @@ private fun MetaDetailsContent(
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
-private fun HeroSection(
+private fun HeroContentSection(
meta: Meta,
nextEpisode: Video?,
onPlayClick: () -> Unit
) {
+ // Full height hero with content at bottom
Box(
modifier = Modifier
- .fillMaxSize()
+ .fillMaxWidth()
+ .height(540.dp), // Full screen height for TV
+ contentAlignment = Alignment.BottomStart
) {
- // Background image
- AsyncImage(
- model = meta.background ?: meta.poster,
- contentDescription = null,
- modifier = Modifier.fillMaxSize(),
- contentScale = ContentScale.Crop
- )
-
- // Gradient overlay
- Box(
- modifier = Modifier
- .fillMaxSize()
- .background(
- Brush.verticalGradient(
- colors = listOf(
- Color.Transparent,
- NuvioColors.Background.copy(alpha = 0.3f),
- NuvioColors.Background.copy(alpha = 0.7f),
- NuvioColors.Background
- ),
- startY = 0f,
- endY = Float.POSITIVE_INFINITY
- )
- )
- )
-
- // Left side gradient for better text readability
- Box(
- modifier = Modifier
- .fillMaxSize()
- .background(
- Brush.horizontalGradient(
- colors = listOf(
- NuvioColors.Background.copy(alpha = 0.8f),
- NuvioColors.Background.copy(alpha = 0.4f),
- Color.Transparent
- ),
- startX = 0f,
- endX = 800f
- )
- )
- )
-
- // Content
+ // Content at the bottom
Column(
modifier = Modifier
- .fillMaxSize()
+ .fillMaxWidth()
.padding(start = 48.dp, end = 48.dp, bottom = 32.dp),
verticalArrangement = Arrangement.Bottom
) {
@@ -488,12 +501,21 @@ private fun SeasonTabs(
Column(
modifier = Modifier
.fillMaxWidth()
+ .background(
+ Brush.verticalGradient(
+ colors = listOf(
+ NuvioColors.Background.copy(alpha = 0.2f),
+ NuvioColors.Background.copy(alpha = 0.4f)
+ )
+ )
+ )
.padding(horizontal = 48.dp, vertical = 24.dp)
) {
TabRow(
selectedTabIndex = selectedIndex,
modifier = Modifier.fillMaxWidth(),
- separator = { Spacer(modifier = Modifier.width(24.dp)) }
+ separator = { Spacer(modifier = Modifier.width(24.dp)) },
+ containerColor = Color.Transparent
) {
seasons.forEachIndexed { index, season ->
Tab(
@@ -524,8 +546,17 @@ private fun EpisodesRow(
onEpisodeClick: (Video) -> Unit
) {
TvLazyRow(
- modifier = Modifier.fillMaxWidth(),
- contentPadding = PaddingValues(horizontal = 48.dp),
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ Brush.verticalGradient(
+ colors = listOf(
+ NuvioColors.Background.copy(alpha = 0.3f),
+ NuvioColors.Background.copy(alpha = 0.5f)
+ )
+ )
+ ),
+ contentPadding = PaddingValues(horizontal = 48.dp, vertical = 16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
items(episodes, key = { it.id }) { episode ->
diff --git a/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeScreen.kt b/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeScreen.kt
index 6e790149..27deeac4 100644
--- a/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeScreen.kt
+++ b/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeScreen.kt
@@ -61,7 +61,7 @@ fun HomeScreen(
) {
items(
items = uiState.catalogRows,
- key = { "${it.addonId}_${it.catalogId}" }
+ key = { "${it.addonId}_${it.type}_${it.catalogId}" }
) { catalogRow ->
CatalogRowSection(
catalogRow = catalogRow,
@@ -72,7 +72,8 @@ fun HomeScreen(
viewModel.onEvent(
HomeEvent.OnLoadMoreCatalog(
catalogId = catalogRow.catalogId,
- addonId = catalogRow.addonId
+ addonId = catalogRow.addonId,
+ type = catalogRow.type.toApiString()
)
)
}
diff --git a/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeUiState.kt b/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeUiState.kt
index e206ff07..51d3da14 100644
--- a/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeUiState.kt
+++ b/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeUiState.kt
@@ -11,6 +11,6 @@ data class HomeUiState(
sealed class HomeEvent {
data class OnItemClick(val itemId: String, val itemType: String) : HomeEvent()
- data class OnLoadMoreCatalog(val catalogId: String, val addonId: String) : HomeEvent()
+ data class OnLoadMoreCatalog(val catalogId: String, val addonId: String, val type: String) : HomeEvent()
data object OnRetry : HomeEvent()
}
diff --git a/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeViewModel.kt b/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeViewModel.kt
index 10a7aada..0f358cd1 100644
--- a/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeViewModel.kt
+++ b/app/src/main/java/com/nuvio/tv/ui/screens/home/HomeViewModel.kt
@@ -37,7 +37,7 @@ class HomeViewModel @Inject constructor(
fun onEvent(event: HomeEvent) {
when (event) {
is HomeEvent.OnItemClick -> navigateToDetail(event.itemId, event.itemType)
- is HomeEvent.OnLoadMoreCatalog -> loadMoreCatalogItems(event.catalogId, event.addonId)
+ is HomeEvent.OnLoadMoreCatalog -> loadMoreCatalogItems(event.catalogId, event.addonId, event.type)
HomeEvent.OnRetry -> loadAllCatalogs()
}
}
@@ -59,7 +59,11 @@ class HomeViewModel @Inject constructor(
// Build catalog order based on addon manifest order
addons.forEach { addon ->
addon.catalogs.forEach { catalog ->
- val key = "${addon.id}_${catalog.id}"
+ val key = catalogKey(
+ addonId = addon.id,
+ type = catalog.type.toApiString(),
+ catalogId = catalog.id
+ )
if (key !in catalogOrder) {
catalogOrder.add(key)
}
@@ -91,7 +95,11 @@ class HomeViewModel @Inject constructor(
).collect { result ->
when (result) {
is NetworkResult.Success -> {
- val key = "${addon.id}_${catalog.id}"
+ val key = catalogKey(
+ addonId = addon.id,
+ type = catalog.type.toApiString(),
+ catalogId = catalog.id
+ )
catalogsMap[key] = result.data
updateCatalogRows()
}
@@ -104,8 +112,8 @@ class HomeViewModel @Inject constructor(
}
}
- private fun loadMoreCatalogItems(catalogId: String, addonId: String) {
- val key = "${addonId}_${catalogId}"
+ private fun loadMoreCatalogItems(catalogId: String, addonId: String, type: String) {
+ val key = catalogKey(addonId = addonId, type = type, catalogId = catalogId)
val currentRow = catalogsMap[key] ?: return
if (currentRow.isLoading || !currentRow.hasMore) return
@@ -157,4 +165,8 @@ class HomeViewModel @Inject constructor(
private fun navigateToDetail(itemId: String, itemType: String) {
_uiState.update { it.copy(selectedItemId = itemId) }
}
+
+ private fun catalogKey(addonId: String, type: String, catalogId: String): String {
+ return "${addonId}_${type}_${catalogId}"
+ }
}