Updates to series overview

This commit is contained in:
Damontecres 2025-09-30 11:44:51 -04:00
parent 31b4881d7c
commit f96ed850b7
No known key found for this signature in database
5 changed files with 83 additions and 37 deletions

View file

@ -5,6 +5,8 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
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.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
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.layout.size
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -12,6 +14,8 @@ import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.clip
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -25,13 +29,14 @@ import com.github.damontecres.dolphin.ui.isNotNullOrBlank
@Composable @Composable
fun BannerCard( fun BannerCard(
imageUrl: String, imageUrl: String?,
cornerText: String?, cornerText: String?,
played: Boolean, played: Boolean,
playPercent: Double,
onClick: () -> Unit, onClick: () -> Unit,
onLongClick: () -> Unit, onLongClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
cardWidth: Dp = 160.dp, cardWidth: Dp = 200.dp,
cardHeight: Dp = cardWidth * 9 / 16, cardHeight: Dp = cardWidth * 9 / 16,
interactionSource: MutableInteractionSource? = null, interactionSource: MutableInteractionSource? = null,
) { ) {
@ -52,7 +57,10 @@ fun BannerCard(
Row( Row(
horizontalArrangement = Arrangement.spacedBy(4.dp), horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.align(Alignment.TopEnd), modifier =
Modifier
.align(Alignment.TopEnd)
.padding(4.dp),
) { ) {
if (played) { if (played) {
Icon( Icon(
@ -61,20 +69,37 @@ fun BannerCard(
tint = MaterialTheme.colorScheme.border.copy(alpha = 1f), tint = MaterialTheme.colorScheme.border.copy(alpha = 1f),
modifier = modifier =
Modifier Modifier
.size(48.dp) .size(24.dp),
.padding(8.dp),
) )
} }
if (cornerText.isNotNullOrBlank()) { if (cornerText.isNotNullOrBlank()) {
Text( Box(
text = cornerText, modifier =
style = MaterialTheme.typography.bodySmall, Modifier
color = MaterialTheme.colorScheme.onSurface, .background(AppColors.TransparentBlack50),
modifier = Modifier.background(AppColors.TransparentBlack50), ) {
) Text(
text = cornerText,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(4.dp),
)
}
} }
} }
} }
if (playPercent > 0 && playPercent < 100) {
Box(
modifier =
Modifier
.align(Alignment.BottomStart)
.background(
MaterialTheme.colorScheme.tertiary,
).clip(RectangleShape)
.height(4.dp)
.fillMaxWidth((playPercent / 100).toFloat()),
)
}
} }
} }
} }

View file

@ -103,6 +103,7 @@ class SeriesViewModel
ItemFields.PRIMARY_IMAGE_ASPECT_RATIO, ItemFields.PRIMARY_IMAGE_ASPECT_RATIO,
ItemFields.CHILD_COUNT, ItemFields.CHILD_COUNT,
ItemFields.MEDIA_STREAMS, ItemFields.MEDIA_STREAMS,
ItemFields.OVERVIEW,
), ),
) )
val pager = ItemPager(api, request, viewModelScope) val pager = ItemPager(api, request, viewModelScope)

View file

