mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-07-27 23:12:24 +00:00
feat: refactor bottom sheet into reusable component
This commit is contained in:
parent
5e9b7c07a0
commit
9bb4c341df
6 changed files with 284 additions and 193 deletions
|
|
@ -0,0 +1,125 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
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.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun NuvioModalBottomSheet(
|
||||
onDismissRequest: () -> Unit,
|
||||
sheetState: SheetState,
|
||||
modifier: Modifier = Modifier,
|
||||
containerColor: Color = MaterialTheme.colorScheme.surface,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
shape: Shape = RoundedCornerShape(topStart = 28.dp, topEnd = 28.dp),
|
||||
showDragHandle: Boolean = true,
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
sheetState = sheetState,
|
||||
modifier = modifier,
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor,
|
||||
shape = shape,
|
||||
dragHandle = if (showDragHandle) {
|
||||
{ NuvioBottomSheetDragHandle() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NuvioBottomSheetDivider(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
HorizontalDivider(
|
||||
modifier = modifier,
|
||||
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.6f),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NuvioBottomSheetActionRow(
|
||||
title: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
icon: ImageVector? = null,
|
||||
trailingContent: (@Composable RowScope.() -> Unit)? = null,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (icon != null) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = title,
|
||||
modifier = Modifier.weight(1f),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
trailingContent?.invoke(this)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
suspend fun dismissNuvioBottomSheet(
|
||||
sheetState: SheetState,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
if (sheetState.isVisible) {
|
||||
sheetState.hide()
|
||||
}
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NuvioBottomSheetDragHandle() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(top = 10.dp, bottom = 6.dp)
|
||||
.size(width = 54.dp, height = 5.dp)
|
||||
.clip(RoundedCornerShape(999.dp))
|
||||
.background(MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.65f)),
|
||||
)
|
||||
}
|
||||
|
|
@ -4,14 +4,11 @@ import androidx.compose.animation.AnimatedVisibility
|
|||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
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.foundation.shape.CircleShape
|
||||
|
|
@ -23,11 +20,8 @@ import androidx.compose.material.icons.filled.Check
|
|||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.CheckCircleOutline
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -58,26 +52,16 @@ fun NuvioPosterActionSheet(
|
|||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
ModalBottomSheet(
|
||||
NuvioModalBottomSheet(
|
||||
onDismissRequest = {
|
||||
coroutineScope.launch {
|
||||
dismissPosterActionSheet(
|
||||
dismissNuvioBottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismiss = onDismiss,
|
||||
)
|
||||
}
|
||||
},
|
||||
sheetState = sheetState,
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
dragHandle = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(top = 10.dp, bottom = 6.dp)
|
||||
.size(width = 54.dp, height = 5.dp)
|
||||
.clip(RoundedCornerShape(999.dp))
|
||||
.background(MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.65f)),
|
||||
)
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -85,28 +69,28 @@ fun NuvioPosterActionSheet(
|
|||
.padding(bottom = 16.dp + nuvioPlatformExtraBottomPadding),
|
||||
) {
|
||||
PosterSheetHeader(item = item)
|
||||
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.6f))
|
||||
PosterSheetActionRow(
|
||||
NuvioBottomSheetDivider()
|
||||
NuvioBottomSheetActionRow(
|
||||
icon = if (isSaved) Icons.Default.Bookmark else Icons.Default.BookmarkBorder,
|
||||
title = if (isSaved) "Remove from Library" else "Add to Library",
|
||||
onClick = {
|
||||
onToggleLibrary()
|
||||
coroutineScope.launch {
|
||||
dismissPosterActionSheet(
|
||||
dismissNuvioBottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismiss = onDismiss,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.6f))
|
||||
PosterSheetActionRow(
|
||||
NuvioBottomSheetDivider()
|
||||
NuvioBottomSheetActionRow(
|
||||
icon = if (isWatched) Icons.Default.CheckCircle else Icons.Default.CheckCircleOutline,
|
||||
title = if (isWatched) "Mark as Unwatched" else "Mark as Watched",
|
||||
onClick = {
|
||||
onToggleWatched()
|
||||
coroutineScope.launch {
|
||||
dismissPosterActionSheet(
|
||||
dismissNuvioBottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismiss = onDismiss,
|
||||
)
|
||||
|
|
@ -117,17 +101,6 @@ fun NuvioPosterActionSheet(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
private suspend fun dismissPosterActionSheet(
|
||||
sheetState: SheetState,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
if (sheetState.isVisible) {
|
||||
sheetState.hide()
|
||||
}
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NuvioWatchedBadge(
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -226,35 +199,3 @@ private fun PosterSheetHeader(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PosterSheetActionRow(
|
||||
icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||
title: String,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowRight
|
|||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -35,6 +34,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.nuvio.app.core.ui.NuvioModalBottomSheet
|
||||
import com.nuvio.app.features.trakt.TraktCommentReview
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
|
@ -56,10 +56,9 @@ fun CommentDetailSheet(
|
|||
scrollState.scrollTo(0)
|
||||
}
|
||||
|
||||
ModalBottomSheet(
|
||||
NuvioModalBottomSheet(
|
||||
onDismissRequest = onDismiss,
|
||||
sheetState = sheetState,
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
||||
shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
package com.nuvio.app.features.details.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
|
|
@ -16,21 +14,19 @@ import androidx.compose.material.icons.filled.CheckCircle
|
|||
import androidx.compose.material.icons.filled.DoneAll
|
||||
import androidx.compose.material.icons.filled.PlaylistAddCheckCircle
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.nuvio.app.core.ui.NuvioBottomSheetActionRow
|
||||
import com.nuvio.app.core.ui.NuvioBottomSheetDivider
|
||||
import com.nuvio.app.core.ui.NuvioModalBottomSheet
|
||||
import com.nuvio.app.core.ui.dismissNuvioBottomSheet
|
||||
import com.nuvio.app.features.details.MetaVideo
|
||||
import com.nuvio.app.core.ui.nuvioPlatformExtraBottomPadding
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -52,25 +48,13 @@ fun EpisodeWatchedActionSheet(
|
|||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
ModalBottomSheet(
|
||||
NuvioModalBottomSheet(
|
||||
onDismissRequest = {
|
||||
coroutineScope.launch {
|
||||
dismissEpisodeActionSheet(sheetState = sheetState, onDismiss = onDismiss)
|
||||
dismissNuvioBottomSheet(sheetState = sheetState, onDismiss = onDismiss)
|
||||
}
|
||||
},
|
||||
sheetState = sheetState,
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
dragHandle = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(top = 10.dp, bottom = 6.dp)
|
||||
.size(width = 54.dp, height = 5.dp)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.65f),
|
||||
shape = RoundedCornerShape(999.dp),
|
||||
),
|
||||
)
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -81,20 +65,20 @@ fun EpisodeWatchedActionSheet(
|
|||
episode = episode,
|
||||
seasonLabel = seasonLabel,
|
||||
)
|
||||
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.6f))
|
||||
EpisodeActionSheetRow(
|
||||
NuvioBottomSheetDivider()
|
||||
NuvioBottomSheetActionRow(
|
||||
icon = Icons.Default.CheckCircle,
|
||||
title = if (isEpisodeWatched) "Mark as unwatched" else "Mark as watched",
|
||||
onClick = {
|
||||
onToggleWatched()
|
||||
coroutineScope.launch {
|
||||
dismissEpisodeActionSheet(sheetState = sheetState, onDismiss = onDismiss)
|
||||
dismissNuvioBottomSheet(sheetState = sheetState, onDismiss = onDismiss)
|
||||
}
|
||||
},
|
||||
)
|
||||
if (canMarkPreviousEpisodes) {
|
||||
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.6f))
|
||||
EpisodeActionSheetRow(
|
||||
NuvioBottomSheetDivider()
|
||||
NuvioBottomSheetActionRow(
|
||||
icon = Icons.Default.DoneAll,
|
||||
title = if (arePreviousEpisodesWatched) {
|
||||
"Mark previous as unwatched"
|
||||
|
|
@ -104,13 +88,13 @@ fun EpisodeWatchedActionSheet(
|
|||
onClick = {
|
||||
onTogglePreviousWatched()
|
||||
coroutineScope.launch {
|
||||
dismissEpisodeActionSheet(sheetState = sheetState, onDismiss = onDismiss)
|
||||
dismissNuvioBottomSheet(sheetState = sheetState, onDismiss = onDismiss)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.6f))
|
||||
EpisodeActionSheetRow(
|
||||
NuvioBottomSheetDivider()
|
||||
NuvioBottomSheetActionRow(
|
||||
icon = Icons.Default.PlaylistAddCheckCircle,
|
||||
title = if (isSeasonWatched) {
|
||||
"Mark $seasonLabel as unwatched"
|
||||
|
|
@ -120,7 +104,7 @@ fun EpisodeWatchedActionSheet(
|
|||
onClick = {
|
||||
onToggleSeasonWatched()
|
||||
coroutineScope.launch {
|
||||
dismissEpisodeActionSheet(sheetState = sheetState, onDismiss = onDismiss)
|
||||
dismissNuvioBottomSheet(sheetState = sheetState, onDismiss = onDismiss)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -128,17 +112,6 @@ fun EpisodeWatchedActionSheet(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
private suspend fun dismissEpisodeActionSheet(
|
||||
sheetState: SheetState,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
if (sheetState.isVisible) {
|
||||
sheetState.hide()
|
||||
}
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EpisodeActionSheetHeader(
|
||||
episode: MetaVideo,
|
||||
|
|
@ -174,33 +147,3 @@ private fun EpisodeActionSheetHeader(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EpisodeActionSheetRow(
|
||||
icon: ImageVector,
|
||||
title: String,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,9 @@ import androidx.compose.material.icons.rounded.Close
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
|
|
@ -33,6 +31,9 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import com.nuvio.app.core.ui.NuvioBottomSheetDivider
|
||||
import com.nuvio.app.core.ui.NuvioModalBottomSheet
|
||||
import com.nuvio.app.core.ui.dismissNuvioBottomSheet
|
||||
import com.nuvio.app.core.ui.nuvioPlatformExtraBottomPadding
|
||||
import com.nuvio.app.features.player.PlatformPlayerSurface
|
||||
import com.nuvio.app.features.player.PlayerResizeMode
|
||||
|
|
@ -74,17 +75,13 @@ fun TrailerPlayerPopup(
|
|||
|
||||
val dismissSheet: () -> Unit = {
|
||||
coroutineScope.launch {
|
||||
if (sheetState.isVisible) {
|
||||
sheetState.hide()
|
||||
}
|
||||
onDismiss()
|
||||
dismissNuvioBottomSheet(sheetState = sheetState, onDismiss = onDismiss)
|
||||
}
|
||||
}
|
||||
|
||||
ModalBottomSheet(
|
||||
NuvioModalBottomSheet(
|
||||
onDismissRequest = dismissSheet,
|
||||
sheetState = sheetState,
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -112,7 +109,6 @@ fun TrailerPlayerPopup(
|
|||
if (headerSubtitle.isNotBlank()) {
|
||||
Text(
|
||||
text = headerSubtitle,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
|
|
@ -129,7 +125,7 @@ fun TrailerPlayerPopup(
|
|||
}
|
||||
}
|
||||
|
||||
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.55f))
|
||||
NuvioBottomSheetDivider()
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -13,24 +13,30 @@ import androidx.compose.foundation.layout.aspectRatio
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Check
|
||||
import androidx.compose.material.icons.rounded.KeyboardArrowDown
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -43,11 +49,17 @@ import androidx.compose.ui.unit.sp
|
|||
import coil3.compose.AsyncImage
|
||||
import com.nuvio.app.core.format.formatReleaseDateForDisplay
|
||||
import com.nuvio.app.core.ui.NuvioAnimatedWatchedBadge
|
||||
import com.nuvio.app.core.ui.NuvioBottomSheetActionRow
|
||||
import com.nuvio.app.core.ui.NuvioBottomSheetDivider
|
||||
import com.nuvio.app.core.ui.NuvioModalBottomSheet
|
||||
import com.nuvio.app.core.ui.dismissNuvioBottomSheet
|
||||
import com.nuvio.app.core.ui.nuvioPlatformExtraBottomPadding
|
||||
import com.nuvio.app.core.ui.posterCardClickable
|
||||
import com.nuvio.app.features.home.MetaPreview
|
||||
import com.nuvio.app.features.home.PosterShape
|
||||
import com.nuvio.app.features.home.components.HomeEmptyStateCard
|
||||
import com.nuvio.app.features.watching.application.WatchingState
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal fun LazyListScope.discoverContent(
|
||||
state: DiscoverUiState,
|
||||
|
|
@ -150,13 +162,17 @@ private fun DiscoverFilterRow(
|
|||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
) {
|
||||
DiscoverDropdownChip(
|
||||
title = "Select Type",
|
||||
label = state.selectedType?.displayTypeLabel() ?: "Type",
|
||||
selectedKey = state.selectedType,
|
||||
options = state.typeOptions.map { DiscoverOptionItem(key = it, label = it.displayTypeLabel()) },
|
||||
enabled = state.typeOptions.isNotEmpty(),
|
||||
onSelected = { onTypeSelected(it.key) },
|
||||
)
|
||||
DiscoverDropdownChip(
|
||||
title = "Select Catalog",
|
||||
label = state.selectedCatalog?.catalogName ?: "Catalog",
|
||||
selectedKey = state.selectedCatalogKey,
|
||||
options = state.catalogOptions.map { option -> DiscoverOptionItem(key = option.key, label = option.catalogName) },
|
||||
enabled = state.catalogOptions.isNotEmpty(),
|
||||
onSelected = { onCatalogSelected(it.key) },
|
||||
|
|
@ -170,7 +186,9 @@ private fun DiscoverFilterRow(
|
|||
addAll(state.genreOptions.map { genre -> DiscoverOptionItem(key = genre, label = genre) })
|
||||
}
|
||||
DiscoverDropdownChip(
|
||||
title = "Select Genre",
|
||||
label = state.selectedGenre ?: "All Genres",
|
||||
selectedKey = state.selectedGenre ?: "",
|
||||
options = genreOptions,
|
||||
enabled = genreOptions.size > 1 || selectedCatalog?.genreRequired == true,
|
||||
onSelected = { option ->
|
||||
|
|
@ -180,57 +198,126 @@ private fun DiscoverFilterRow(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun DiscoverDropdownChip(
|
||||
title: String,
|
||||
label: String,
|
||||
selectedKey: String?,
|
||||
options: List<DiscoverOptionItem>,
|
||||
enabled: Boolean,
|
||||
onSelected: (DiscoverOptionItem) -> Unit,
|
||||
) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
var isSheetVisible by remember { mutableStateOf(false) }
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
Box {
|
||||
Row(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(20.dp))
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.then(
|
||||
if (enabled) {
|
||||
Modifier.clickable { isSheetVisible = true }
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
)
|
||||
.padding(horizontal = 18.dp, vertical = 14.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = if (enabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.KeyboardArrowDown,
|
||||
contentDescription = null,
|
||||
tint = if (enabled) MaterialTheme.colorScheme.onSurfaceVariant else MaterialTheme.colorScheme.outline,
|
||||
)
|
||||
}
|
||||
|
||||
if (isSheetVisible) {
|
||||
DiscoverOptionsSheet(
|
||||
title = title,
|
||||
options = options,
|
||||
selectedKey = selectedKey,
|
||||
sheetState = sheetState,
|
||||
onDismiss = {
|
||||
coroutineScope.launch {
|
||||
dismissNuvioBottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismiss = { isSheetVisible = false },
|
||||
)
|
||||
}
|
||||
},
|
||||
onSelected = { option ->
|
||||
onSelected(option)
|
||||
coroutineScope.launch {
|
||||
dismissNuvioBottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismiss = { isSheetVisible = false },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun DiscoverOptionsSheet(
|
||||
title: String,
|
||||
options: List<DiscoverOptionItem>,
|
||||
selectedKey: String?,
|
||||
sheetState: SheetState,
|
||||
onDismiss: () -> Unit,
|
||||
onSelected: (DiscoverOptionItem) -> Unit,
|
||||
) {
|
||||
NuvioModalBottomSheet(
|
||||
onDismissRequest = onDismiss,
|
||||
sheetState = sheetState,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(20.dp))
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.then(
|
||||
if (enabled) {
|
||||
Modifier.clickable { expanded = true }
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
)
|
||||
.padding(horizontal = 18.dp, vertical = 14.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 16.dp + nuvioPlatformExtraBottomPadding),
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = if (enabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
text = title,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.KeyboardArrowDown,
|
||||
contentDescription = null,
|
||||
tint = if (enabled) MaterialTheme.colorScheme.onSurfaceVariant else MaterialTheme.colorScheme.outline,
|
||||
)
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false },
|
||||
) {
|
||||
options.forEach { option ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(option.label) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
onSelected(option)
|
||||
},
|
||||
)
|
||||
NuvioBottomSheetDivider()
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = 420.dp),
|
||||
) {
|
||||
itemsIndexed(options) { index, option ->
|
||||
NuvioBottomSheetActionRow(
|
||||
title = option.label,
|
||||
onClick = { onSelected(option) },
|
||||
trailingContent = {
|
||||
if (option.key == selectedKey) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Check,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
if (index < options.lastIndex) {
|
||||
NuvioBottomSheetDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue