Removed duplicate GalleryImage boilerplate.
Use .chunked()
Pull out constants (ASPECT_RATIO, IMAGE_SPACING).
This commit is contained in:
davotoula
2025-09-11 21:52:36 +02:00
parent 4dace5aaf8
commit 8f1027b55d

View File

@@ -22,31 +22,37 @@ package com.vitorpamplona.amethyst.ui.components
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.BoxWithConstraints
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
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.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.Dp
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.Size10dp import com.vitorpamplona.amethyst.ui.theme.Size10dp
import com.vitorpamplona.amethyst.ui.theme.Size5dp import com.vitorpamplona.amethyst.ui.theme.Size5dp
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
private const val ASPECT_RATIO = 4f / 3f
private val IMAGE_SPACING: Dp = Size5dp
@Composable @Composable
private fun GalleryImage( private fun GalleryImage(
image: MediaUrlImage, image: MediaUrlImage,
allImages: ImmutableList<MediaUrlImage>, allImages: ImmutableList<MediaUrlImage>,
modifier: Modifier, modifier: Modifier = Modifier,
roundedCorner: Boolean, roundedCorner: Boolean,
contentScale: ContentScale, contentScale: ContentScale,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
@@ -69,77 +75,48 @@ fun ImageGallery(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
roundedCorner: Boolean = true, roundedCorner: Boolean = true,
) { ) {
// Add vertical padding around the entire gallery for better text separation if (images.isEmpty()) return
Column(modifier = modifier.padding(vertical = Size10dp)) { Column(modifier = modifier.padding(vertical = Size10dp)) {
when { when (images.size) {
images.isEmpty() -> { 1 -> SingleImageGallery(images, accountViewModel, roundedCorner)
// No images to display 2 -> TwoImageGallery(images, accountViewModel, roundedCorner)
} 3 -> ThreeImageGallery(images, accountViewModel, roundedCorner)
images.size == 1 -> { 4 -> FourImageGallery(images, accountViewModel, roundedCorner)
// Single image - display full width else -> ManyImageGallery(images, accountViewModel, roundedCorner)
GalleryImage(
image = images.first(),
allImages = images,
modifier = Modifier.fillMaxWidth(),
roundedCorner = roundedCorner,
contentScale = ContentScale.FillWidth,
accountViewModel = accountViewModel,
)
}
images.size == 2 -> {
// Two images - side by side in 4:3 ratio
TwoImageGallery(
images = images,
accountViewModel = accountViewModel,
roundedCorner = roundedCorner,
modifier = Modifier,
)
}
images.size == 3 -> {
// Three images - one large, two small
ThreeImageGallery(
images = images,
accountViewModel = accountViewModel,
roundedCorner = roundedCorner,
modifier = Modifier,
)
}
images.size == 4 -> {
// Four images - 2x2 grid
FourImageGallery(
images = images,
accountViewModel = accountViewModel,
roundedCorner = roundedCorner,
modifier = Modifier,
)
}
else -> {
// Many images - use staggered grid with 4:3 ratio
ManyImageGallery(
images = images,
accountViewModel = accountViewModel,
roundedCorner = roundedCorner,
modifier = Modifier,
)
}
} }
} }
} }
@Composable
private fun SingleImageGallery(
images: ImmutableList<MediaUrlImage>,
accountViewModel: AccountViewModel,
roundedCorner: Boolean,
) {
GalleryImage(
image = images.first(),
allImages = images,
modifier = Modifier.fillMaxWidth(),
roundedCorner = roundedCorner,
contentScale = ContentScale.FillWidth,
accountViewModel = accountViewModel,
)
}
@Composable @Composable
private fun TwoImageGallery( private fun TwoImageGallery(
images: ImmutableList<MediaUrlImage>, images: ImmutableList<MediaUrlImage>,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
roundedCorner: Boolean, roundedCorner: Boolean,
modifier: Modifier,
) { ) {
Row( Row(
modifier = modifier.aspectRatio(4f / 3f), modifier = Modifier.aspectRatio(ASPECT_RATIO),
horizontalArrangement = Arrangement.spacedBy(Size5dp), horizontalArrangement = Arrangement.spacedBy(IMAGE_SPACING),
) { ) {
repeat(2) { index -> images.take(2).forEach { image ->
GalleryImage( GalleryImage(
image = images[index], image = image,
allImages = images, allImages = images,
modifier = Modifier.weight(1f).fillMaxSize(), modifier = Modifier.weight(1f).fillMaxSize(),
roundedCorner = roundedCorner, roundedCorner = roundedCorner,
@@ -155,13 +132,11 @@ private fun ThreeImageGallery(
images: ImmutableList<MediaUrlImage>, images: ImmutableList<MediaUrlImage>,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
roundedCorner: Boolean, roundedCorner: Boolean,
modifier: Modifier,
) { ) {
Row( Row(
modifier = modifier.aspectRatio(4f / 3f), modifier = Modifier.aspectRatio(ASPECT_RATIO),
horizontalArrangement = Arrangement.spacedBy(Size5dp), horizontalArrangement = Arrangement.spacedBy(IMAGE_SPACING),
) { ) {
// Large image on the left
GalleryImage( GalleryImage(
image = images[0], image = images[0],
allImages = images, allImages = images,
@@ -171,14 +146,13 @@ private fun ThreeImageGallery(
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) )
// Two smaller images on the right
Column( Column(
modifier = Modifier.weight(1f).fillMaxSize(), modifier = Modifier.weight(1f).fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(Size5dp), verticalArrangement = Arrangement.spacedBy(IMAGE_SPACING),
) { ) {
repeat(2) { index -> images.drop(1).forEach { image ->
GalleryImage( GalleryImage(
image = images[index + 1], image = image,
allImages = images, allImages = images,
modifier = Modifier.weight(1f).fillMaxSize(), modifier = Modifier.weight(1f).fillMaxSize(),
roundedCorner = roundedCorner, roundedCorner = roundedCorner,
@@ -195,21 +169,19 @@ private fun FourImageGallery(
images: ImmutableList<MediaUrlImage>, images: ImmutableList<MediaUrlImage>,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
roundedCorner: Boolean, roundedCorner: Boolean,
modifier: Modifier,
) { ) {
Column( Column(
modifier = modifier.aspectRatio(4f / 3f), modifier = Modifier.aspectRatio(ASPECT_RATIO),
verticalArrangement = Arrangement.spacedBy(Size5dp), verticalArrangement = Arrangement.spacedBy(IMAGE_SPACING),
) { ) {
repeat(2) { rowIndex -> images.chunked(2).forEach { rowImages ->
Row( Row(
modifier = Modifier.weight(1f).fillMaxWidth(), modifier = Modifier.weight(1f).fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(Size5dp), horizontalArrangement = Arrangement.spacedBy(IMAGE_SPACING),
) { ) {
repeat(2) { colIndex -> rowImages.forEach { image ->
val imageIndex = rowIndex * 2 + colIndex
GalleryImage( GalleryImage(
image = images[imageIndex], image = image,
allImages = images, allImages = images,
modifier = Modifier.weight(1f).fillMaxSize(), modifier = Modifier.weight(1f).fillMaxSize(),
roundedCorner = roundedCorner, roundedCorner = roundedCorner,
@@ -227,71 +199,65 @@ private fun ManyImageGallery(
images: ImmutableList<MediaUrlImage>, images: ImmutableList<MediaUrlImage>,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
roundedCorner: Boolean, roundedCorner: Boolean,
modifier: Modifier,
) { ) {
// Calculate optimal grid layout for many images
val columns = val columns =
when { when {
images.size <= 6 -> 3 // 3 columns for 5-6 images images.size <= 9 -> 3
images.size <= 9 -> 3 // 3 columns for 7-9 images else -> 4
else -> 4 // 4 columns for 10+ images
} }
if (images.size <= 20) { if (images.size <= 20) {
// For smaller sets, use non-lazy Column/Row approach (simpler, no constraint issues) // Non-lazy for small sets
val rows = (images.size + columns - 1) / columns // Ceiling division Column(verticalArrangement = Arrangement.spacedBy(Size5dp)) {
images.chunked(columns).forEach { rowImages ->
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(Size5dp),
) {
repeat(rows) { rowIndex ->
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(Size5dp), horizontalArrangement = Arrangement.spacedBy(Size5dp),
) { ) {
repeat(columns) { colIndex -> rowImages.forEach { image ->
val imageIndex = rowIndex * columns + colIndex GalleryImage(
if (imageIndex < images.size) { image = image,
GalleryImage( allImages = images,
image = images[imageIndex], modifier = Modifier.weight(1f).aspectRatio(1f),
allImages = images, roundedCorner = roundedCorner,
modifier = Modifier.weight(1f).aspectRatio(1f), contentScale = ContentScale.Crop,
roundedCorner = roundedCorner, accountViewModel = accountViewModel,
contentScale = ContentScale.Crop, )
accountViewModel = accountViewModel, }
) repeat(columns - rowImages.size) {
} else { Spacer(Modifier.weight(1f))
// Empty space for incomplete rows
Box(modifier = Modifier.weight(1f))
}
} }
} }
} }
} }
} else { } else {
// For larger sets, use LazyVerticalGrid with explicit height constraint // Lazy for large sets — expands fully, no independent scroll
val rows = (images.size + columns - 1) / columns BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
// Calculate height: (image height + spacing) * rows - last spacing val totalSpacing = Size5dp * (columns - 1)
// Assume square images with 5dp spacing val imageSize = (maxWidth - totalSpacing) / columns
val gridHeight = (100 * rows + 5 * (rows - 1)).dp val rows = (images.size + columns - 1) / columns
val gridHeight = (imageSize * rows) + (Size5dp * (rows - 1))
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(columns), columns = GridCells.Fixed(columns),
modifier = modifier.height(gridHeight), // Explicit height constraint modifier =
verticalArrangement = Arrangement.spacedBy(Size5dp), Modifier
horizontalArrangement = Arrangement.spacedBy(Size5dp), .fillMaxWidth()
userScrollEnabled = false, .height(gridHeight),
) { verticalArrangement = Arrangement.spacedBy(Size5dp),
items(images) { image -> horizontalArrangement = Arrangement.spacedBy(Size5dp),
GalleryImage( userScrollEnabled = false,
image = image, ) {
allImages = images, items(images) { image ->
modifier = Modifier.aspectRatio(1f), GalleryImage(
roundedCorner = roundedCorner, image = image,
contentScale = ContentScale.Crop, allImages = images,
accountViewModel = accountViewModel, modifier = Modifier.size(imageSize),
) roundedCorner = roundedCorner,
contentScale = ContentScale.Crop,
accountViewModel = accountViewModel,
)
}
} }
} }
} }