mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-24 03:36:51 +00:00
Remove status and progress overlays from TV hero section
Removes eyebrow text, watched status chips, and progress bars from the focused hero component on the TV home screen. The underlying model and UI are simplified to display only the title and basic metadata.
This commit is contained in:
@@ -16,12 +16,8 @@ import kotlin.math.roundToInt
|
|||||||
internal data class TvFocusedHeroModel(
|
internal data class TvFocusedHeroModel(
|
||||||
val id: UUID,
|
val id: UUID,
|
||||||
val backdropImageUrl: String,
|
val backdropImageUrl: String,
|
||||||
val eyebrowText: String,
|
|
||||||
val title: String,
|
val title: String,
|
||||||
val metadata: List<String>,
|
val metadata: List<String>,
|
||||||
val watchedText: String?,
|
|
||||||
val progressFraction: Float?,
|
|
||||||
val progressLabel: String?,
|
|
||||||
) {
|
) {
|
||||||
val metadataText: String?
|
val metadataText: String?
|
||||||
get() = metadata.takeIf { it.isNotEmpty() }?.joinToString(" • ")
|
get() = metadata.takeIf { it.isNotEmpty() }?.joinToString(" • ")
|
||||||
@@ -30,16 +26,16 @@ internal data class TvFocusedHeroModel(
|
|||||||
internal fun FocusableItem.toTvFocusedHeroModel(): TvFocusedHeroModel {
|
internal fun FocusableItem.toTvFocusedHeroModel(): TvFocusedHeroModel {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is ContinueWatchingItem -> when (type) {
|
is ContinueWatchingItem -> when (type) {
|
||||||
BaseItemKind.MOVIE -> movie!!.toTvFocusedHeroModel(sourceLabel = "Continue watching")
|
BaseItemKind.MOVIE -> movie!!.toTvFocusedHeroModel()
|
||||||
BaseItemKind.EPISODE -> episode!!.toTvFocusedHeroModel(sourceLabel = "Continue watching")
|
BaseItemKind.EPISODE -> episode!!.toTvFocusedHeroModel()
|
||||||
else -> unsupportedType(type)
|
else -> unsupportedType(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
is NextUpItem -> episode.toTvFocusedHeroModel(sourceLabel = "Next up")
|
is NextUpItem -> episode.toTvFocusedHeroModel()
|
||||||
is PosterItem -> when (type) {
|
is PosterItem -> when (type) {
|
||||||
BaseItemKind.MOVIE -> movie!!.toTvFocusedHeroModel(sourceLabel = "Movie")
|
BaseItemKind.MOVIE -> movie!!.toTvFocusedHeroModel()
|
||||||
BaseItemKind.EPISODE -> episode!!.toTvFocusedHeroModel(sourceLabel = "Episode")
|
BaseItemKind.EPISODE -> episode!!.toTvFocusedHeroModel()
|
||||||
BaseItemKind.SERIES -> series!!.toTvFocusedHeroModel(sourceLabel = "Series")
|
BaseItemKind.SERIES -> series!!.toTvFocusedHeroModel()
|
||||||
else -> unsupportedType(type)
|
else -> unsupportedType(type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,32 +46,25 @@ internal fun backdropImageUrl(imageUrlPrefix: String?, fallbackImageUrl: String)
|
|||||||
return backdropImageUrl.ifBlank { fallbackImageUrl }
|
return backdropImageUrl.ifBlank { fallbackImageUrl }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Movie.toTvFocusedHeroModel(sourceLabel: String): TvFocusedHeroModel {
|
private fun Movie.toTvFocusedHeroModel(): TvFocusedHeroModel {
|
||||||
val progressFraction = progressFraction(progress)
|
|
||||||
return TvFocusedHeroModel(
|
return TvFocusedHeroModel(
|
||||||
id = id,
|
id = id,
|
||||||
backdropImageUrl = backdropImageUrl(
|
backdropImageUrl = backdropImageUrl(
|
||||||
imageUrlPrefix = imageUrlPrefix,
|
imageUrlPrefix = imageUrlPrefix,
|
||||||
fallbackImageUrl = JellyfinImageHelper.finishImageUrl(imageUrlPrefix, ImageType.PRIMARY)
|
fallbackImageUrl = JellyfinImageHelper.finishImageUrl(imageUrlPrefix, ImageType.PRIMARY)
|
||||||
),
|
),
|
||||||
eyebrowText = sourceLabel,
|
|
||||||
title = title,
|
title = title,
|
||||||
metadata = listOf(year, rating, runtime, format).compactMetadata(),
|
metadata = listOf(year, rating, runtime, format).compactMetadata(),
|
||||||
watchedText = "Watched".takeIf { watched },
|
|
||||||
progressFraction = progressFraction.takeIf { !watched && it != null },
|
|
||||||
progressLabel = progressLabel(progressFraction).takeIf { !watched && progressFraction != null },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Episode.toTvFocusedHeroModel(sourceLabel: String): TvFocusedHeroModel {
|
private fun Episode.toTvFocusedHeroModel(): TvFocusedHeroModel {
|
||||||
val progressFraction = progressFraction(progress)
|
|
||||||
return TvFocusedHeroModel(
|
return TvFocusedHeroModel(
|
||||||
id = id,
|
id = id,
|
||||||
backdropImageUrl = backdropImageUrl(
|
backdropImageUrl = backdropImageUrl(
|
||||||
imageUrlPrefix = imageUrlPrefix,
|
imageUrlPrefix = imageUrlPrefix,
|
||||||
fallbackImageUrl = JellyfinImageHelper.finishImageUrl(imageUrlPrefix, ImageType.PRIMARY)
|
fallbackImageUrl = JellyfinImageHelper.finishImageUrl(imageUrlPrefix, ImageType.PRIMARY)
|
||||||
),
|
),
|
||||||
eyebrowText = sourceLabel,
|
|
||||||
title = title,
|
title = title,
|
||||||
metadata = listOf(
|
metadata = listOf(
|
||||||
"Episode $index",
|
"Episode $index",
|
||||||
@@ -84,13 +73,10 @@ private fun Episode.toTvFocusedHeroModel(sourceLabel: String): TvFocusedHeroMode
|
|||||||
rating,
|
rating,
|
||||||
format
|
format
|
||||||
).compactMetadata(),
|
).compactMetadata(),
|
||||||
watchedText = "Watched".takeIf { watched },
|
|
||||||
progressFraction = progressFraction.takeIf { !watched && it != null },
|
|
||||||
progressLabel = progressLabel(progressFraction).takeIf { !watched && progressFraction != null },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Series.toTvFocusedHeroModel(sourceLabel: String): TvFocusedHeroModel {
|
private fun Series.toTvFocusedHeroModel(): TvFocusedHeroModel {
|
||||||
val unwatchedText = if (unwatchedEpisodeCount > 0) {
|
val unwatchedText = if (unwatchedEpisodeCount > 0) {
|
||||||
"$unwatchedEpisodeCount unwatched"
|
"$unwatchedEpisodeCount unwatched"
|
||||||
} else {
|
} else {
|
||||||
@@ -103,12 +89,8 @@ private fun Series.toTvFocusedHeroModel(sourceLabel: String): TvFocusedHeroModel
|
|||||||
imageUrlPrefix = imageUrlPrefix,
|
imageUrlPrefix = imageUrlPrefix,
|
||||||
fallbackImageUrl = JellyfinImageHelper.finishImageUrl(imageUrlPrefix, ImageType.PRIMARY)
|
fallbackImageUrl = JellyfinImageHelper.finishImageUrl(imageUrlPrefix, ImageType.PRIMARY)
|
||||||
),
|
),
|
||||||
eyebrowText = sourceLabel,
|
|
||||||
title = name,
|
title = name,
|
||||||
metadata = listOf(year, seasonLabel(seasonCount), unwatchedText).compactMetadata(),
|
metadata = listOf(year, seasonLabel(seasonCount), unwatchedText).compactMetadata(),
|
||||||
watchedText = null,
|
|
||||||
progressFraction = null,
|
|
||||||
progressLabel = null,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,39 +3,29 @@ package hu.bbara.purefin.tv.home.ui
|
|||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
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.width
|
|
||||||
import androidx.compose.foundation.layout.widthIn
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import hu.bbara.purefin.common.ui.components.MediaProgressBar
|
|
||||||
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
|
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
|
||||||
|
|
||||||
internal const val TvHomeHeroTitleTag = "tv-home-hero-title"
|
internal const val TvHomeHeroTitleTag = "tv-home-hero-title"
|
||||||
internal const val TvHomeHeroStatusTag = "tv-home-hero-status"
|
|
||||||
internal const val TvHomeHeroProgressLabelTag = "tv-home-hero-progress-label"
|
internal const val TvHomeHeroProgressLabelTag = "tv-home-hero-progress-label"
|
||||||
|
|
||||||
private const val TvHomeHeroAnimationMillis = 180
|
private const val TvHomeHeroAnimationMillis = 180
|
||||||
@@ -107,13 +97,6 @@ internal fun TvFocusedItemHero(
|
|||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier = Modifier.widthIn(max = 720.dp)
|
modifier = Modifier.widthIn(max = 720.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
|
||||||
text = hero.eyebrowText.uppercase(),
|
|
||||||
color = scheme.primary,
|
|
||||||
fontSize = 10.sp,
|
|
||||||
letterSpacing = 1.2.sp,
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
Text(
|
Text(
|
||||||
text = hero.title,
|
text = hero.title,
|
||||||
color = scheme.onBackground,
|
color = scheme.onBackground,
|
||||||
@@ -134,54 +117,6 @@ internal fun TvFocusedItemHero(
|
|||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (hero.watchedText != null || hero.progressFraction != null) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
|
||||||
modifier = Modifier
|
|
||||||
.clip(RoundedCornerShape(22.dp))
|
|
||||||
.background(scheme.surfaceContainerHigh.copy(alpha = 0.92f))
|
|
||||||
.border(1.dp, scheme.outlineVariant.copy(alpha = 0.6f), RoundedCornerShape(22.dp))
|
|
||||||
.padding(horizontal = 12.dp, vertical = 10.dp)
|
|
||||||
) {
|
|
||||||
hero.watchedText?.let { watchedText ->
|
|
||||||
TvHomeMetaChip(
|
|
||||||
text = watchedText,
|
|
||||||
highlighted = true,
|
|
||||||
modifier = Modifier.testTag(TvHomeHeroStatusTag)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (hero.progressFraction != null && hero.progressLabel != null) {
|
|
||||||
Column(
|
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
|
||||||
modifier = Modifier.width(188.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "Progress",
|
|
||||||
color = scheme.onSurfaceVariant.copy(alpha = 0.85f),
|
|
||||||
fontSize = 10.sp,
|
|
||||||
fontWeight = FontWeight.Medium
|
|
||||||
)
|
|
||||||
MediaProgressBar(
|
|
||||||
progress = hero.progressFraction,
|
|
||||||
foregroundColor = scheme.primary,
|
|
||||||
backgroundColor = scheme.surfaceVariant,
|
|
||||||
contentPadding = PaddingValues(0.dp),
|
|
||||||
barHeight = 6.dp,
|
|
||||||
modifier = Modifier
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Text(
|
|
||||||
text = hero.progressLabel,
|
|
||||||
color = scheme.onBackground,
|
|
||||||
fontSize = 14.sp,
|
|
||||||
fontWeight = FontWeight.SemiBold,
|
|
||||||
textAlign = TextAlign.End,
|
|
||||||
modifier = Modifier.testTag(TvHomeHeroProgressLabelTag)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user