feat(card): introduce MediaImageCard for consistent media display across components

This commit is contained in:
2026-05-02 20:47:35 +02:00
parent fbcba6fbfc
commit 2753a45645
8 changed files with 184 additions and 390 deletions

View File

@@ -0,0 +1,93 @@
package hu.bbara.purefin.ui.common.card
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
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.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.TextStyle
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.dp
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
@Composable
fun MediaImageCard(
imageUrl: String?,
title: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
subtitle: String? = null,
imageModifier: Modifier = Modifier,
shapeSize: Dp = 12.dp,
imageAspectRatio: Float = 16f / 10f,
titleStyle: TextStyle = MaterialTheme.typography.bodyLarge,
subtitleStyle: TextStyle = MaterialTheme.typography.bodySmall,
textPadding: PaddingValues = PaddingValues(8.dp),
imageOverlay: @Composable BoxScope.() -> Unit = {}
) {
val scheme = MaterialTheme.colorScheme
val shape = RoundedCornerShape(shapeSize)
Card(
onClick = onClick,
shape = shape,
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
modifier = modifier.fillMaxWidth()
) {
Column(modifier = Modifier.fillMaxWidth()) {
Box(
modifier = imageModifier
.fillMaxWidth()
.aspectRatio(imageAspectRatio)
.clip(RoundedCornerShape(topStart = shapeSize, topEnd = shapeSize))
.background(scheme.surface)
) {
PurefinAsyncImage(
model = imageUrl,
contentDescription = title,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
imageOverlay()
}
Column(modifier = Modifier.padding(textPadding)) {
Text(
text = title,
style = titleStyle,
fontWeight = FontWeight.SemiBold,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
subtitle
?.takeIf { it.isNotBlank() }
?.let { text ->
Spacer(modifier = Modifier.height(4.dp))
Text(
text = text,
style = subtitleStyle,
color = scheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
}

View File

@@ -1,33 +1,21 @@
package hu.bbara.purefin.ui.screen.home.components.continuewatching
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import hu.bbara.purefin.ui.model.MediaUiModel
import hu.bbara.purefin.ui.common.bar.MediaProgressBar
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.card.MediaImageCard
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
import hu.bbara.purefin.ui.model.MediaUiModel
@Composable
internal fun ContinueWatchingCard(
@@ -41,67 +29,38 @@ internal fun ContinueWatchingCard(
onMediaSelected(item)
}
Card(
MediaImageCard(
imageUrl = item.primaryImageUrl,
title = item.primaryText,
subtitle = item.secondaryText,
onClick = onClick,
shape = RoundedCornerShape(26.dp),
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
imageModifier = Modifier.homeMediaSharedBoundsSource(sharedBoundsKey),
shapeSize = 26.dp,
imageAspectRatio = 16f / 9f,
titleStyle = MaterialTheme.typography.titleMedium,
textPadding = PaddingValues(horizontal = 16.dp, vertical = 14.dp),
modifier = modifier.width(280.dp)
) {
Column(modifier = Modifier.fillMaxWidth()) {
Box(
modifier = Modifier
.homeMediaSharedBoundsSource(sharedBoundsKey)
.fillMaxWidth()
.aspectRatio(16f / 9f)
.background(scheme.surfaceContainer)
) {
PurefinAsyncImage(
model = item.primaryImageUrl,
contentDescription = item.primaryText,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
Box(
modifier = Modifier
.matchParentSize()
.background(
Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.08f),
Color.Black.copy(alpha = 0.38f)
)
)
Box(
modifier = Modifier
.matchParentSize()
.background(
Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.08f),
Color.Black.copy(alpha = 0.38f)
)
)
item.progress?.let { progress ->
MediaProgressBar(
progress = progress,
foregroundColor = scheme.primary,
backgroundColor = Color.White.copy(alpha = 0.24f),
modifier = Modifier.align(Alignment.BottomStart)
)
}
}
Column(
verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp)
) {
Text(
text = item.primaryText,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = item.secondaryText,
style = MaterialTheme.typography.bodySmall,
color = scheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
)
item.progress?.let { progress ->
MediaProgressBar(
progress = progress,
foregroundColor = scheme.primary,
backgroundColor = Color.White.copy(alpha = 0.24f),
modifier = Modifier.align(Alignment.BottomStart)
)
}
}
}

View File

@@ -23,11 +23,11 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import hu.bbara.purefin.ui.model.MediaUiModel
import hu.bbara.purefin.ui.common.bar.MediaProgressBar
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
import hu.bbara.purefin.ui.model.MediaUiModel
@Composable
internal fun SuggestionCard(
@@ -74,7 +74,7 @@ internal fun SuggestionCard(
)
)
)
)
)
Column(
verticalArrangement = Arrangement.SpaceBetween,
modifier = Modifier

View File

@@ -1,30 +1,13 @@
package hu.bbara.purefin.ui.screen.home.components.library
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
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.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
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.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.card.MediaImageCard
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
import hu.bbara.purefin.ui.model.EpisodeUiModel
@@ -38,64 +21,31 @@ internal fun HomeBrowseCard(
onMediaSelected: (MediaUiModel) -> Unit,
modifier: Modifier = Modifier
) {
val scheme = MaterialTheme.colorScheme
val onClick = rememberHomeMediaSharedBoundsClick(sharedBoundsKey) {
onMediaSelected(uiModel)
}
Card(
MediaImageCard(
imageUrl = uiModel.primaryImageUrl,
title = uiModel.primaryText,
subtitle = uiModel.secondaryText,
onClick = onClick,
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
imageModifier = Modifier.homeMediaSharedBoundsSource(sharedBoundsKey),
imageAspectRatio = 15f / 16f,
modifier = modifier.width(188.dp)
) {
Column(modifier = Modifier.fillMaxWidth()) {
Box(
modifier = Modifier
.homeMediaSharedBoundsSource(sharedBoundsKey)
.fillMaxWidth()
.aspectRatio(16f / 10f)
.clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp))
.background(scheme.surface)
) {
PurefinAsyncImage(
model = uiModel.primaryImageUrl,
contentDescription = uiModel.primaryText,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
when (uiModel) {
is MovieUiModel, is EpisodeUiModel -> {
WatchStateBadge(
size = 28,
modifier = Modifier
.align(Alignment.TopEnd)
.padding(8.dp),
watched = uiModel.watched,
started = (uiModel.progress ?: 0f) > 0f
)
}
else -> Unit
}
}
Spacer(modifier = Modifier.height(10.dp))
Column(modifier = Modifier.padding(12.dp)) {
Text(
text = uiModel.primaryText,
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.SemiBold,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = uiModel.secondaryText,
style = MaterialTheme.typography.bodySmall,
color = scheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis
when (uiModel) {
is MovieUiModel, is EpisodeUiModel -> {
WatchStateBadge(
size = 28,
modifier = Modifier
.align(Alignment.TopEnd)
.padding(8.dp),
watched = uiModel.watched,
started = (uiModel.progress ?: 0f) > 0f
)
}
else -> Unit
}
}
}

View File

@@ -1,31 +1,19 @@
package hu.bbara.purefin.ui.screen.home.components.nextup
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import hu.bbara.purefin.ui.model.MediaUiModel
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.card.MediaImageCard
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
import hu.bbara.purefin.ui.model.MediaUiModel
@Composable
internal fun NextUpCard(
@@ -34,64 +22,33 @@ internal fun NextUpCard(
onMediaSelected: (MediaUiModel) -> Unit,
modifier: Modifier = Modifier
) {
val scheme = MaterialTheme.colorScheme
val onClick = rememberHomeMediaSharedBoundsClick(sharedBoundsKey) {
onMediaSelected(uiModel)
}
Card(
MediaImageCard(
imageUrl = uiModel.primaryImageUrl,
title = uiModel.primaryText,
subtitle = uiModel.secondaryText,
onClick = onClick,
shape = RoundedCornerShape(24.dp),
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
imageModifier = Modifier.homeMediaSharedBoundsSource(sharedBoundsKey),
shapeSize = 24.dp,
titleStyle = MaterialTheme.typography.titleSmall,
textPadding = PaddingValues(horizontal = 14.dp, vertical = 12.dp),
modifier = modifier.width(256.dp)
) {
Column(modifier = Modifier.fillMaxWidth()) {
Box(
modifier = Modifier
.homeMediaSharedBoundsSource(sharedBoundsKey)
.fillMaxWidth()
.aspectRatio(16f / 10f)
.background(scheme.surface)
) {
PurefinAsyncImage(
model = uiModel.primaryImageUrl,
contentDescription = uiModel.primaryText,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
Box(
modifier = Modifier
.matchParentSize()
.background(
Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Transparent,
Color.Black.copy(alpha = 0.26f)
)
)
Box(
modifier = Modifier
.matchParentSize()
.background(
Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Transparent,
Color.Black.copy(alpha = 0.26f)
)
)
)
}
Column(
verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier.padding(horizontal = 14.dp, vertical = 12.dp)
) {
Text(
text = uiModel.primaryText,
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.SemiBold,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = uiModel.secondaryText,
style = MaterialTheme.typography.bodySmall,
color = scheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
)
}
}

View File

@@ -1,29 +1,10 @@
package hu.bbara.purefin.ui.screen.home.components.search
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
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.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import hu.bbara.purefin.feature.search.SearchResult
import hu.bbara.purefin.model.MediaKind
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.card.MediaImageCard
@Composable
internal fun SearchResultCard(
@@ -31,52 +12,15 @@ internal fun SearchResultCard(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val scheme = MaterialTheme.colorScheme
Card(
MediaImageCard(
imageUrl = item.posterUrl,
title = item.title,
subtitle = when (item.type) {
MediaKind.MOVIE -> "Movie"
MediaKind.SERIES -> "Series"
else -> "Title"
},
onClick = onClick,
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
modifier = modifier
.fillMaxWidth()
) {
Column(modifier = Modifier.fillMaxWidth()) {
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(16f / 10f)
.clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp))
.background(scheme.surface)
) {
PurefinAsyncImage(
model = item.posterUrl,
contentDescription = item.title,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
}
Spacer(modifier = Modifier.height(10.dp))
Column(modifier = Modifier.padding(12.dp)) {
Text(
text = item.title,
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.SemiBold,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = when (item.type) {
MediaKind.MOVIE -> "Movie"
MediaKind.SERIES -> "Series"
else -> "Title"
},
style = MaterialTheme.typography.bodySmall,
color = scheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
}
)
}

View File

@@ -1,27 +1,8 @@
package hu.bbara.purefin.ui.screen.libraries.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.card.MediaImageCard
import hu.bbara.purefin.ui.model.LibraryUiModel
@Composable
@@ -30,39 +11,10 @@ fun LibraryListItem(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val scheme = MaterialTheme.colorScheme
Card(
MediaImageCard(
imageUrl = uiModel.posterUrl,
title = uiModel.name,
onClick = onClick,
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
modifier = modifier.fillMaxWidth()
) {
Column(modifier = Modifier.fillMaxWidth()) {
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(16f / 10f)
.clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp))
.background(scheme.surface)
) {
PurefinAsyncImage(
model = uiModel.posterUrl,
contentDescription = uiModel.name,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
}
Spacer(modifier = Modifier.height(10.dp))
Column(modifier = Modifier.padding(12.dp)) {
Text(
text = uiModel.name,
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.SemiBold,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
}
modifier = modifier
)
}

View File

@@ -2,39 +2,26 @@ package hu.bbara.purefin.ui.screen.library
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
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.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import hu.bbara.purefin.feature.browse.library.LibraryViewModel
import hu.bbara.purefin.navigation.LibraryDto
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.card.MediaImageCard
import hu.bbara.purefin.ui.model.EpisodeUiModel
import hu.bbara.purefin.ui.model.MediaUiModel
import hu.bbara.purefin.ui.model.MovieUiModel
@@ -88,8 +75,10 @@ internal fun LibraryPosterGrid(
modifier = modifier.background(MaterialTheme.colorScheme.background)
) {
items(libraryItems, key = { item -> item.id }) { item ->
LibraryMediaCard(
item = item,
MediaImageCard(
imageUrl = item.primaryImageUrl,
title = item.primaryText,
subtitle = item.secondaryText,
onClick = {
when (item) {
is MovieUiModel -> viewModel.onMovieSelected(item.id)
@@ -97,39 +86,7 @@ internal fun LibraryPosterGrid(
is EpisodeUiModel -> Unit
}
}
)
}
}
}
@Composable
private fun LibraryMediaCard(
item: MediaUiModel,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val scheme = MaterialTheme.colorScheme
Card(
onClick = onClick,
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
modifier = modifier.fillMaxWidth()
) {
Column(modifier = Modifier.fillMaxWidth()) {
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(16f / 10f)
.clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp))
.background(scheme.surface)
) {
PurefinAsyncImage(
model = item.primaryImageUrl,
contentDescription = item.primaryText,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
when (item) {
is MovieUiModel, is EpisodeUiModel -> {
WatchStateBadge(
@@ -144,24 +101,6 @@ private fun LibraryMediaCard(
else -> Unit
}
}
Spacer(modifier = Modifier.height(10.dp))
Column(modifier = Modifier.padding(12.dp)) {
Text(
text = item.primaryText,
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.SemiBold,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = item.secondaryText,
style = MaterialTheme.typography.bodySmall,
color = scheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
}
}