mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-23 19:26:50 +00:00
refactor(tv): replace custom season tab implementation with TabRow for improved UI consistency
This commit is contained in:
@@ -94,7 +94,7 @@ internal fun TvSeriesScreenContent(
|
|||||||
series = series,
|
series = series,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(18.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
TvSeasonTabs(
|
TvSeasonTabs(
|
||||||
seasons = series.seasons,
|
seasons = series.seasons,
|
||||||
selectedSeason = selectedSeason,
|
selectedSeason = selectedSeason,
|
||||||
@@ -103,7 +103,7 @@ internal fun TvSeriesScreenContent(
|
|||||||
onSelect = { selectedSeason = it },
|
onSelect = { selectedSeason = it },
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
TvEpisodeCarousel(
|
TvEpisodeCarousel(
|
||||||
episodes = selectedSeason.episodes,
|
episodes = selectedSeason.episodes,
|
||||||
onPlayEpisode = { onPlayEpisode(it.id) },
|
onPlayEpisode = { onPlayEpisode(it.id) },
|
||||||
|
|||||||
@@ -6,12 +6,10 @@ import androidx.compose.foundation.background
|
|||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.LocalBringIntoViewSpec
|
import androidx.compose.foundation.gestures.LocalBringIntoViewSpec
|
||||||
import androidx.compose.foundation.horizontalScroll
|
|
||||||
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.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -20,10 +18,10 @@ 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.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.PlayCircle
|
import androidx.compose.material.icons.outlined.PlayCircle
|
||||||
@@ -44,7 +42,6 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
@@ -52,6 +49,9 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
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.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.tv.material3.Tab
|
||||||
|
import androidx.tv.material3.TabDefaults
|
||||||
|
import androidx.tv.material3.TabRow
|
||||||
import hu.bbara.purefin.image.ArtworkKind
|
import hu.bbara.purefin.image.ArtworkKind
|
||||||
import hu.bbara.purefin.image.ImageUrlBuilder
|
import hu.bbara.purefin.image.ImageUrlBuilder
|
||||||
import hu.bbara.purefin.model.CastMember
|
import hu.bbara.purefin.model.CastMember
|
||||||
@@ -64,6 +64,7 @@ import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
|||||||
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
|
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
|
||||||
import hu.bbara.purefin.ui.screen.home.components.TvHomeRowBringIntoViewSpec
|
import hu.bbara.purefin.ui.screen.home.components.TvHomeRowBringIntoViewSpec
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import androidx.tv.material3.Text as TvText
|
||||||
|
|
||||||
internal const val SeriesFirstSeasonTabTag = "series-first-season-tab"
|
internal const val SeriesFirstSeasonTabTag = "series-first-season-tab"
|
||||||
internal const val SeriesNextUpEpisodeCardTag = "series-next-up-episode-card"
|
internal const val SeriesNextUpEpisodeCardTag = "series-next-up-episode-card"
|
||||||
@@ -84,17 +85,21 @@ internal fun TvSeasonTabs(
|
|||||||
firstItemTestTag: String? = null,
|
firstItemTestTag: String? = null,
|
||||||
onSelect: (Season) -> Unit
|
onSelect: (Season) -> Unit
|
||||||
) {
|
) {
|
||||||
Row(
|
val selectedSeasonIndex = seasons.indexOf(selectedSeason).coerceAtLeast(0)
|
||||||
|
|
||||||
|
TabRow(
|
||||||
|
selectedTabIndex = selectedSeasonIndex,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.horizontalScroll(rememberScrollState()),
|
.wrapContentWidth(Alignment.Start),
|
||||||
horizontalArrangement = Arrangement.spacedBy(20.dp)
|
contentColor = MaterialTheme.colorScheme.onSurface
|
||||||
) {
|
) {
|
||||||
seasons.forEachIndexed { index, season ->
|
seasons.forEachIndexed { index, season ->
|
||||||
TvSeasonTab(
|
Tab(
|
||||||
name = season.name,
|
selected = index == selectedSeasonIndex,
|
||||||
isSelected = season == selectedSeason,
|
onFocus = { onSelect(season) },
|
||||||
onSelect = { onSelect(season) },
|
onClick = { onSelect(season) },
|
||||||
|
colors = TabDefaults.underlinedIndicatorTabColors(),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(
|
.then(
|
||||||
if (index == 0 && firstItemFocusRequester != null) {
|
if (index == 0 && firstItemFocusRequester != null) {
|
||||||
@@ -103,45 +108,29 @@ internal fun TvSeasonTabs(
|
|||||||
Modifier
|
Modifier
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
.then(
|
||||||
|
if (index == 0 && firstItemTestTag != null) {
|
||||||
|
Modifier.testTag(firstItemTestTag)
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun TvSeasonTab(
|
|
||||||
name: String,
|
|
||||||
isSelected: Boolean,
|
|
||||||
onSelect: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
val scheme = MaterialTheme.colorScheme
|
|
||||||
val mutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
|
|
||||||
var isFocused by remember { mutableStateOf(false) }
|
|
||||||
val color = if (isSelected || isFocused) scheme.primary else mutedStrong
|
|
||||||
val underlineColor = if (isSelected || isFocused) scheme.primary else Color.Transparent
|
|
||||||
val underlineHeight = if (isFocused) 3.dp else 2.dp
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = modifier
|
|
||||||
.padding(bottom = 8.dp)
|
|
||||||
.onFocusChanged { isFocused = it.isFocused }
|
|
||||||
.clickable { onSelect() }
|
|
||||||
) {
|
) {
|
||||||
Text(
|
TvText(
|
||||||
text = name,
|
text = season.name,
|
||||||
color = color,
|
|
||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
fontWeight = if (isSelected || isFocused) FontWeight.Bold else FontWeight.Medium
|
fontWeight = if (index == selectedSeasonIndex) {
|
||||||
)
|
FontWeight.Bold
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
} else {
|
||||||
Box(
|
FontWeight.Medium
|
||||||
modifier = Modifier
|
},
|
||||||
.height(underlineHeight)
|
maxLines = 1,
|
||||||
.width(52.dp)
|
overflow = TextOverflow.Ellipsis,
|
||||||
.background(underlineColor)
|
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
|||||||
Reference in New Issue
Block a user