extract PosterCard into a common UI component

- Move `PosterCard` from `HomeSections.kt` to a new standalone file `PosterCard.kt` in `hu.bbara.purefin.common.ui`.
- Refactor `PosterCard` layout from a `Box` to a `Column` for better text placement.
- Update text styling on `PosterCard` to limit to 1 line and adjust padding.
- Clean up code formatting and imports in `HomeSections.kt`.
This commit is contained in:
2026-01-19 19:41:05 +01:00
parent a515819daa
commit 14efa152a3
2 changed files with 89 additions and 77 deletions

View File

@@ -39,6 +39,7 @@ import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import hu.bbara.purefin.app.home.HomePageViewModel import hu.bbara.purefin.app.home.HomePageViewModel
import hu.bbara.purefin.common.ui.PosterCard
import hu.bbara.purefin.image.JellyfinImageHelper import hu.bbara.purefin.image.JellyfinImageHelper
import hu.bbara.purefin.player.PlayerActivity import hu.bbara.purefin.player.PlayerActivity
import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.BaseItemKind
@@ -47,14 +48,10 @@ import kotlin.math.nextUp
@Composable @Composable
fun ContinueWatchingSection( fun ContinueWatchingSection(
items: List<ContinueWatchingItem>, items: List<ContinueWatchingItem>, colors: HomeColors, modifier: Modifier = Modifier
colors: HomeColors,
modifier: Modifier = Modifier
) { ) {
SectionHeader( SectionHeader(
title = "Continue Watching", title = "Continue Watching", action = null, colors = colors
action = null,
colors = colors
) )
LazyRow( LazyRow(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
@@ -62,12 +59,9 @@ fun ContinueWatchingSection(
horizontalArrangement = Arrangement.spacedBy(16.dp) horizontalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
items( items(
items = items, items = items, key = { it.id }) { item ->
key = { it.id }
) { item ->
ContinueWatchingCard( ContinueWatchingCard(
item = item, item = item, colors = colors
colors = colors
) )
} }
} }
@@ -104,18 +98,17 @@ fun ContinueWatchingCard(
) { ) {
AsyncImage( AsyncImage(
model = JellyfinImageHelper.toImageUrl( model = JellyfinImageHelper.toImageUrl(
url = "https://jellyfin.bbara.hu", url = "https://jellyfin.bbara.hu", itemId = item.id, type = ImageType.PRIMARY
itemId = item.id,
type = ImageType.PRIMARY
), ),
contentDescription = null, contentDescription = null,
modifier = Modifier.fillMaxSize() modifier = Modifier
.fillMaxSize()
.clickable { .clickable {
openItem(item) openItem(item)
}, },
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
) )
Box( Box(
modifier = Modifier modifier = Modifier
.matchParentSize() .matchParentSize()
@@ -136,12 +129,11 @@ fun ContinueWatchingCard(
) )
} }
Button( Button(
modifier = Modifier.align(Alignment.BottomEnd), modifier = Modifier.align(Alignment.BottomEnd), onClick = {
onClick = {
val intent = Intent(context, PlayerActivity::class.java) val intent = Intent(context, PlayerActivity::class.java)
intent.putExtra("MEDIA_ID", item.id.toString()) intent.putExtra("MEDIA_ID", item.id.toString())
context.startActivity(intent) context.startActivity(intent)
}) { }) {
Icon(imageVector = Icons.Outlined.PlayArrow, contentDescription = "Play") Icon(imageVector = Icons.Outlined.PlayArrow, contentDescription = "Play")
} }
} }
@@ -174,9 +166,7 @@ fun LibraryPosterSection(
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
SectionHeader( SectionHeader(
title = title, title = title, action = action, colors = colors
action = action,
colors = colors
) )
LazyRow( LazyRow(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
@@ -184,9 +174,7 @@ fun LibraryPosterSection(
horizontalArrangement = Arrangement.spacedBy(16.dp) horizontalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
items( items(
items = items, items = items, key = { it.id }) { item ->
key = { it.id }
) { item ->
PosterCard( PosterCard(
item = item, item = item,
colors = colors, colors = colors,
@@ -195,52 +183,6 @@ fun LibraryPosterSection(
} }
} }
@Composable
fun PosterCard(
item: PosterItem,
colors: HomeColors,
modifier: Modifier = Modifier,
viewModel: HomePageViewModel = hiltViewModel()
) {
fun openItem(posterItem: PosterItem)
{
when (posterItem.type) {
BaseItemKind.MOVIE -> viewModel.onMovieSelected(posterItem.id.toString())
BaseItemKind.SERIES -> viewModel.onSeriesSelected(posterItem.id.toString())
BaseItemKind.EPISODE -> viewModel.onSelectEpisode(posterItem.id.toString())
else -> {}
}
}
Box(
modifier = modifier
.width(144.dp)
.aspectRatio(2f / 3f)
.shadow(10.dp, RoundedCornerShape(14.dp))
.clip(RoundedCornerShape(14.dp))
.background(colors.card)
.clickable(onClick = { openItem(item) })
) {
AsyncImage(
model = JellyfinImageHelper.toImageUrl(url = "https://jellyfin.bbara.hu", itemId = item.imageItemId, type = ImageType.PRIMARY),
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
Text(
text = item.title,
color = colors.textPrimary,
fontSize = 13.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier
.align(Alignment.BottomStart)
.padding(10.dp),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
}
@Composable @Composable
fun SectionHeader( fun SectionHeader(
title: String, title: String,
@@ -257,10 +199,7 @@ fun SectionHeader(
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
Text( Text(
text = title, text = title, color = colors.textPrimary, fontSize = 20.sp, fontWeight = FontWeight.Bold
color = colors.textPrimary,
fontSize = 20.sp,
fontWeight = FontWeight.Bold
) )
if (action != null) { if (action != null) {
Text( Text(
@@ -268,8 +207,7 @@ fun SectionHeader(
color = colors.primary, color = colors.primary,
fontSize = 14.sp, fontSize = 14.sp,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
modifier = Modifier.clickable { onActionClick() } modifier = Modifier.clickable { onActionClick() })
)
} }
} }
} }

View File

@@ -0,0 +1,74 @@
package hu.bbara.purefin.common.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
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.draw.shadow
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.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import coil3.compose.AsyncImage
import hu.bbara.purefin.app.home.HomePageViewModel
import hu.bbara.purefin.app.home.ui.HomeColors
import hu.bbara.purefin.app.home.ui.PosterItem
import hu.bbara.purefin.image.JellyfinImageHelper
import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.ImageType
@Composable
fun PosterCard(
item: PosterItem,
colors: HomeColors,
modifier: Modifier = Modifier,
viewModel: HomePageViewModel = hiltViewModel()
) {
fun openItem(posterItem: PosterItem) {
when (posterItem.type) {
BaseItemKind.MOVIE -> viewModel.onMovieSelected(posterItem.id.toString())
BaseItemKind.SERIES -> viewModel.onSeriesSelected(posterItem.id.toString())
BaseItemKind.EPISODE -> viewModel.onSelectEpisode(posterItem.id.toString())
else -> {}
}
}
Column(
modifier = Modifier
.width(144.dp)
) {
AsyncImage(
model = JellyfinImageHelper.toImageUrl(
url = "https://jellyfin.bbara.hu",
itemId = item.imageItemId,
type = ImageType.PRIMARY
),
contentDescription = null,
modifier = Modifier
.aspectRatio(2f / 3f)
.shadow(10.dp, RoundedCornerShape(14.dp))
.clip(RoundedCornerShape(14.dp))
.background(colors.card)
.clickable(onClick = { openItem(item) }),
contentScale = ContentScale.Crop
)
Text(
text = item.title,
color = colors.textPrimary,
fontSize = 13.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(top = 8.dp, start = 4.dp, end = 4.dp, bottom = 8.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}