@ -117,7 +117,7 @@ fun FocusedEpisodeFooter(
} }
Row( Row(
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
) { ) {
dto.mediaStreams dto.mediaStreams
?.firstOrNull { it.type == MediaStreamType.VIDEO } ?.firstOrNull { it.type == MediaStreamType.VIDEO }
@ -133,12 +133,17 @@ fun FocusedEpisodeFooter(
dto.mediaStreams dto.mediaStreams
?.firstOrNull { it.type == MediaStreamType.AUDIO } ?.firstOrNull { it.type == MediaStreamType.AUDIO }
?.let { stream -> ?.let { stream ->
stream.displayTitle?.let { // TODO probably a cleaner way to do this
TitleValueText( // Removes part of "5.1 Surround - English - AAC - Default"
"Audio", stream.displayTitle
it, ?.replace(" - Default", "")
) ?.ifBlank { null }
} ?.let {
TitleValueText(
"Audio",
it,
)
}
} }
dto.mediaStreams dto.mediaStreams

View file

@ -45,7 +45,7 @@ fun FocusedEpisodeHeader(
) { ) {
Text( Text(
text = dto.episodeTitle ?: dto.name ?: "", text = dto.episodeTitle ?: dto.name ?: "",
style = MaterialTheme.typography.headlineMedium, style = MaterialTheme.typography.headlineSmall,
modifier = Modifier, modifier = Modifier,
) )
Row( Row(
@ -64,18 +64,20 @@ fun FocusedEpisodeHeader(
} }
DotSeparatedRow( DotSeparatedRow(
texts = details, texts = details,
textStyle = MaterialTheme.typography.titleLarge, textStyle = MaterialTheme.typography.titleSmall,
) )
} }
Row( Row(
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Text( dto.criticRating?.let {
text = "Critic: ${dto.criticRating}", Text(
style = MaterialTheme.typography.bodySmall, text = "Critic: ${dto.criticRating}",
modifier = Modifier, style = MaterialTheme.typography.bodySmall,
) modifier = Modifier,
)
}
StarRating( StarRating(
rating100 = rating100 =
dto dto
@ -115,11 +117,14 @@ fun FocusedEpisodeHeader(
) { ) {
Text( Text(
text = overview, text = overview,
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
maxLines = 3, maxLines = 3,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(8.dp), modifier =
Modifier
.padding(8.dp)
.height(60.dp),
) )
} }
} }

View file

@ -24,6 +24,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue 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.drawWithContent import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
@ -38,7 +39,7 @@ import androidx.tv.material3.TabRow
import androidx.tv.material3.Text import androidx.tv.material3.Text
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import com.github.damontecres.dolphin.data.model.BaseItem import com.github.damontecres.dolphin.data.model.BaseItem
import com.github.damontecres.dolphin.ui.cards.ItemCard import com.github.damontecres.dolphin.ui.cards.BannerCard
import com.github.damontecres.dolphin.ui.ifElse import com.github.damontecres.dolphin.ui.ifElse
import com.github.damontecres.dolphin.ui.isNotNullOrBlank import com.github.damontecres.dolphin.ui.isNotNullOrBlank
@ -81,7 +82,8 @@ fun SeriesOverviewContent(
alignment = Alignment.TopEnd, alignment = Alignment.TopEnd,
modifier = modifier =
Modifier Modifier
.fillMaxHeight(.5f) .fillMaxHeight(.6f)
.alpha(.4f)
.drawWithContent { .drawWithContent {
drawContent() drawContent()
drawRect( drawRect(
@ -110,8 +112,10 @@ fun SeriesOverviewContent(
selectedTabIndex = selectedTabIndex, selectedTabIndex = selectedTabIndex,
modifier = modifier =
Modifier Modifier
.ifElse(focusRequesters.size > selectedTabIndex, { Modifier.focusRestorer(focusRequesters[selectedTabIndex]) }) .ifElse(
.focusRequester(tabRowFocusRequester) focusRequesters.size > selectedTabIndex,
{ Modifier.focusRestorer(focusRequesters[selectedTabIndex]) },
).focusRequester(tabRowFocusRequester)
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
.fillMaxWidth(), .fillMaxWidth(),
) { ) {
@ -142,7 +146,7 @@ fun SeriesOverviewContent(
series.name?.let { series.name?.let {
Text( Text(
text = it, text = it,
style = MaterialTheme.typography.headlineLarge, style = MaterialTheme.typography.headlineMedium,
modifier = Modifier, modifier = Modifier,
) )
} }
@ -155,7 +159,7 @@ fun SeriesOverviewContent(
overviewOnClick = { overviewOnClick = {
// TODO show full overview dialog // TODO show full overview dialog
}, },
modifier = Modifier, modifier = Modifier.fillMaxWidth(.66f),
) )
} }
} }
@ -165,7 +169,7 @@ fun SeriesOverviewContent(
LazyRow( LazyRow(
state = state, state = state,
horizontalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(start = 32.dp), contentPadding = PaddingValues(start = 16.dp),
modifier = modifier.focusRestorer(firstItemFocusRequester), modifier = modifier.focusRestorer(firstItemFocusRequester),
) { ) {
itemsIndexed(episodes) { index, episode -> itemsIndexed(episodes) { index, episode ->
@ -173,8 +177,11 @@ fun SeriesOverviewContent(
if (interactionSource.collectIsFocusedAsState().value) { if (interactionSource.collectIsFocusedAsState().value) {
onFocus.invoke(SeasonEpisode(selectedTabIndex, index)) onFocus.invoke(SeasonEpisode(selectedTabIndex, index))
} }
ItemCard( BannerCard(
item = episode, imageUrl = episode?.imageUrl,
cornerText = "E${episode?.data?.indexNumber}",
played = episode?.data?.userData?.played ?: false,
playPercent = episode?.data?.userData?.playedPercentage ?: 0.0,
onClick = { if (episode != null) onClick.invoke(episode) }, onClick = { if (episode != null) onClick.invoke(episode) },
onLongClick = { if (episode != null) onLongClick.invoke(episode) }, onLongClick = { if (episode != null) onLongClick.invoke(episode) },
modifier = modifier =
@ -195,7 +202,10 @@ fun SeriesOverviewContent(
playOnClick = {}, playOnClick = {},
moreOnClick = {}, moreOnClick = {},
watchOnClick = {}, watchOnClick = {},
modifier = Modifier.fillMaxWidth(), modifier =
Modifier
.fillMaxWidth()
.padding(start = 16.dp),
) )
} }
} }