feat(card): replace dropdown menu with modal bottom sheet and add long-press

Replace the DropdownMenu popup in MediaImageCard with a ModalBottomSheet
that appears when the three-dot icon is pressed on ContinueWatchingCard
items. Also trigger the bottom sheet on long-press of the card.
Follows the existing ModalBottomSheet pattern from SeriesComponents.
This commit is contained in:
2026-06-07 09:40:20 +00:00
parent 10e7b450f3
commit 3f9fd6e36e

View File

@@ -1,6 +1,9 @@
package hu.bbara.purefin.ui.common.card package hu.bbara.purefin.ui.common.card
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
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.BoxScope import androidx.compose.foundation.layout.BoxScope
@@ -18,11 +21,13 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -32,6 +37,7 @@ 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.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@@ -41,6 +47,7 @@ import androidx.compose.ui.unit.dp
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.model.MediaAction import hu.bbara.purefin.ui.model.MediaAction
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@Composable @Composable
fun MediaImageCard( fun MediaImageCard(
imageUrl: String?, imageUrl: String?,
@@ -60,13 +67,19 @@ fun MediaImageCard(
val scheme = MaterialTheme.colorScheme val scheme = MaterialTheme.colorScheme
val shape = RoundedCornerShape(shapeSize) val shape = RoundedCornerShape(shapeSize)
var expanded by remember { mutableStateOf(false) } var showBottomSheet by remember { mutableStateOf(false) }
Card( Card(
onClick = onClick,
shape = shape, shape = shape,
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer), colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
modifier = modifier.fillMaxWidth() modifier = modifier
.fillMaxWidth()
.combinedClickable(
onClick = onClick,
onLongClick = if (popupActions.isNotEmpty()) {
{ showBottomSheet = true }
} else null
)
) { ) {
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) {
Box( Box(
@@ -115,29 +128,39 @@ fun MediaImageCard(
} }
} }
if (popupActions.isNotEmpty()) { if (popupActions.isNotEmpty()) {
Box() { IconButton(
IconButton( onClick = { showBottomSheet = true },
onClick = { expanded = !expanded }, ) {
) { Icon(
Icon( imageVector = Icons.Outlined.MoreVert,
imageVector = Icons.Outlined.MoreVert, contentDescription = "More actions"
contentDescription = "More actions" )
)
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
popupActions.forEach { action ->
DropdownMenuItem(
text = { Text(text = action.name) },
onClick = action.onClick
)
}
}
} }
} }
} }
} }
} }
if (showBottomSheet) {
ModalBottomSheet(
onDismissRequest = { showBottomSheet = false },
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 24.dp)
) {
popupActions.forEach { action ->
ListItem(
headlineContent = { Text(text = action.name) },
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
modifier = Modifier.clickable {
action.onClick()
showBottomSheet = false
}
)
}
}
}
}
} }