mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-22 18:02:05 +02:00
refactor
Removed duplicate GalleryImage boilerplate. Use .chunked() Pull out constants (ASPECT_RATIO, IMAGE_SPACING).
This commit is contained in:
@@ -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,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user