mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-01 17:16:11 +00:00
fix: keep home hero clear of continue watching
This commit is contained in:
parent
aeb5e04321
commit
cafb21dbe8
4 changed files with 114 additions and 44 deletions
|
|
@ -11,7 +11,6 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.nuvio.app.core.network.NetworkCondition
|
||||
|
|
@ -39,6 +38,7 @@ import com.nuvio.app.features.home.components.HomeHeroReservedSpace
|
|||
import com.nuvio.app.features.home.components.HomeHeroSection
|
||||
import com.nuvio.app.features.home.components.HomeSkeletonHero
|
||||
import com.nuvio.app.features.home.components.HomeSkeletonRow
|
||||
import com.nuvio.app.features.home.components.HomeContinueWatchingSectionBottomPadding
|
||||
import com.nuvio.app.features.trakt.TraktAuthRepository
|
||||
import com.nuvio.app.features.trakt.TRAKT_CONTINUE_WATCHING_DAYS_CAP_ALL
|
||||
import com.nuvio.app.features.trakt.TraktSettingsRepository
|
||||
|
|
@ -74,7 +74,6 @@ import com.nuvio.app.features.watching.domain.isReleasedBy
|
|||
import com.nuvio.app.features.collection.CollectionRepository
|
||||
import com.nuvio.app.features.profiles.ProfileRepository
|
||||
import com.nuvio.app.features.home.components.HomeCollectionRowSection
|
||||
import com.nuvio.app.features.watchprogress.ContinueWatchingSectionStyle
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.async
|
||||
|
|
@ -85,8 +84,7 @@ import kotlinx.coroutines.sync.Semaphore
|
|||
import kotlinx.coroutines.sync.withPermit
|
||||
import kotlinx.coroutines.yield
|
||||
import com.nuvio.app.features.trakt.TraktEpisodeMappingService
|
||||
import com.nuvio.app.features.home.components.ContinueWatchingLayout
|
||||
import com.nuvio.app.features.home.components.continueWatchingLandscapeCardHeight
|
||||
import com.nuvio.app.features.home.components.continueWatchingHeroViewportReserveHeight
|
||||
import com.nuvio.app.features.home.components.homeSectionHorizontalPaddingForWidth
|
||||
import com.nuvio.app.features.home.components.rememberContinueWatchingLayout
|
||||
import kotlinx.coroutines.CancellationException
|
||||
|
|
@ -693,9 +691,6 @@ fun HomeScreen(
|
|||
val homeSectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value)
|
||||
val continueWatchingLayout = rememberContinueWatchingLayout(maxWidth.value)
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
val continueWatchingCardHeight = remember(posterCardStyle.widthDp) {
|
||||
continueWatchingLandscapeCardHeight(posterCardStyle.widthDp)
|
||||
}
|
||||
val nativeBottomNavigationOverlayHeight =
|
||||
if (LocalNuvioBottomNavigationOverlayPadding.current > 0.dp) {
|
||||
nuvioSafeBottomPadding()
|
||||
|
|
@ -708,18 +703,24 @@ fun HomeScreen(
|
|||
continueWatchingPreferences.style,
|
||||
continueWatchingItems.isNotEmpty(),
|
||||
continueWatchingLayout,
|
||||
continueWatchingCardHeight,
|
||||
posterCardStyle.widthDp,
|
||||
homeSettingsUiState.hideCatalogUnderline,
|
||||
nativeBottomNavigationOverlayHeight,
|
||||
) {
|
||||
heroMobileBelowSectionHeightHint(
|
||||
maxWidthDp = maxWidth.value,
|
||||
continueWatchingVisible = continueWatchingPreferences.isVisible,
|
||||
hasContinueWatchingItems = continueWatchingItems.isNotEmpty(),
|
||||
continueWatchingStyle = continueWatchingPreferences.style,
|
||||
continueWatchingLayout = continueWatchingLayout,
|
||||
continueWatchingCardHeight = continueWatchingCardHeight,
|
||||
bottomNavigationOverlayHeight = nativeBottomNavigationOverlayHeight,
|
||||
)
|
||||
if (
|
||||
maxWidth.value < 600f &&
|
||||
continueWatchingPreferences.isVisible &&
|
||||
continueWatchingItems.isNotEmpty()
|
||||
) {
|
||||
continueWatchingHeroViewportReserveHeight(
|
||||
style = continueWatchingPreferences.style,
|
||||
layout = continueWatchingLayout,
|
||||
basePosterWidthDp = posterCardStyle.widthDp,
|
||||
showHeaderAccent = !homeSettingsUiState.hideCatalogUnderline,
|
||||
) + nativeBottomNavigationOverlayHeight
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
NuvioScreen(
|
||||
|
|
@ -764,7 +765,7 @@ fun HomeScreen(
|
|||
style = continueWatchingPreferences.style,
|
||||
useEpisodeThumbnails = continueWatchingPreferences.useEpisodeThumbnails,
|
||||
blurNextUp = continueWatchingPreferences.blurNextUp,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
modifier = Modifier.padding(bottom = HomeContinueWatchingSectionBottomPadding),
|
||||
sectionPadding = homeSectionPadding,
|
||||
layout = continueWatchingLayout,
|
||||
onItemClick = onContinueWatchingClick,
|
||||
|
|
@ -789,7 +790,7 @@ fun HomeScreen(
|
|||
style = continueWatchingPreferences.style,
|
||||
useEpisodeThumbnails = continueWatchingPreferences.useEpisodeThumbnails,
|
||||
blurNextUp = continueWatchingPreferences.blurNextUp,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
modifier = Modifier.padding(bottom = HomeContinueWatchingSectionBottomPadding),
|
||||
sectionPadding = homeSectionPadding,
|
||||
layout = continueWatchingLayout,
|
||||
onItemClick = onContinueWatchingClick,
|
||||
|
|
@ -837,7 +838,7 @@ fun HomeScreen(
|
|||
style = continueWatchingPreferences.style,
|
||||
useEpisodeThumbnails = continueWatchingPreferences.useEpisodeThumbnails,
|
||||
blurNextUp = continueWatchingPreferences.blurNextUp,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
modifier = Modifier.padding(bottom = HomeContinueWatchingSectionBottomPadding),
|
||||
sectionPadding = homeSectionPadding,
|
||||
layout = continueWatchingLayout,
|
||||
onItemClick = onContinueWatchingClick,
|
||||
|
|
@ -1168,26 +1169,6 @@ private fun shouldTreatAsActiveInProgressForNextUpSuppression(
|
|||
return progress.lastUpdatedEpochMs >= latestCompletedAt
|
||||
}
|
||||
|
||||
private fun heroMobileBelowSectionHeightHint(
|
||||
maxWidthDp: Float,
|
||||
continueWatchingVisible: Boolean,
|
||||
hasContinueWatchingItems: Boolean,
|
||||
continueWatchingStyle: ContinueWatchingSectionStyle,
|
||||
continueWatchingLayout: ContinueWatchingLayout,
|
||||
continueWatchingCardHeight: Dp,
|
||||
bottomNavigationOverlayHeight: Dp,
|
||||
): Dp? {
|
||||
if (maxWidthDp >= 600f || !continueWatchingVisible || !hasContinueWatchingItems) return null
|
||||
|
||||
val sectionHeight = when (continueWatchingStyle) {
|
||||
ContinueWatchingSectionStyle.Card -> continueWatchingCardHeight + 56.dp
|
||||
ContinueWatchingSectionStyle.Wide -> continueWatchingLayout.wideCardHeight + 56.dp
|
||||
ContinueWatchingSectionStyle.Poster ->
|
||||
continueWatchingLayout.posterCardHeight + continueWatchingLayout.posterTitleBlockHeight + 70.dp
|
||||
}
|
||||
return sectionHeight + bottomNavigationOverlayHeight
|
||||
}
|
||||
|
||||
internal fun buildHomeContinueWatchingItems(
|
||||
visibleEntries: List<WatchProgressEntry>,
|
||||
cachedInProgressByVideoId: Map<String, ContinueWatchingItem> = emptyMap(),
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import coil3.compose.AsyncImage
|
|||
import com.nuvio.app.core.ui.DisintegratingContainer
|
||||
import com.nuvio.app.core.ui.NuvioProgressBar
|
||||
import com.nuvio.app.core.ui.NuvioShelfSection
|
||||
import com.nuvio.app.core.ui.NuvioTokens
|
||||
import com.nuvio.app.core.ui.PosterLandscapeAspectRatio
|
||||
import com.nuvio.app.core.ui.landscapePosterHeightForWidth
|
||||
import com.nuvio.app.core.ui.landscapePosterWidth
|
||||
|
|
@ -70,6 +71,7 @@ private val ContinueWatchingStatusBadgeShape = RoundedCornerShape(4.dp)
|
|||
private val ContinueWatchingNewEpisodeBadgeColor = Color(0xFF1D4ED8)
|
||||
private val ContinueWatchingNewSeasonBadgeColor = Color(0xFFB45309)
|
||||
private const val ContinueWatchingLandscapeCardScale = 1.2f
|
||||
internal val HomeContinueWatchingSectionBottomPadding = 12.dp
|
||||
|
||||
internal fun continueWatchingLandscapeCardWidth(basePosterWidthDp: Int): Dp =
|
||||
(landscapePosterWidth(basePosterWidthDp).value * ContinueWatchingLandscapeCardScale).dp
|
||||
|
|
@ -77,6 +79,45 @@ internal fun continueWatchingLandscapeCardWidth(basePosterWidthDp: Int): Dp =
|
|||
internal fun continueWatchingLandscapeCardHeight(basePosterWidthDp: Int): Dp =
|
||||
landscapePosterHeightForWidth(continueWatchingLandscapeCardWidth(basePosterWidthDp))
|
||||
|
||||
internal fun continueWatchingSectionHeightEstimate(
|
||||
style: ContinueWatchingSectionStyle,
|
||||
layout: ContinueWatchingLayout,
|
||||
basePosterWidthDp: Int,
|
||||
showHeaderAccent: Boolean,
|
||||
): Dp {
|
||||
val headerHeight = NuvioTokens.Space.s40 + if (showHeaderAccent) {
|
||||
NuvioTokens.Space.s6 + NuvioTokens.Space.s4
|
||||
} else {
|
||||
0.dp
|
||||
}
|
||||
val headerToRowGap = NuvioTokens.Space.s8 + NuvioTokens.Space.s2
|
||||
val rowHeight = when (style) {
|
||||
ContinueWatchingSectionStyle.Card -> continueWatchingLandscapeCardHeight(basePosterWidthDp)
|
||||
ContinueWatchingSectionStyle.Wide -> layout.wideCardHeight
|
||||
ContinueWatchingSectionStyle.Poster -> layout.posterCardHeight + layout.posterTitleBlockHeight
|
||||
}
|
||||
return headerHeight + headerToRowGap + rowHeight + HomeContinueWatchingSectionBottomPadding
|
||||
}
|
||||
|
||||
internal fun continueWatchingHeroViewportReserveHeight(
|
||||
style: ContinueWatchingSectionStyle,
|
||||
layout: ContinueWatchingLayout,
|
||||
basePosterWidthDp: Int,
|
||||
showHeaderAccent: Boolean,
|
||||
): Dp {
|
||||
val bottomNavigationClearance = when (style) {
|
||||
ContinueWatchingSectionStyle.Card,
|
||||
ContinueWatchingSectionStyle.Wide -> NuvioTokens.Space.s24
|
||||
ContinueWatchingSectionStyle.Poster -> 0.dp
|
||||
}
|
||||
return continueWatchingSectionHeightEstimate(
|
||||
style = style,
|
||||
layout = layout,
|
||||
basePosterWidthDp = basePosterWidthDp,
|
||||
showHeaderAccent = showHeaderAccent,
|
||||
) + bottomNavigationClearance
|
||||
}
|
||||
|
||||
private fun continueWatchingProgressPercent(progressFraction: Float): Int =
|
||||
(progressFraction * 100f).roundToInt().coerceIn(1, 99)
|
||||
|
||||
|
|
|
|||
|
|
@ -489,14 +489,24 @@ private fun mobileHeroHeight(
|
|||
val widthFallbackHeight = (maxWidthDp * 1.16f).dp
|
||||
val baseHeight = viewportDrivenHeight ?: widthFallbackHeight
|
||||
|
||||
val cappedHeight = if (viewportHeightDp != null && mobileBelowSectionHeightHintDp != null) {
|
||||
val maxAllowedFromViewport = (viewportHeightDp - mobileBelowSectionHeightHintDp).dp
|
||||
val maxAllowedFromViewportDp = if (viewportHeightDp != null && mobileBelowSectionHeightHintDp != null) {
|
||||
viewportHeightDp - mobileBelowSectionHeightHintDp
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val cappedHeight = if (maxAllowedFromViewportDp != null) {
|
||||
val maxAllowedFromViewport = maxAllowedFromViewportDp.dp
|
||||
baseHeight.coerceAtMost(maxAllowedFromViewport)
|
||||
} else {
|
||||
baseHeight
|
||||
}
|
||||
val minHeight = if (maxAllowedFromViewportDp != null) {
|
||||
minOf(MOBILE_HERO_MIN_HEIGHT_DP, maxAllowedFromViewportDp.coerceAtLeast(0f)).dp
|
||||
} else {
|
||||
MOBILE_HERO_MIN_HEIGHT_DP.dp
|
||||
}
|
||||
|
||||
return cappedHeight.coerceIn(MOBILE_HERO_MIN_HEIGHT_DP.dp, MOBILE_HERO_MAX_HEIGHT_DP.dp)
|
||||
return cappedHeight.coerceIn(minHeight, MOBILE_HERO_MAX_HEIGHT_DP.dp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.nuvio.app.features.home.components
|
||||
|
||||
import com.nuvio.app.features.watchprogress.ContinueWatchingSectionStyle
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
|
@ -26,4 +27,41 @@ class HomeHeroSectionTest {
|
|||
assertEquals(true, layout.isTablet)
|
||||
assertEquals(386.4f, layout.heroHeight.value, 0.001f)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mobile hero height leaves room for continue watching card section`() {
|
||||
val viewportHeight = 844f
|
||||
val continueWatchingLayout = rememberContinueWatchingLayout(maxWidthDp = 390f)
|
||||
val continueWatchingHeight = continueWatchingSectionHeightEstimate(
|
||||
style = ContinueWatchingSectionStyle.Card,
|
||||
layout = continueWatchingLayout,
|
||||
basePosterWidthDp = 110,
|
||||
showHeaderAccent = true,
|
||||
)
|
||||
val reserveHeight = continueWatchingHeroViewportReserveHeight(
|
||||
style = ContinueWatchingSectionStyle.Card,
|
||||
layout = continueWatchingLayout,
|
||||
basePosterWidthDp = 110,
|
||||
showHeaderAccent = true,
|
||||
)
|
||||
val layout = homeHeroLayout(
|
||||
maxWidthDp = 390f,
|
||||
viewportHeightDp = viewportHeight,
|
||||
mobileBelowSectionHeightHintDp = reserveHeight.value,
|
||||
)
|
||||
|
||||
assertEquals(24f, viewportHeight - layout.heroHeight.value - continueWatchingHeight.value, 0.001f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mobile hero can shrink below default minimum to fit short viewport`() {
|
||||
val layout = homeHeroLayout(
|
||||
maxWidthDp = 390f,
|
||||
viewportHeightDp = 568f,
|
||||
mobileBelowSectionHeightHintDp = 300f,
|
||||
)
|
||||
|
||||
assertEquals(false, layout.isTablet)
|
||||
assertEquals(268f, layout.heroHeight.value, 0.001f)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue