added removal of next up cards
This commit is contained in:
parent
f30a9277c2
commit
2df29bf988
6 changed files with 105 additions and 44 deletions
|
|
@ -40,7 +40,7 @@ fun ClassicHomeContent(
|
|||
onNavigateToDetail: (String, String, String) -> Unit,
|
||||
onContinueWatchingClick: (ContinueWatchingItem) -> Unit,
|
||||
onNavigateToCatalogSeeAll: (String, String, String) -> Unit,
|
||||
onRemoveContinueWatching: (String, Int?, Int?) -> Unit,
|
||||
onRemoveContinueWatching: (String, Int?, Int?, Boolean) -> Unit,
|
||||
onRequestTrailerPreview: (MetaPreview) -> Unit,
|
||||
onSaveFocusState: (Int, Int, Int, Int, Map<String, Int>) -> Unit
|
||||
) {
|
||||
|
|
@ -156,7 +156,8 @@ fun ClassicHomeContent(
|
|||
is ContinueWatchingItem.InProgress -> item.progress.episode
|
||||
is ContinueWatchingItem.NextUp -> item.info.episode
|
||||
}
|
||||
onRemoveContinueWatching(contentId, season, episode)
|
||||
val isNextUp = item is ContinueWatchingItem.NextUp
|
||||
onRemoveContinueWatching(contentId, season, episode, isNextUp)
|
||||
},
|
||||
focusedItemIndex = when {
|
||||
focusState.hasSavedFocus && focusState.focusedRowIndex == -1 -> focusState.focusedItemIndex
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ fun GridHomeContent(
|
|||
onNavigateToDetail: (String, String, String) -> Unit,
|
||||
onContinueWatchingClick: (ContinueWatchingItem) -> Unit,
|
||||
onNavigateToCatalogSeeAll: (String, String, String) -> Unit,
|
||||
onRemoveContinueWatching: (String, Int?, Int?) -> Unit,
|
||||
onRemoveContinueWatching: (String, Int?, Int?, Boolean) -> Unit,
|
||||
posterCardStyle: PosterCardStyle = PosterCardDefaults.Style,
|
||||
onSaveGridFocusState: (Int, Int) -> Unit
|
||||
) {
|
||||
|
|
@ -232,7 +232,8 @@ fun GridHomeContent(
|
|||
is ContinueWatchingItem.InProgress -> item.progress.episode
|
||||
is ContinueWatchingItem.NextUp -> item.info.episode
|
||||
}
|
||||
onRemoveContinueWatching(contentId, season, episode)
|
||||
val isNextUp = item is ContinueWatchingItem.NextUp
|
||||
onRemoveContinueWatching(contentId, season, episode, isNextUp)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -353,7 +354,8 @@ fun GridHomeContent(
|
|||
is ContinueWatchingItem.InProgress -> item.progress.episode
|
||||
is ContinueWatchingItem.NextUp -> item.info.episode
|
||||
}
|
||||
onRemoveContinueWatching(contentId, season, episode)
|
||||
val isNextUp = item is ContinueWatchingItem.NextUp
|
||||
onRemoveContinueWatching(contentId, season, episode, isNextUp)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,8 +145,8 @@ fun HomeScreen(
|
|||
onNavigateToDetail = onNavigateToDetail,
|
||||
onContinueWatchingClick = onContinueWatchingClick,
|
||||
onNavigateToCatalogSeeAll = onNavigateToCatalogSeeAll,
|
||||
onRemoveContinueWatching = { contentId, season, episode ->
|
||||
viewModel.onEvent(HomeEvent.OnRemoveContinueWatching(contentId, season, episode))
|
||||
onRemoveContinueWatching = { contentId, season, episode, isNextUp ->
|
||||
viewModel.onEvent(HomeEvent.OnRemoveContinueWatching(contentId, season, episode, isNextUp))
|
||||
},
|
||||
onRequestTrailerPreview = { item ->
|
||||
viewModel.requestTrailerPreview(item)
|
||||
|
|
@ -162,8 +162,8 @@ fun HomeScreen(
|
|||
onNavigateToDetail = onNavigateToDetail,
|
||||
onContinueWatchingClick = onContinueWatchingClick,
|
||||
onNavigateToCatalogSeeAll = onNavigateToCatalogSeeAll,
|
||||
onRemoveContinueWatching = { contentId, season, episode ->
|
||||
viewModel.onEvent(HomeEvent.OnRemoveContinueWatching(contentId, season, episode))
|
||||
onRemoveContinueWatching = { contentId, season, episode, isNextUp ->
|
||||
viewModel.onEvent(HomeEvent.OnRemoveContinueWatching(contentId, season, episode, isNextUp))
|
||||
},
|
||||
onSaveGridFocusState = { vi, vo ->
|
||||
viewModel.saveGridFocusState(vi, vo)
|
||||
|
|
|
|||
|
|
@ -86,6 +86,11 @@ sealed class GridItem {
|
|||
sealed class HomeEvent {
|
||||
data class OnItemClick(val itemId: String, val itemType: String) : HomeEvent()
|
||||
data class OnLoadMoreCatalog(val catalogId: String, val addonId: String, val type: String) : HomeEvent()
|
||||
data class OnRemoveContinueWatching(val contentId: String, val season: Int? = null, val episode: Int? = null) : HomeEvent()
|
||||
data class OnRemoveContinueWatching(
|
||||
val contentId: String,
|
||||
val season: Int? = null,
|
||||
val episode: Int? = null,
|
||||
val isNextUp: Boolean = false
|
||||
) : HomeEvent()
|
||||
data object OnRetry : HomeEvent()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ class HomeViewModel @Inject constructor(
|
|||
private val trailerPreviewUrlsState = mutableStateMapOf<String, String>()
|
||||
private var activeTrailerPreviewItemId: String? = null
|
||||
private var trailerPreviewRequestVersion: Long = 0L
|
||||
private val dismissedNextUpKeys = MutableStateFlow<Set<String>>(emptySet())
|
||||
val trailerPreviewUrls: Map<String, String>
|
||||
get() = trailerPreviewUrlsState
|
||||
|
||||
|
|
@ -288,7 +289,13 @@ class HomeViewModel @Inject constructor(
|
|||
when (event) {
|
||||
is HomeEvent.OnItemClick -> navigateToDetail(event.itemId, event.itemType)
|
||||
is HomeEvent.OnLoadMoreCatalog -> loadMoreCatalogItems(event.catalogId, event.addonId, event.type)
|
||||
is HomeEvent.OnRemoveContinueWatching -> removeContinueWatching(event.contentId, event.season, event.episode)
|
||||
is HomeEvent.OnRemoveContinueWatching ->
|
||||
removeContinueWatching(
|
||||
contentId = event.contentId,
|
||||
season = event.season,
|
||||
episode = event.episode,
|
||||
isNextUp = event.isNextUp
|
||||
)
|
||||
HomeEvent.OnRetry -> viewModelScope.launch { loadAllCatalogs(addonsCache) }
|
||||
}
|
||||
}
|
||||
|
|
@ -297,10 +304,11 @@ class HomeViewModel @Inject constructor(
|
|||
viewModelScope.launch {
|
||||
combine(
|
||||
watchProgressRepository.allProgress,
|
||||
traktSettingsDataStore.continueWatchingDaysCap
|
||||
) { items, daysCap ->
|
||||
items to daysCap
|
||||
}.collectLatest { (items, daysCap) ->
|
||||
traktSettingsDataStore.continueWatchingDaysCap,
|
||||
dismissedNextUpKeys
|
||||
) { items, daysCap, dismissedNextUp ->
|
||||
Triple(items, daysCap, dismissedNextUp)
|
||||
}.collectLatest { (items, daysCap, dismissedNextUp) ->
|
||||
val windowMs = daysCap.toLong() * 24L * 60L * 60L * 1000L
|
||||
val cutoffMs = System.currentTimeMillis() - windowMs
|
||||
val recentItems = items
|
||||
|
|
@ -324,7 +332,8 @@ class HomeViewModel @Inject constructor(
|
|||
// Then enrich Next Up in background with bounded concurrency.
|
||||
enrichContinueWatchingProgressively(
|
||||
allProgress = recentItems,
|
||||
inProgressItems = inProgressOnly
|
||||
inProgressItems = inProgressOnly,
|
||||
dismissedNextUp = dismissedNextUp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -340,7 +349,8 @@ class HomeViewModel @Inject constructor(
|
|||
|
||||
private suspend fun enrichContinueWatchingProgressively(
|
||||
allProgress: List<WatchProgress>,
|
||||
inProgressItems: List<ContinueWatchingItem.InProgress>
|
||||
inProgressItems: List<ContinueWatchingItem.InProgress>,
|
||||
dismissedNextUp: Set<String>
|
||||
) = coroutineScope {
|
||||
val inProgressIds = inProgressItems.map { it.progress.contentId }.toSet()
|
||||
|
||||
|
|
@ -355,6 +365,7 @@ class HomeViewModel @Inject constructor(
|
|||
.groupBy { it.contentId }
|
||||
.mapNotNull { (_, items) -> items.maxByOrNull { it.lastWatched } }
|
||||
.filter { it.contentId !in inProgressIds }
|
||||
.filter { progress -> nextUpDismissKey(progress.contentId) !in dismissedNextUp }
|
||||
.sortedByDescending { it.lastWatched }
|
||||
.take(MAX_NEXT_UP_LOOKUPS)
|
||||
|
||||
|
|
@ -465,7 +476,29 @@ class HomeViewModel @Inject constructor(
|
|||
return type == "series" || type == "tv"
|
||||
}
|
||||
|
||||
private fun removeContinueWatching(contentId: String, season: Int? = null, episode: Int? = null) {
|
||||
private fun removeContinueWatching(
|
||||
contentId: String,
|
||||
season: Int? = null,
|
||||
episode: Int? = null,
|
||||
isNextUp: Boolean = false
|
||||
) {
|
||||
if (isNextUp) {
|
||||
val dismissKey = nextUpDismissKey(contentId)
|
||||
dismissedNextUpKeys.update { it + dismissKey }
|
||||
_uiState.update { state ->
|
||||
state.copy(
|
||||
continueWatchingItems = state.continueWatchingItems.filterNot { item ->
|
||||
when (item) {
|
||||
is ContinueWatchingItem.NextUp ->
|
||||
nextUpDismissKey(item.info.contentId) == dismissKey
|
||||
is ContinueWatchingItem.InProgress -> false
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
Log.d(
|
||||
TAG,
|
||||
|
|
@ -475,6 +508,8 @@ class HomeViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun nextUpDismissKey(contentId: String): String = contentId
|
||||
|
||||
private fun observeInstalledAddons() {
|
||||
viewModelScope.launch {
|
||||
addonRepository.getInstalledAddons()
|
||||
|
|
|
|||
|
|
@ -240,12 +240,14 @@ fun TraktScreen(
|
|||
}
|
||||
}
|
||||
|
||||
SettingsActionRow(
|
||||
title = "Continue Watching Window",
|
||||
subtitle = "Days of Trakt progress considered for continue watching",
|
||||
value = "${uiState.continueWatchingDaysCap} days",
|
||||
onClick = { showDaysCapDialog = true }
|
||||
)
|
||||
if (uiState.mode == TraktConnectionMode.CONNECTED) {
|
||||
SettingsActionRow(
|
||||
title = "Continue Watching Window",
|
||||
subtitle = "Days of Trakt progress considered for continue watching",
|
||||
value = "${uiState.continueWatchingDaysCap} days",
|
||||
onClick = { showDaysCapDialog = true }
|
||||
)
|
||||
}
|
||||
|
||||
if (uiState.mode != TraktConnectionMode.CONNECTED) {
|
||||
uiState.statusMessage?.let { status ->
|
||||
|
|
@ -294,7 +296,7 @@ fun TraktScreen(
|
|||
Dialog(onDismissRequest = { showDaysCapDialog = false }) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.width(560.dp)
|
||||
.width(620.dp)
|
||||
.background(NuvioColors.BackgroundElevated, RoundedCornerShape(16.dp))
|
||||
.border(1.dp, NuvioColors.Border, RoundedCornerShape(16.dp))
|
||||
.padding(24.dp),
|
||||
|
|
@ -311,30 +313,46 @@ fun TraktScreen(
|
|||
color = NuvioColors.TextSecondary
|
||||
)
|
||||
|
||||
continueWatchingDayOptions.forEach { days ->
|
||||
val selected = uiState.continueWatchingDaysCap == days
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.onContinueWatchingDaysCapSelected(days)
|
||||
showDaysCapDialog = false
|
||||
},
|
||||
colors = ButtonDefaults.colors(
|
||||
containerColor = if (selected) NuvioColors.Primary else NuvioColors.BackgroundCard,
|
||||
contentColor = if (selected) Color.Black else NuvioColors.TextPrimary
|
||||
)
|
||||
continueWatchingDayOptions.chunked(2).forEach { rowOptions ->
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text("$days days")
|
||||
rowOptions.forEach { days ->
|
||||
val selected = uiState.continueWatchingDaysCap == days
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.onContinueWatchingDaysCapSelected(days)
|
||||
showDaysCapDialog = false
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
colors = ButtonDefaults.colors(
|
||||
containerColor = if (selected) NuvioColors.Primary else NuvioColors.BackgroundCard,
|
||||
contentColor = if (selected) Color.Black else NuvioColors.TextPrimary
|
||||
)
|
||||
) {
|
||||
Text("$days days")
|
||||
}
|
||||
}
|
||||
if (rowOptions.size == 1) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = { showDaysCapDialog = false },
|
||||
colors = ButtonDefaults.colors(
|
||||
containerColor = NuvioColors.BackgroundCard,
|
||||
contentColor = NuvioColors.TextPrimary
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
Text("Cancel")
|
||||
Button(
|
||||
onClick = { showDaysCapDialog = false },
|
||||
colors = ButtonDefaults.colors(
|
||||
containerColor = NuvioColors.BackgroundCard,
|
||||
contentColor = NuvioColors.TextPrimary
|
||||
)
|
||||
) {
|
||||
Text("Cancel")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue