continue watching with episode history limits and UI updates
This commit is contained in:
parent
07ae0d889d
commit
a1bd550bbc
4 changed files with 84 additions and 3 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -22,3 +22,4 @@ android-tv-samples
|
|||
JustPlayer
|
||||
TODO.local.md
|
||||
local.dev.properties
|
||||
trakt-docss
|
||||
|
|
@ -28,6 +28,10 @@ class WatchProgressPreferences @Inject constructor(
|
|||
|
||||
// Maximum items to keep in continue watching
|
||||
private val maxItems = 50
|
||||
|
||||
private val maxEpisodesPerContent = 25
|
||||
|
||||
private val maxStoredEntries = 300
|
||||
|
||||
/**
|
||||
* Get all watch progress items, sorted by last watched (most recent first)
|
||||
|
|
@ -199,8 +203,47 @@ class WatchProgressPreferences @Inject constructor(
|
|||
.sortedByDescending { it.value }
|
||||
.take(maxItems)
|
||||
.map { it.key }
|
||||
.toSet()
|
||||
val keepContentIdSet = keepContentIds.toSet()
|
||||
|
||||
return map.filterValues { it.contentId in keepContentIds }
|
||||
val filteredByContent = map.filterValues { it.contentId in keepContentIdSet }
|
||||
val boundedByContent = mutableMapOf<String, WatchProgress>()
|
||||
|
||||
keepContentIds.forEach { contentId ->
|
||||
val entriesForContent = filteredByContent.filterValues { it.contentId == contentId }
|
||||
|
||||
// Keep canonical content-level record when present.
|
||||
entriesForContent[contentId]?.let { boundedByContent[contentId] = it }
|
||||
|
||||
val recentEpisodeEntries = entriesForContent
|
||||
.filterKeys { it != contentId }
|
||||
.entries
|
||||
.sortedByDescending { it.value.lastWatched }
|
||||
.take(maxEpisodesPerContent)
|
||||
|
||||
recentEpisodeEntries.forEach { (key, value) ->
|
||||
boundedByContent[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
if (boundedByContent.size <= maxStoredEntries) return boundedByContent
|
||||
|
||||
val pinnedContentKeys = keepContentIds.filter { boundedByContent.containsKey(it) }.toSet()
|
||||
val remainingSlots = (maxStoredEntries - pinnedContentKeys.size).coerceAtLeast(0)
|
||||
|
||||
val limited = mutableMapOf<String, WatchProgress>()
|
||||
pinnedContentKeys.forEach { key ->
|
||||
boundedByContent[key]?.let { limited[key] = it }
|
||||
}
|
||||
|
||||
boundedByContent.entries
|
||||
.asSequence()
|
||||
.filter { (key, _) -> key !in pinnedContentKeys }
|
||||
.sortedByDescending { (_, value) -> value.lastWatched }
|
||||
.take(remainingSlots)
|
||||
.forEach { (key, value) ->
|
||||
limited[key] = value
|
||||
}
|
||||
|
||||
return limited
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -306,6 +306,42 @@ fun GridHomeContent(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!continueWatchingInserted && uiState.continueWatchingItems.isNotEmpty()) {
|
||||
item(
|
||||
key = "continue_watching_fallback",
|
||||
span = { TvGridItemSpan(maxLineSpan) },
|
||||
contentType = "continue_watching"
|
||||
) {
|
||||
GridContinueWatchingSection(
|
||||
items = uiState.continueWatchingItems,
|
||||
focusedItemIndex = if (shouldRequestInitialFocus && !hasHero) 0 else -1,
|
||||
onItemClick = { item ->
|
||||
onContinueWatchingClick(item)
|
||||
},
|
||||
onDetailsClick = { item ->
|
||||
onNavigateToDetail(
|
||||
when (item) {
|
||||
is ContinueWatchingItem.InProgress -> item.progress.contentId
|
||||
is ContinueWatchingItem.NextUp -> item.info.contentId
|
||||
},
|
||||
when (item) {
|
||||
is ContinueWatchingItem.InProgress -> item.progress.contentType
|
||||
is ContinueWatchingItem.NextUp -> item.info.contentType
|
||||
},
|
||||
""
|
||||
)
|
||||
},
|
||||
onRemoveItem = { item ->
|
||||
val contentId = when (item) {
|
||||
is ContinueWatchingItem.InProgress -> item.progress.contentId
|
||||
is ContinueWatchingItem.NextUp -> item.info.contentId
|
||||
}
|
||||
onRemoveContinueWatching(contentId)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sticky header overlay
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@ fun HomeScreen(
|
|||
val gridFocusState by viewModel.gridFocusState.collectAsState()
|
||||
val hasHeroContent = uiState.heroSectionEnabled && uiState.heroItems.isNotEmpty()
|
||||
val hasCatalogContent = uiState.catalogRows.any { it.items.isNotEmpty() }
|
||||
val hasPrimaryHomeContent = hasHeroContent || hasCatalogContent
|
||||
val hasContinueWatchingContent = uiState.continueWatchingItems.isNotEmpty()
|
||||
val hasPrimaryHomeContent = hasHeroContent || hasCatalogContent || hasContinueWatchingContent
|
||||
var hasEnteredHomeContent: Boolean by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(hasPrimaryHomeContent) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue