fix(watching): align next-up release-day behavior

fixes #1684
This commit is contained in:
tapframe 2026-08-04 22:46:56 +05:30
parent ba2ec4a0e8
commit 257e806092
6 changed files with 53 additions and 29 deletions

View file

@ -2072,11 +2072,11 @@
<string name="updates_release_missing_title">Release has no tag or name</string>
<!-- Continue Watching air date display -->
<string name="cw_airs_date">Airs %1$s</string>
<string name="cw_airs_today">Airs today</string>
<string name="cw_airs_tomorrow">Airs tomorrow</string>
<string name="cw_airs_today">Airs Today</string>
<string name="cw_airs_tomorrow">Airs Tomorrow</string>
<plurals name="cw_airs_in_days">
<item quantity="one">Airs in %1$d day</item>
<item quantity="other">Airs in %1$d days</item>
<item quantity="one">Airs in %1$d Day</item>
<item quantity="other">Airs in %1$d Days</item>
</plurals>
<string name="cw_airs_date_short">%1$s</string>
<string name="cw_airs_today_short">Today</string>

View file

@ -638,12 +638,12 @@ private fun ContinueWatchingCard(
)
}
val todayIsoDate = CurrentDateProvider.todayIsoDate()
val compactAirDateText = if (item.progressFraction <= 0f && item.seasonNumber != null && item.episodeNumber != null) {
computeAirDateBadgeText(item.released, todayIsoDate, compact = true)
val airDateText = if (item.progressFraction <= 0f && item.seasonNumber != null && item.episodeNumber != null) {
computeAirDateBadgeText(item.released, todayIsoDate)
} else {
null
}
val preferBackdropForNextUp = item.isNextUp && compactAirDateText != null && !item.isReleaseAlert
val preferBackdropForNextUp = item.isNextUp && airDateText != null && !item.isReleaseAlert
val imageUrl = item.continueWatchingCardArtworkUrl(
useEpisodeThumbnails = useEpisodeThumbnails,
preferBackdropForNextUp = preferBackdropForNextUp,
@ -654,8 +654,8 @@ private fun ContinueWatchingCard(
} else {
null
}
val episodeTitle = item.episodeTitle?.trim()?.takeIf { it.isNotBlank() } ?: compactAirDateText
val badgeText = continueWatchingCardBadgeText(item = item, compactAirDateText = compactAirDateText)
val episodeTitle = item.episodeTitle?.trim()?.takeIf { it.isNotBlank() } ?: airDateText
val badgeText = continueWatchingCardBadgeText(item = item, airDateText = airDateText)
val backgroundColor = MaterialTheme.colorScheme.background
val badgeBackground = when {
item.isNewSeasonRelease -> ContinueWatchingNewSeasonBadgeColor
@ -800,7 +800,7 @@ private fun ContinueWatchingCard(
@Composable
private fun continueWatchingCardBadgeText(
item: ContinueWatchingItem,
compactAirDateText: String?,
airDateText: String?,
): String {
if (item.progressFraction > 0f) {
if (item.durationMs <= 0L) {
@ -827,7 +827,7 @@ private fun continueWatchingCardBadgeText(
return when {
item.isReleaseAlert && item.isNewSeasonRelease -> stringResource(Res.string.cw_new_season)
item.isReleaseAlert -> stringResource(Res.string.cw_new_episode)
compactAirDateText != null -> compactAirDateText
airDateText != null -> airDateText
else -> stringResource(Res.string.home_continue_watching_up_next)
}
}
@ -903,7 +903,7 @@ private fun ContinueWatchingWideCard(
else stringResource(Res.string.cw_new_episode)
}
else -> {
computeAirDateBadgeText(item.released, todayIsoDate, compact = isCompact)
computeAirDateBadgeText(item.released, todayIsoDate)
?: stringResource(Res.string.home_continue_watching_up_next)
}
}
@ -1018,7 +1018,7 @@ private fun ContinueWatchingPosterCard(
else stringResource(Res.string.cw_new_episode)
}
else -> {
computeAirDateBadgeText(item.released, todayIsoDate, compact = true)
computeAirDateBadgeText(item.released, todayIsoDate)
?: stringResource(Res.string.home_continue_watching_up_next)
}
}

View file

@ -58,7 +58,8 @@ internal fun shouldSurfaceNextEpisode(
todayIsoDate = todayIsoDate,
releasedDate = releasedDate,
) ?: return false
if (!showUnairedNextUp || daysUntilRelease <= 0) return false
if (daysUntilRelease <= 0) return true
if (!showUnairedNextUp) return false
return !isSeasonRollover || daysUntilRelease <= UpcomingNextSeasonWindowDays
}
if (!isSeasonRollover) {

View file

@ -13,7 +13,6 @@ import co.touchlab.kermit.Logger
fun computeAirDateBadgeText(
releasedIso: String?,
todayIsoDate: String,
compact: Boolean
): String? {
if (releasedIso.isNullOrBlank() || todayIsoDate.isBlank()) {
return null
@ -31,22 +30,12 @@ fun computeAirDateBadgeText(
return when {
daysUntil < 0 -> null
daysUntil == 0 -> {
if (compact) stringResource(Res.string.cw_airs_today_short)
else stringResource(Res.string.cw_airs_today)
}
daysUntil == 1 -> {
if (compact) stringResource(Res.string.cw_airs_tomorrow_short)
else stringResource(Res.string.cw_airs_tomorrow)
}
daysUntil in 2..7 -> {
if (compact) pluralStringResource(Res.plurals.cw_airs_in_days_short, daysUntil, daysUntil)
else pluralStringResource(Res.plurals.cw_airs_in_days, daysUntil, daysUntil)
}
daysUntil == 0 -> stringResource(Res.string.cw_airs_today)
daysUntil == 1 -> stringResource(Res.string.cw_airs_tomorrow)
daysUntil in 2..7 -> pluralStringResource(Res.plurals.cw_airs_in_days, daysUntil, daysUntil)
else -> {
val formattedDate = formatReleaseDateWithoutYear(releasedIso)
if (compact) stringResource(Res.string.cw_airs_date_short, formattedDate)
else stringResource(Res.string.cw_airs_date, formattedDate)
stringResource(Res.string.cw_airs_date, formattedDate)
}
}
}

View file

@ -285,6 +285,26 @@ class SeriesContinuityTest {
assertEquals("s1e2", nextEpisode.videoId)
}
@Test
fun nextReleasedEpisodeAfter_keeps_dated_unavailable_episode_on_release_day() {
val episodes = listOf(
WatchingReleasedEpisode(videoId = "s1e1", seasonNumber = 1, episodeNumber = 1, title = "Episode 1", releasedDate = "2026-07-01"),
WatchingReleasedEpisode(videoId = "s1e2", seasonNumber = 1, episodeNumber = 2, title = "Episode 2", releasedDate = "2026-07-12T20:00:00", available = false),
)
val nextEpisode = nextReleasedEpisodeAfter(
content = show,
episodes = episodes,
seasonNumber = 1,
episodeNumber = 1,
todayIsoDate = "2026-07-12",
showUnairedNextUp = true,
)
assertNotNull(nextEpisode)
assertEquals("s1e2", nextEpisode.videoId)
}
@Test
fun decideSeriesPrimaryAction_falls_back_to_specials_when_no_main_season() {
val specialsOnly = listOf(

View file

@ -114,6 +114,20 @@ class WatchingPoliciesTest {
)
}
@Test
fun shouldSurfaceNextEpisode_keeps_dated_unavailable_episode_visible_on_release_day() {
assertTrue(
shouldSurfaceNextEpisode(
watchedSeasonNumber = 1,
candidateSeasonNumber = 1,
todayIsoDate = "2026-07-12",
releasedDate = "2026-07-12T20:00:00",
showUnairedNextUp = true,
available = false,
),
)
}
@Test
fun shouldSurfaceNextEpisode_keeps_unavailable_phantom_episode_hidden() {
assertFalse(