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.Box
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.size
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.ui.Alignment
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.unit.Dp
import androidx.compose.ui.unit.dp
@ -25,13 +29,14 @@ import com.github.damontecres.dolphin.ui.isNotNullOrBlank
@Composable
fun BannerCard(
imageUrl: String,
imageUrl: String?,
cornerText: String?,
played: Boolean,
playPercent: Double,
onClick: () -> Unit,
onLongClick: () -> Unit,
modifier: Modifier = Modifier,
cardWidth: Dp = 160.dp,
cardWidth: Dp = 200.dp,
cardHeight: Dp = cardWidth * 9 / 16,
interactionSource: MutableInteractionSource? = null,
) {
@ -52,7 +57,10 @@ fun BannerCard(
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.align(Alignment.TopEnd),
modifier =
Modifier
.align(Alignment.TopEnd)
.padding(4.dp),
) {
if (played) {
Icon(
@ -61,20 +69,37 @@ fun BannerCard(
tint = MaterialTheme.colorScheme.border.copy(alpha = 1f),
modifier =
Modifier
.size(48.dp)
.padding(8.dp),
.size(24.dp),
)
}
if (cornerText.isNotNullOrBlank()) {
Text(
text = cornerText,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.background(AppColors.TransparentBlack50),
)
Box(
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.CHILD_COUNT,
ItemFields.MEDIA_STREAMS,
ItemFields.OVERVIEW,
),
)
val pager = ItemPager(api, request, viewModelScope)

View file

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

View file

@ -45,7 +45,7 @@ fun FocusedEpisodeHeader(
) {
Text(
text = dto.episodeTitle ?: dto.name ?: "",
style = MaterialTheme.typography.headlineMedium,
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier,
)
Row(
@ -64,18 +64,20 @@ fun FocusedEpisodeHeader(
}
DotSeparatedRow(
texts = details,
textStyle = MaterialTheme.typography.titleLarge,
textStyle = MaterialTheme.typography.titleSmall,
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "Critic: ${dto.criticRating}",
style = MaterialTheme.typography.bodySmall,
modifier = Modifier,
)
dto.criticRating?.let {
Text(
text = "Critic: ${dto.criticRating}",
style = MaterialTheme.typography.bodySmall,
modifier = Modifier,
)
}
StarRating(
rating100 =
dto
@ -115,11 +117,14 @@ fun FocusedEpisodeHeader(
) {
Text(
text = overview,
style = MaterialTheme.typography.bodyLarge,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface,
maxLines = 3,
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.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.drawWithContent
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 coil3.compose.AsyncImage
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.isNotNullOrBlank
@ -81,7 +82,8 @@ fun SeriesOverviewContent(
alignment = Alignment.TopEnd,
modifier =
Modifier
.fillMaxHeight(.5f)
.fillMaxHeight(.6f)
.alpha(.4f)
.drawWithContent {
drawContent()
drawRect(
@ -110,8 +112,10 @@ fun SeriesOverviewContent(
selectedTabIndex = selectedTabIndex,
modifier =
Modifier
.ifElse(focusRequesters.size > selectedTabIndex, { Modifier.focusRestorer(focusRequesters[selectedTabIndex]) })
.focusRequester(tabRowFocusRequester)
.ifElse(
focusRequesters.size > selectedTabIndex,
{ Modifier.focusRestorer(focusRequesters[selectedTabIndex]) },
).focusRequester(tabRowFocusRequester)
.padding(horizontal = 16.dp)
.fillMaxWidth(),
) {
@ -142,7 +146,7 @@ fun SeriesOverviewContent(
series.name?.let {
Text(
text = it,
style = MaterialTheme.typography.headlineLarge,
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier,
)
}
@ -155,7 +159,7 @@ fun SeriesOverviewContent(
overviewOnClick = {
// TODO show full overview dialog
},
modifier = Modifier,
modifier = Modifier.fillMaxWidth(.66f),
)
}
}
@ -165,7 +169,7 @@ fun SeriesOverviewContent(
LazyRow(
state = state,
horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(start = 32.dp),
contentPadding = PaddingValues(start = 16.dp),
modifier = modifier.focusRestorer(firstItemFocusRequester),
) {
itemsIndexed(episodes) { index, episode ->
@ -173,8 +177,11 @@ fun SeriesOverviewContent(
if (interactionSource.collectIsFocusedAsState().value) {
onFocus.invoke(SeasonEpisode(selectedTabIndex, index))
}
ItemCard(
item = episode,
BannerCard(
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) },
onLongClick = { if (episode != null) onLongClick.invoke(episode) },
modifier =
@ -195,7 +202,10 @@ fun SeriesOverviewContent(
playOnClick = {},
moreOnClick = {},
watchOnClick = {},
modifier = Modifier.fillMaxWidth(),
modifier =
Modifier
.fillMaxWidth()
.padding(start = 16.dp),
)
}
}