feat(series): replace season tabs with BottomSheet selector

Convert SeasonTabs from horizontal scrollable tabs to a clickable label that opens a ModalBottomSheet listing all seasons for selection.

- Removes unused LazyRow, itemsIndexed, rememberLazyListState imports
- Removes unused SeasonTab private composable and SeasonTabTagPrefix constant
- Adds ArrowDropDown and Check icon imports
- Adds OptIn(ExperimentalMaterial3Api) for ModalBottomSheet
This commit is contained in:
2026-06-06 10:51:12 +00:00
parent 753b461453
commit dd66302da0

View File

@@ -12,19 +12,17 @@ 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
import androidx.compose.foundation.layout.fillMaxWidth 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.sizeIn import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
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.Add import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.ArrowDropDown
import androidx.compose.material.icons.outlined.AutoAwesome import androidx.compose.material.icons.outlined.AutoAwesome
import androidx.compose.material.icons.outlined.Cast import androidx.compose.material.icons.outlined.Cast
import androidx.compose.material.icons.outlined.Check
import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Download import androidx.compose.material.icons.outlined.Download
import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material.icons.outlined.MoreVert
@@ -37,7 +35,6 @@ import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -333,6 +330,7 @@ private fun DownloadOptionRow(
) )
} }
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
internal fun SeasonTabs( internal fun SeasonTabs(
seasons: List<Season>, seasons: List<Season>,
@@ -340,62 +338,67 @@ internal fun SeasonTabs(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onSelect: (Season) -> Unit onSelect: (Season) -> Unit
) { ) {
val selectedSeasonIndex = seasons.indexOf(selectedSeason).coerceAtLeast(0) var showBottomSheet by remember { mutableStateOf(false) }
val listState = rememberLazyListState()
LaunchedEffect(selectedSeasonIndex, seasons.size) {
if (seasons.isNotEmpty()) {
listState.scrollToItem(selectedSeasonIndex)
}
}
LazyRow(
state = listState,
modifier = modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(20.dp),
verticalAlignment = Alignment.CenterVertically
) {
itemsIndexed(seasons, key = { _, season -> season.id }) { index, season ->
SeasonTab(
name = season.name,
isSelected = season == selectedSeason,
modifier = Modifier
.testTag("$SeriesSeasonTabTagPrefix$index")
.clickable { onSelect(season) }
)
}
}
}
@Composable
private fun SeasonTab(
name: String,
isSelected: Boolean,
modifier: Modifier = Modifier
) {
val scheme = MaterialTheme.colorScheme val scheme = MaterialTheme.colorScheme
val mutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
val color = if (isSelected) scheme.primary else mutedStrong Row(
val borderColor = if (isSelected) scheme.primary else Color.Transparent
Column(
modifier = modifier modifier = modifier
.padding(bottom = 8.dp) .clickable { showBottomSheet = true }
.testTag(SeriesSeasonSelectorTag),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
Text( Text(
text = name, text = selectedSeason?.name ?: "Select Season",
color = color, style = MaterialTheme.typography.titleMedium,
fontSize = 13.sp, color = scheme.onSurface
fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Medium
) )
Spacer(modifier = Modifier.height(8.dp)) Icon(
Box( imageVector = Icons.Outlined.ArrowDropDown,
modifier = Modifier contentDescription = "Select season",
.height(2.dp) tint = scheme.onSurfaceVariant
.width(52.dp)
.background(borderColor)
) )
} }
if (showBottomSheet) {
ModalBottomSheet(
onDismissRequest = { showBottomSheet = false },
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 24.dp)
) {
Text(
text = "Select Season",
style = MaterialTheme.typography.titleLarge,
color = scheme.onSurface,
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp)
)
seasons.forEach { season ->
ListItem(
headlineContent = {
Text(text = season.name)
},
leadingContent = if (season == selectedSeason) {
{
Icon(
imageVector = Icons.Outlined.Check,
contentDescription = null,
tint = scheme.primary
)
}
} else null,
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
modifier = Modifier.clickable {
onSelect(season)
showBottomSheet = false
}
)
}
}
}
}
} }
@Composable @Composable
@@ -495,7 +498,7 @@ internal const val SeriesDownloadDialogTag = "series-download-dialog"
internal const val SeriesDownloadSeasonButtonTag = "series-download-season-button" internal const val SeriesDownloadSeasonButtonTag = "series-download-season-button"
internal const val SeriesDownloadAllButtonTag = "series-download-all-button" internal const val SeriesDownloadAllButtonTag = "series-download-all-button"
internal const val SeriesSmartDownloadButtonTag = "series-smart-download-button" internal const val SeriesSmartDownloadButtonTag = "series-smart-download-button"
internal const val SeriesSeasonTabTagPrefix = "series-season-tab-" internal const val SeriesSeasonSelectorTag = "series-season-selector"
internal const val SeriesEpisodeCarouselTag = "series-episode-carousel" internal const val SeriesEpisodeCarouselTag = "series-episode-carousel"
internal const val SeriesEpisodeCardTagPrefix = "series-episode-card-" internal const val SeriesEpisodeCardTagPrefix = "series-episode-card-"