mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-03 18:16:15 +00:00
feat; dynamic hero height adjustment mobile
This commit is contained in:
parent
f8e7354bee
commit
19b51c38d3
3 changed files with 71 additions and 1 deletions
|
|
@ -11,6 +11,7 @@ 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.ui.NuvioScreen
|
||||
|
|
@ -44,10 +45,12 @@ import com.nuvio.app.features.watching.domain.buildPlaybackVideoId
|
|||
import com.nuvio.app.features.collection.CollectionRepository
|
||||
import com.nuvio.app.features.home.components.HomeCollectionRowSection
|
||||
import com.nuvio.app.features.watching.domain.isReleasedBy
|
||||
import com.nuvio.app.features.watchprogress.ContinueWatchingSectionStyle
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import com.nuvio.app.features.home.components.ContinueWatchingLayout
|
||||
import com.nuvio.app.features.home.components.homeSectionHorizontalPaddingForWidth
|
||||
import com.nuvio.app.features.home.components.rememberContinueWatchingLayout
|
||||
|
||||
|
|
@ -322,6 +325,21 @@ fun HomeScreen(
|
|||
BoxWithConstraints(modifier = modifier.fillMaxSize()) {
|
||||
val homeSectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value)
|
||||
val continueWatchingLayout = rememberContinueWatchingLayout(maxWidth.value)
|
||||
val mobileHeroBelowSectionHeightHint = remember(
|
||||
maxWidth.value,
|
||||
continueWatchingPreferences.isVisible,
|
||||
continueWatchingPreferences.style,
|
||||
continueWatchingItems.isNotEmpty(),
|
||||
continueWatchingLayout,
|
||||
) {
|
||||
heroMobileBelowSectionHeightHint(
|
||||
maxWidthDp = maxWidth.value,
|
||||
continueWatchingVisible = continueWatchingPreferences.isVisible,
|
||||
hasContinueWatchingItems = continueWatchingItems.isNotEmpty(),
|
||||
continueWatchingStyle = continueWatchingPreferences.style,
|
||||
continueWatchingLayout = continueWatchingLayout,
|
||||
)
|
||||
}
|
||||
|
||||
NuvioScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
|
@ -335,12 +353,14 @@ fun HomeScreen(
|
|||
showHeroSkeleton -> HomeSkeletonHero(
|
||||
modifier = Modifier,
|
||||
viewportHeight = maxHeight,
|
||||
mobileBelowSectionHeightHint = mobileHeroBelowSectionHeightHint,
|
||||
)
|
||||
|
||||
homeUiState.heroItems.isNotEmpty() -> HomeHeroSection(
|
||||
items = homeUiState.heroItems,
|
||||
modifier = Modifier,
|
||||
viewportHeight = maxHeight,
|
||||
mobileBelowSectionHeightHint = mobileHeroBelowSectionHeightHint,
|
||||
listState = homeListState,
|
||||
onItemClick = onPosterClick,
|
||||
)
|
||||
|
|
@ -348,6 +368,7 @@ fun HomeScreen(
|
|||
else -> HomeHeroReservedSpace(
|
||||
modifier = Modifier,
|
||||
viewportHeight = maxHeight,
|
||||
mobileBelowSectionHeightHint = mobileHeroBelowSectionHeightHint,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -467,6 +488,22 @@ fun HomeScreen(
|
|||
private const val HOME_CATALOG_PREVIEW_LIMIT = 18
|
||||
private const val TRAKT_CONTINUE_WATCHING_DAYS_CAP_DEFAULT = 60
|
||||
|
||||
private fun heroMobileBelowSectionHeightHint(
|
||||
maxWidthDp: Float,
|
||||
continueWatchingVisible: Boolean,
|
||||
hasContinueWatchingItems: Boolean,
|
||||
continueWatchingStyle: ContinueWatchingSectionStyle,
|
||||
continueWatchingLayout: ContinueWatchingLayout,
|
||||
): Dp? {
|
||||
if (maxWidthDp >= 600f || !continueWatchingVisible || !hasContinueWatchingItems) return null
|
||||
|
||||
return when (continueWatchingStyle) {
|
||||
ContinueWatchingSectionStyle.Wide -> continueWatchingLayout.wideCardHeight + 56.dp
|
||||
ContinueWatchingSectionStyle.Poster ->
|
||||
continueWatchingLayout.posterCardHeight + continueWatchingLayout.posterTitleBlockHeight + 70.dp
|
||||
}
|
||||
}
|
||||
|
||||
internal fun buildHomeContinueWatchingItems(
|
||||
visibleEntries: List<WatchProgressEntry>,
|
||||
cachedInProgressByVideoId: Map<String, ContinueWatchingItem> = emptyMap(),
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ private const val HERO_SCROLL_UP_SCALE_MULTIPLIER = 0.002f
|
|||
private const val HERO_SCROLL_MAX_SCALE = 1.3f
|
||||
private const val HERO_SWIPE_THRESHOLD_FRACTION = 0.16f
|
||||
private const val HERO_SWIPE_VELOCITY_THRESHOLD = 300f
|
||||
private const val MOBILE_HERO_VIEWPORT_RATIO = 0.78f
|
||||
private const val MOBILE_HERO_MIN_HEIGHT_DP = 360f
|
||||
private const val MOBILE_HERO_MAX_HEIGHT_DP = 760f
|
||||
|
||||
internal data class HomeHeroLayout(
|
||||
val isTablet: Boolean,
|
||||
|
|
@ -80,6 +83,7 @@ fun HomeHeroSection(
|
|||
items: List<MetaPreview>,
|
||||
modifier: Modifier = Modifier,
|
||||
viewportHeight: Dp? = null,
|
||||
mobileBelowSectionHeightHint: Dp? = null,
|
||||
listState: LazyListState? = null,
|
||||
onItemClick: ((MetaPreview) -> Unit)? = null,
|
||||
) {
|
||||
|
|
@ -101,6 +105,7 @@ fun HomeHeroSection(
|
|||
val layout = homeHeroLayout(
|
||||
maxWidthDp = maxWidth.value,
|
||||
viewportHeightDp = viewportHeight?.value,
|
||||
mobileBelowSectionHeightHintDp = mobileBelowSectionHeightHint?.value,
|
||||
)
|
||||
val heroWidthPx = with(LocalDensity.current) { maxWidth.toPx() }
|
||||
val heroHeightPx = with(LocalDensity.current) { layout.heroHeight.toPx() }
|
||||
|
|
@ -313,6 +318,7 @@ private fun heroPageVisibility(
|
|||
fun HomeHeroReservedSpace(
|
||||
modifier: Modifier = Modifier,
|
||||
viewportHeight: Dp? = null,
|
||||
mobileBelowSectionHeightHint: Dp? = null,
|
||||
) {
|
||||
BoxWithConstraints(
|
||||
modifier = modifier
|
||||
|
|
@ -322,6 +328,7 @@ fun HomeHeroReservedSpace(
|
|||
val layout = homeHeroLayout(
|
||||
maxWidthDp = maxWidth.value,
|
||||
viewportHeightDp = viewportHeight?.value,
|
||||
mobileBelowSectionHeightHintDp = mobileBelowSectionHeightHint?.value,
|
||||
)
|
||||
|
||||
Spacer(
|
||||
|
|
@ -414,6 +421,7 @@ private fun HeroMetaText(text: String) {
|
|||
internal fun homeHeroLayout(
|
||||
maxWidthDp: Float,
|
||||
viewportHeightDp: Float? = null,
|
||||
mobileBelowSectionHeightHintDp: Float? = null,
|
||||
): HomeHeroLayout =
|
||||
when {
|
||||
maxWidthDp >= 1200f -> HomeHeroLayout(
|
||||
|
|
@ -448,7 +456,11 @@ internal fun homeHeroLayout(
|
|||
)
|
||||
else -> HomeHeroLayout(
|
||||
isTablet = false,
|
||||
heroHeight = viewportHeightDp?.let { (it * 0.78f).dp } ?: (maxWidthDp * 1.16f).dp.coerceIn(420.dp, 760.dp),
|
||||
heroHeight = mobileHeroHeight(
|
||||
maxWidthDp = maxWidthDp,
|
||||
viewportHeightDp = viewportHeightDp,
|
||||
mobileBelowSectionHeightHintDp = mobileBelowSectionHeightHintDp,
|
||||
),
|
||||
contentMaxWidth = 480.dp,
|
||||
contentWidthFraction = 1f,
|
||||
contentHorizontalPadding = 24.dp,
|
||||
|
|
@ -458,6 +470,25 @@ internal fun homeHeroLayout(
|
|||
)
|
||||
}
|
||||
|
||||
private fun mobileHeroHeight(
|
||||
maxWidthDp: Float,
|
||||
viewportHeightDp: Float?,
|
||||
mobileBelowSectionHeightHintDp: Float?,
|
||||
): Dp {
|
||||
val viewportDrivenHeight = viewportHeightDp?.let { (it * MOBILE_HERO_VIEWPORT_RATIO).dp }
|
||||
val widthFallbackHeight = (maxWidthDp * 1.16f).dp
|
||||
val baseHeight = viewportDrivenHeight ?: widthFallbackHeight
|
||||
|
||||
val cappedHeight = if (viewportHeightDp != null && mobileBelowSectionHeightHintDp != null) {
|
||||
val maxAllowedFromViewport = (viewportHeightDp - mobileBelowSectionHeightHintDp).dp
|
||||
baseHeight.coerceAtMost(maxAllowedFromViewport)
|
||||
} else {
|
||||
baseHeight
|
||||
}
|
||||
|
||||
return cappedHeight.coerceIn(MOBILE_HERO_MIN_HEIGHT_DP.dp, MOBILE_HERO_MAX_HEIGHT_DP.dp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HeroMetaDot() {
|
||||
Box(
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ private fun rememberHomeSkeletonBrush(): Brush {
|
|||
fun HomeSkeletonHero(
|
||||
modifier: Modifier = Modifier,
|
||||
viewportHeight: Dp? = null,
|
||||
mobileBelowSectionHeightHint: Dp? = null,
|
||||
) {
|
||||
val brush = rememberHomeSkeletonBrush()
|
||||
|
||||
|
|
@ -72,6 +73,7 @@ fun HomeSkeletonHero(
|
|||
val layout = homeHeroLayout(
|
||||
maxWidthDp = maxWidth.value,
|
||||
viewportHeightDp = viewportHeight?.value,
|
||||
mobileBelowSectionHeightHintDp = mobileBelowSectionHeightHint?.value,
|
||||
)
|
||||
val containerWidth = maxWidth
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue