mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-16 23:12:12 +00:00
Merge pull request #1012 from guuilp/feat/continue-watching-sort-mode
feat(settings): add Continue Watching sort mode setting
This commit is contained in:
commit
13ce89cb99
8 changed files with 233 additions and 4 deletions
|
|
@ -517,6 +517,12 @@
|
||||||
<string name="settings_continue_watching_blur_next_up_title">Blur Unwatched in Continue Watching</string>
|
<string name="settings_continue_watching_blur_next_up_title">Blur Unwatched in Continue Watching</string>
|
||||||
<string name="settings_continue_watching_show_unaired_next_up_description">Include upcoming episodes in Continue Watching before they air.</string>
|
<string name="settings_continue_watching_show_unaired_next_up_description">Include upcoming episodes in Continue Watching before they air.</string>
|
||||||
<string name="settings_continue_watching_show_unaired_next_up_title">Show Unaired Next Up Episodes</string>
|
<string name="settings_continue_watching_show_unaired_next_up_title">Show Unaired Next Up Episodes</string>
|
||||||
|
<string name="settings_continue_watching_section_sort_order">SORT ORDER</string>
|
||||||
|
<string name="settings_continue_watching_sort_mode_title">Sort Order</string>
|
||||||
|
<string name="settings_continue_watching_sort_mode_default">Default</string>
|
||||||
|
<string name="settings_continue_watching_sort_mode_default_desc">Sort all items by recency</string>
|
||||||
|
<string name="settings_continue_watching_sort_mode_streaming">Streaming Style</string>
|
||||||
|
<string name="settings_continue_watching_sort_mode_streaming_desc">Released items first, upcoming at the end</string>
|
||||||
<string name="settings_continue_watching_section_card_style">Poster Card Style</string>
|
<string name="settings_continue_watching_section_card_style">Poster Card Style</string>
|
||||||
<string name="settings_continue_watching_section_on_launch">ON LAUNCH</string>
|
<string name="settings_continue_watching_section_on_launch">ON LAUNCH</string>
|
||||||
<string name="settings_continue_watching_section_up_next_behavior">UP NEXT BEHAVIOR</string>
|
<string name="settings_continue_watching_section_up_next_behavior">UP NEXT BEHAVIOR</string>
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import com.nuvio.app.features.watchprogress.ContinueWatchingEnrichmentCache
|
||||||
import com.nuvio.app.features.watchprogress.CurrentDateProvider
|
import com.nuvio.app.features.watchprogress.CurrentDateProvider
|
||||||
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepository
|
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepository
|
||||||
import com.nuvio.app.features.watchprogress.ContinueWatchingItem
|
import com.nuvio.app.features.watchprogress.ContinueWatchingItem
|
||||||
|
import com.nuvio.app.features.watchprogress.ContinueWatchingSortMode
|
||||||
import com.nuvio.app.features.watchprogress.isSeriesTypeForContinueWatching
|
import com.nuvio.app.features.watchprogress.isSeriesTypeForContinueWatching
|
||||||
import com.nuvio.app.features.watchprogress.nextUpDismissKey
|
import com.nuvio.app.features.watchprogress.nextUpDismissKey
|
||||||
import com.nuvio.app.features.watchprogress.WatchProgressClock
|
import com.nuvio.app.features.watchprogress.WatchProgressClock
|
||||||
|
|
@ -247,11 +248,14 @@ fun HomeScreen(
|
||||||
visibleContinueWatchingEntries,
|
visibleContinueWatchingEntries,
|
||||||
cachedInProgressItems,
|
cachedInProgressItems,
|
||||||
effectivNextUpItems,
|
effectivNextUpItems,
|
||||||
|
continueWatchingPreferences.sortMode,
|
||||||
) {
|
) {
|
||||||
buildHomeContinueWatchingItems(
|
buildHomeContinueWatchingItems(
|
||||||
visibleEntries = visibleContinueWatchingEntries,
|
visibleEntries = visibleContinueWatchingEntries,
|
||||||
cachedInProgressByVideoId = cachedInProgressItems,
|
cachedInProgressByVideoId = cachedInProgressItems,
|
||||||
nextUpItemsBySeries = effectivNextUpItems,
|
nextUpItemsBySeries = effectivNextUpItems,
|
||||||
|
sortMode = continueWatchingPreferences.sortMode,
|
||||||
|
todayIsoDate = CurrentDateProvider.todayIsoDate(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val availableManifests = remember(addonsUiState.addons) {
|
val availableManifests = remember(addonsUiState.addons) {
|
||||||
|
|
@ -633,6 +637,8 @@ internal fun buildHomeContinueWatchingItems(
|
||||||
visibleEntries: List<WatchProgressEntry>,
|
visibleEntries: List<WatchProgressEntry>,
|
||||||
cachedInProgressByVideoId: Map<String, ContinueWatchingItem> = emptyMap(),
|
cachedInProgressByVideoId: Map<String, ContinueWatchingItem> = emptyMap(),
|
||||||
nextUpItemsBySeries: Map<String, Pair<Long, ContinueWatchingItem>>,
|
nextUpItemsBySeries: Map<String, Pair<Long, ContinueWatchingItem>>,
|
||||||
|
sortMode: ContinueWatchingSortMode = ContinueWatchingSortMode.DEFAULT,
|
||||||
|
todayIsoDate: String = "",
|
||||||
): List<ContinueWatchingItem> {
|
): List<ContinueWatchingItem> {
|
||||||
val inProgressSeriesIds = visibleEntries
|
val inProgressSeriesIds = visibleEntries
|
||||||
.asSequence()
|
.asSequence()
|
||||||
|
|
@ -641,7 +647,7 @@ internal fun buildHomeContinueWatchingItems(
|
||||||
.filter(String::isNotBlank)
|
.filter(String::isNotBlank)
|
||||||
.toSet()
|
.toSet()
|
||||||
|
|
||||||
return buildList {
|
val candidates = buildList {
|
||||||
addAll(
|
addAll(
|
||||||
visibleEntries.map { entry ->
|
visibleEntries.map { entry ->
|
||||||
val liveItem = entry.toContinueWatchingItem()
|
val liveItem = entry.toContinueWatchingItem()
|
||||||
|
|
@ -663,13 +669,62 @@ internal fun buildHomeContinueWatchingItems(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deduplicate by series/content id first (order-stable)
|
||||||
|
val seen = mutableSetOf<String>()
|
||||||
|
val deduplicated = candidates
|
||||||
.sortedWith(
|
.sortedWith(
|
||||||
compareByDescending<HomeContinueWatchingCandidate> { it.lastUpdatedEpochMs }
|
compareByDescending<HomeContinueWatchingCandidate> { it.lastUpdatedEpochMs }
|
||||||
.thenByDescending { it.isProgressEntry },
|
.thenByDescending { it.isProgressEntry },
|
||||||
)
|
)
|
||||||
.filter { candidate -> candidate.item.shouldDisplayInContinueWatching() }
|
.filter { candidate -> candidate.item.shouldDisplayInContinueWatching() }
|
||||||
.distinctBy { candidate -> candidate.item.parentMetaId.ifBlank { candidate.item.videoId } }
|
.filter { candidate ->
|
||||||
|
val key = candidate.item.parentMetaId.ifBlank { candidate.item.videoId }
|
||||||
|
seen.add(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (sortMode) {
|
||||||
|
ContinueWatchingSortMode.DEFAULT -> deduplicated.map(HomeContinueWatchingCandidate::item)
|
||||||
|
ContinueWatchingSortMode.STREAMING_STYLE -> applyStreamingStyleSort(deduplicated, todayIsoDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyStreamingStyleSort(
|
||||||
|
candidates: List<HomeContinueWatchingCandidate>,
|
||||||
|
todayIsoDate: String,
|
||||||
|
): List<ContinueWatchingItem> {
|
||||||
|
val (released, unreleased) = candidates.partition { candidate ->
|
||||||
|
val item = candidate.item
|
||||||
|
if (!item.isNextUp) {
|
||||||
|
true // in-progress items are always "released"
|
||||||
|
} else {
|
||||||
|
val itemReleased = item.released
|
||||||
|
if (itemReleased.isNullOrBlank() || todayIsoDate.isBlank()) {
|
||||||
|
true // no date info → treat as released
|
||||||
|
} else {
|
||||||
|
isReleasedBy(todayIsoDate = todayIsoDate, releasedDate = itemReleased)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Released: most recently watched first (already sorted by dedup pass)
|
||||||
|
val sortedReleased = released.map(HomeContinueWatchingCandidate::item)
|
||||||
|
|
||||||
|
// Unaired: soonest air date first; unknown dates go to the end
|
||||||
|
val sortedUnreleased = unreleased
|
||||||
|
.sortedWith { a, b ->
|
||||||
|
val dateA = a.item.released?.takeIf { it.isNotBlank() }
|
||||||
|
val dateB = b.item.released?.takeIf { it.isNotBlank() }
|
||||||
|
when {
|
||||||
|
dateA == null && dateB == null -> 0
|
||||||
|
dateA == null -> 1
|
||||||
|
dateB == null -> -1
|
||||||
|
else -> dateA.compareTo(dateB)
|
||||||
|
}
|
||||||
|
}
|
||||||
.map(HomeContinueWatchingCandidate::item)
|
.map(HomeContinueWatchingCandidate::item)
|
||||||
|
|
||||||
|
return sortedReleased + sortedUnreleased
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class CompletedSeriesCandidate(
|
private data class CompletedSeriesCandidate(
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,27 @@ import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
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.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.Check
|
||||||
import androidx.compose.material.icons.rounded.CheckCircle
|
import androidx.compose.material.icons.rounded.CheckCircle
|
||||||
|
import androidx.compose.material3.BasicAlertDialog
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
|
|
@ -25,6 +34,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import com.nuvio.app.features.home.components.ContinueWatchingStylePreview
|
import com.nuvio.app.features.home.components.ContinueWatchingStylePreview
|
||||||
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepository
|
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepository
|
||||||
import com.nuvio.app.features.watchprogress.ContinueWatchingSectionStyle
|
import com.nuvio.app.features.watchprogress.ContinueWatchingSectionStyle
|
||||||
|
import com.nuvio.app.features.watchprogress.ContinueWatchingSortMode
|
||||||
import nuvio.composeapp.generated.resources.Res
|
import nuvio.composeapp.generated.resources.Res
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_resume_prompt_description
|
import nuvio.composeapp.generated.resources.settings_continue_watching_resume_prompt_description
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_resume_prompt_title
|
import nuvio.composeapp.generated.resources.settings_continue_watching_resume_prompt_title
|
||||||
|
|
@ -34,10 +44,16 @@ import nuvio.composeapp.generated.resources.settings_continue_watching_show_unai
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_show_unaired_next_up_title
|
import nuvio.composeapp.generated.resources.settings_continue_watching_show_unaired_next_up_title
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_section_card_style
|
import nuvio.composeapp.generated.resources.settings_continue_watching_section_card_style
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_section_on_launch
|
import nuvio.composeapp.generated.resources.settings_continue_watching_section_on_launch
|
||||||
|
import nuvio.composeapp.generated.resources.settings_continue_watching_section_sort_order
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_section_up_next_behavior
|
import nuvio.composeapp.generated.resources.settings_continue_watching_section_up_next_behavior
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_section_visibility
|
import nuvio.composeapp.generated.resources.settings_continue_watching_section_visibility
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_show_description
|
import nuvio.composeapp.generated.resources.settings_continue_watching_show_description
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_show_title
|
import nuvio.composeapp.generated.resources.settings_continue_watching_show_title
|
||||||
|
import nuvio.composeapp.generated.resources.settings_continue_watching_sort_mode_default
|
||||||
|
import nuvio.composeapp.generated.resources.settings_continue_watching_sort_mode_default_desc
|
||||||
|
import nuvio.composeapp.generated.resources.settings_continue_watching_sort_mode_streaming
|
||||||
|
import nuvio.composeapp.generated.resources.settings_continue_watching_sort_mode_streaming_desc
|
||||||
|
import nuvio.composeapp.generated.resources.settings_continue_watching_sort_mode_title
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_style_poster
|
import nuvio.composeapp.generated.resources.settings_continue_watching_style_poster
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_style_poster_description
|
import nuvio.composeapp.generated.resources.settings_continue_watching_style_poster_description
|
||||||
import nuvio.composeapp.generated.resources.settings_continue_watching_style_wide
|
import nuvio.composeapp.generated.resources.settings_continue_watching_style_wide
|
||||||
|
|
@ -58,6 +74,7 @@ internal fun LazyListScope.continueWatchingSettingsContent(
|
||||||
showUnairedNextUp: Boolean,
|
showUnairedNextUp: Boolean,
|
||||||
blurNextUp: Boolean,
|
blurNextUp: Boolean,
|
||||||
showResumePromptOnLaunch: Boolean,
|
showResumePromptOnLaunch: Boolean,
|
||||||
|
sortMode: ContinueWatchingSortMode,
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
|
|
@ -145,6 +162,39 @@ internal fun LazyListScope.continueWatchingSettingsContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
item {
|
||||||
|
var showSortModeSheet by remember { mutableStateOf(false) }
|
||||||
|
SettingsSection(
|
||||||
|
title = stringResource(Res.string.settings_continue_watching_section_sort_order),
|
||||||
|
isTablet = isTablet,
|
||||||
|
) {
|
||||||
|
SettingsGroup(isTablet = isTablet) {
|
||||||
|
val currentModeLabel = stringResource(
|
||||||
|
when (sortMode) {
|
||||||
|
ContinueWatchingSortMode.DEFAULT -> Res.string.settings_continue_watching_sort_mode_default
|
||||||
|
ContinueWatchingSortMode.STREAMING_STYLE -> Res.string.settings_continue_watching_sort_mode_streaming
|
||||||
|
}
|
||||||
|
)
|
||||||
|
SettingsNavigationRow(
|
||||||
|
title = stringResource(Res.string.settings_continue_watching_sort_mode_title),
|
||||||
|
description = currentModeLabel,
|
||||||
|
isTablet = isTablet,
|
||||||
|
onClick = { showSortModeSheet = true },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showSortModeSheet) {
|
||||||
|
ContinueWatchingSortModeDialog(
|
||||||
|
currentMode = sortMode,
|
||||||
|
onModeSelected = { mode ->
|
||||||
|
ContinueWatchingPreferencesRepository.setSortMode(mode)
|
||||||
|
showSortModeSheet = false
|
||||||
|
},
|
||||||
|
onDismiss = { showSortModeSheet = false },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -250,3 +300,101 @@ private val ContinueWatchingSectionStyle.descriptionRes: StringResource
|
||||||
ContinueWatchingSectionStyle.Wide -> Res.string.settings_continue_watching_style_wide_description
|
ContinueWatchingSectionStyle.Wide -> Res.string.settings_continue_watching_style_wide_description
|
||||||
ContinueWatchingSectionStyle.Poster -> Res.string.settings_continue_watching_style_poster_description
|
ContinueWatchingSectionStyle.Poster -> Res.string.settings_continue_watching_style_poster_description
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun ContinueWatchingSortModeDialog(
|
||||||
|
currentMode: ContinueWatchingSortMode,
|
||||||
|
onModeSelected: (ContinueWatchingSortMode) -> Unit,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
) {
|
||||||
|
val options = listOf(
|
||||||
|
Triple(
|
||||||
|
ContinueWatchingSortMode.DEFAULT,
|
||||||
|
Res.string.settings_continue_watching_sort_mode_default,
|
||||||
|
Res.string.settings_continue_watching_sort_mode_default_desc,
|
||||||
|
),
|
||||||
|
Triple(
|
||||||
|
ContinueWatchingSortMode.STREAMING_STYLE,
|
||||||
|
Res.string.settings_continue_watching_sort_mode_streaming,
|
||||||
|
Res.string.settings_continue_watching_sort_mode_streaming_desc,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
BasicAlertDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
color = MaterialTheme.colorScheme.surface,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(20.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(Res.string.settings_continue_watching_sort_mode_title),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
options.forEach { (mode, titleRes, descriptionRes) ->
|
||||||
|
val isSelected = mode == currentMode
|
||||||
|
val containerColor = if (isSelected) {
|
||||||
|
MaterialTheme.colorScheme.primary.copy(alpha = 0.14f)
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f)
|
||||||
|
}
|
||||||
|
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { onModeSelected(mode) },
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
color = containerColor,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 14.dp, vertical = 12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(titleRes),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(2.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(descriptionRes),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
if (isSelected) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Check,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,7 @@ fun ContinueWatchingSettingsScreen(
|
||||||
showUnairedNextUp = continueWatchingPreferencesUiState.showUnairedNextUp,
|
showUnairedNextUp = continueWatchingPreferencesUiState.showUnairedNextUp,
|
||||||
blurNextUp = continueWatchingPreferencesUiState.blurNextUp,
|
blurNextUp = continueWatchingPreferencesUiState.blurNextUp,
|
||||||
showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch,
|
showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch,
|
||||||
|
sortMode = continueWatchingPreferencesUiState.sortMode,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -490,6 +490,7 @@ private fun MobileSettingsScreen(
|
||||||
showUnairedNextUp = continueWatchingPreferencesUiState.showUnairedNextUp,
|
showUnairedNextUp = continueWatchingPreferencesUiState.showUnairedNextUp,
|
||||||
blurNextUp = continueWatchingPreferencesUiState.blurNextUp,
|
blurNextUp = continueWatchingPreferencesUiState.blurNextUp,
|
||||||
showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch,
|
showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch,
|
||||||
|
sortMode = continueWatchingPreferencesUiState.sortMode,
|
||||||
)
|
)
|
||||||
SettingsPage.PosterCustomization -> posterCustomizationSettingsContent(
|
SettingsPage.PosterCustomization -> posterCustomizationSettingsContent(
|
||||||
isTablet = false,
|
isTablet = false,
|
||||||
|
|
@ -842,6 +843,7 @@ private fun TabletSettingsScreen(
|
||||||
showUnairedNextUp = continueWatchingPreferencesUiState.showUnairedNextUp,
|
showUnairedNextUp = continueWatchingPreferencesUiState.showUnairedNextUp,
|
||||||
blurNextUp = continueWatchingPreferencesUiState.blurNextUp,
|
blurNextUp = continueWatchingPreferencesUiState.blurNextUp,
|
||||||
showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch,
|
showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch,
|
||||||
|
sortMode = continueWatchingPreferencesUiState.sortMode,
|
||||||
)
|
)
|
||||||
SettingsPage.PosterCustomization -> posterCustomizationSettingsContent(
|
SettingsPage.PosterCustomization -> posterCustomizationSettingsContent(
|
||||||
isTablet = true,
|
isTablet = true,
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@ private const val COMMENTS_SORT = "likes"
|
||||||
private const val COMMENTS_LIMIT = 100
|
private const val COMMENTS_LIMIT = 100
|
||||||
private const val COMMENTS_CACHE_TTL_MS = 10 * 60_000L
|
private const val COMMENTS_CACHE_TTL_MS = 10 * 60_000L
|
||||||
private val INLINE_SPOILER_REGEX = Regex(
|
private val INLINE_SPOILER_REGEX = Regex(
|
||||||
"\\[spoiler\\].*?\\[/spoiler\\]",
|
"(?is)\\[spoiler\\].*?\\[/spoiler\\]"
|
||||||
setOf(RegexOption.IGNORE_CASE, RegexOption.DOT_MATCHES_ALL),
|
|
||||||
)
|
)
|
||||||
private val INLINE_SPOILER_TAG_REGEX = Regex("\\[/?spoiler\\]", RegexOption.IGNORE_CASE)
|
private val INLINE_SPOILER_TAG_REGEX = Regex("\\[/?spoiler\\]", RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ private data class StoredContinueWatchingPreferences(
|
||||||
val blurNextUp: Boolean = false,
|
val blurNextUp: Boolean = false,
|
||||||
val dismissedNextUpKeys: Set<String> = emptySet(),
|
val dismissedNextUpKeys: Set<String> = emptySet(),
|
||||||
val showResumePromptOnLaunch: Boolean = true,
|
val showResumePromptOnLaunch: Boolean = true,
|
||||||
|
@SerialName("sort_mode")
|
||||||
|
val sortMode: ContinueWatchingSortMode = ContinueWatchingSortMode.DEFAULT,
|
||||||
)
|
)
|
||||||
|
|
||||||
object ContinueWatchingPreferencesRepository {
|
object ContinueWatchingPreferencesRepository {
|
||||||
|
|
@ -97,6 +99,7 @@ object ContinueWatchingPreferencesRepository {
|
||||||
blurNextUp = stored.blurNextUp,
|
blurNextUp = stored.blurNextUp,
|
||||||
dismissedNextUpKeys = stored.dismissedNextUpKeys,
|
dismissedNextUpKeys = stored.dismissedNextUpKeys,
|
||||||
showResumePromptOnLaunch = stored.showResumePromptOnLaunch,
|
showResumePromptOnLaunch = stored.showResumePromptOnLaunch,
|
||||||
|
sortMode = stored.sortMode,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ContinueWatchingPreferencesUiState()
|
ContinueWatchingPreferencesUiState()
|
||||||
|
|
@ -155,6 +158,13 @@ object ContinueWatchingPreferencesRepository {
|
||||||
persist()
|
persist()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setSortMode(mode: ContinueWatchingSortMode) {
|
||||||
|
ensureLoaded()
|
||||||
|
if (_uiState.value.sortMode == mode) return
|
||||||
|
_uiState.value = _uiState.value.copy(sortMode = mode)
|
||||||
|
persist()
|
||||||
|
}
|
||||||
|
|
||||||
fun removeDismissedNextUpKeysForContent(contentId: String) {
|
fun removeDismissedNextUpKeysForContent(contentId: String) {
|
||||||
ensureLoaded()
|
ensureLoaded()
|
||||||
val normalizedContentId = contentId.trim()
|
val normalizedContentId = contentId.trim()
|
||||||
|
|
@ -178,6 +188,7 @@ object ContinueWatchingPreferencesRepository {
|
||||||
blurNextUp = _uiState.value.blurNextUp,
|
blurNextUp = _uiState.value.blurNextUp,
|
||||||
dismissedNextUpKeys = _uiState.value.dismissedNextUpKeys,
|
dismissedNextUpKeys = _uiState.value.dismissedNextUpKeys,
|
||||||
showResumePromptOnLaunch = _uiState.value.showResumePromptOnLaunch,
|
showResumePromptOnLaunch = _uiState.value.showResumePromptOnLaunch,
|
||||||
|
sortMode = _uiState.value.sortMode,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,12 @@ enum class ContinueWatchingSectionStyle {
|
||||||
Poster,
|
Poster,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class ContinueWatchingSortMode {
|
||||||
|
DEFAULT,
|
||||||
|
STREAMING_STYLE,
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class WatchProgressEntry(
|
data class WatchProgressEntry(
|
||||||
val contentType: String,
|
val contentType: String,
|
||||||
|
|
@ -175,6 +181,7 @@ data class ContinueWatchingPreferencesUiState(
|
||||||
val blurNextUp: Boolean = false,
|
val blurNextUp: Boolean = false,
|
||||||
val dismissedNextUpKeys: Set<String> = emptySet(),
|
val dismissedNextUpKeys: Set<String> = emptySet(),
|
||||||
val showResumePromptOnLaunch: Boolean = true,
|
val showResumePromptOnLaunch: Boolean = true,
|
||||||
|
val sortMode: ContinueWatchingSortMode = ContinueWatchingSortMode.DEFAULT,
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun nextUpDismissKey(
|
internal fun nextUpDismissKey(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue