Skip to content

Commit

Permalink
Implement galleries for various types of images
Browse files Browse the repository at this point in the history
  • Loading branch information
mehmedalijaK committed Mar 4, 2025
1 parent fa604fd commit 2002b04
Show file tree
Hide file tree
Showing 3 changed files with 274 additions and 36 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ dependencies {
implementation(libs.activity.compose)
implementation(libs.androidx.lifecycle.process)
implementation(libs.androidx.activity.ktx)
implementation(libs.androidx.storage)
runtimeOnly(libs.androidx.appcompat)

implementation(platform(libs.compose.bom))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ package net.primal.android.notes.feed.note.ui.attachment
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
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.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -23,6 +30,11 @@ import net.primal.android.core.compose.attachment.model.NoteAttachmentUi
import net.primal.android.notes.feed.note.ui.events.MediaClickEvent
import net.primal.android.theme.AppTheme

const val SINGLE_IMAGE = 1
const val TWO_IMAGES = 2
const val THREE_IMAGES = 3
const val FOUR_IMAGES = 4

@ExperimentalFoundationApi
@Composable
fun NoteMediaAttachmentsHorizontalPager(
Expand All @@ -37,45 +49,63 @@ fun NoteMediaAttachmentsHorizontalPager(

val pagerState = rememberPagerState { imagesCount }

if (imagesCount == 1) {
val attachment = mediaAttachments.first()
NoteMediaAttachment(
attachment = attachment,
blossoms = blossoms,
imageSizeDp = imageSizeDp,
onClick = { positionMs ->
onMediaClick(
MediaClickEvent(
noteId = attachment.noteId,
noteAttachmentType = attachment.type,
mediaUrl = attachment.url,
positionMs = positionMs,
),
)
},
)
} else {
HorizontalPager(state = pagerState, pageSpacing = 12.dp) {
val attachment = mediaAttachments[it]
NoteMediaAttachment(
attachment = attachment,
when (imagesCount) {
SINGLE_IMAGE -> {
SingleImageGallery(
mediaAttachments = mediaAttachments,
blossoms = blossoms,
imageSizeDp = imageSizeDp,
onClick = { positionMs ->
onMediaClick(
MediaClickEvent(
noteId = attachment.noteId,
noteAttachmentType = attachment.type,
mediaUrl = attachment.url,
positionMs = positionMs,
),
)
},
onMediaClick = onMediaClick,
)
}
TWO_IMAGES -> {
TwoImageGallery(
mediaAttachments = mediaAttachments,
blossoms = blossoms,
imageSizeDp = imageSizeDp,
onMediaClick = onMediaClick,
)
}
THREE_IMAGES -> {
ThreeImageGallery(
mediaAttachments = mediaAttachments,
blossoms = blossoms,
imageSizeDp = imageSizeDp,
onMediaClick = onMediaClick,
)
}
FOUR_IMAGES -> {
FourImageGallery(
mediaAttachments = mediaAttachments,
blossoms = blossoms,
imageSizeDp = imageSizeDp,
onMediaClick = onMediaClick,
)
}
else -> {
HorizontalPager(state = pagerState, pageSpacing = 12.dp) {
val attachment = mediaAttachments[it]
NoteMediaAttachment(
modifier = Modifier.clip(AppTheme.shapes.medium),
attachment = attachment,
blossoms = blossoms,
imageSizeDp = imageSizeDp,
onClick = { positionMs ->
onMediaClick(
MediaClickEvent(
noteId = attachment.noteId,
noteAttachmentType = attachment.type,
mediaUrl = attachment.url,
positionMs = positionMs,
),
)
},
)
}
}
}

if (imagesCount > 1) {
if (imagesCount > FOUR_IMAGES) {
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
Expand All @@ -95,17 +125,222 @@ fun NoteMediaAttachmentsHorizontalPager(
}
}

@Composable
private fun FourImageGallery(
mediaAttachments: List<NoteAttachmentUi>,
blossoms: List<String>,
imageSizeDp: DpSize,
onMediaClick: (MediaClickEvent) -> Unit,
) {
val shapeMatrix = listOf(
RoundedCornerShape(AppTheme.shapes.medium.topStart, CornerSize(0.dp), CornerSize(0.dp), CornerSize(0.dp)),
RoundedCornerShape(CornerSize(0.dp), AppTheme.shapes.medium.topEnd, CornerSize(0.dp), CornerSize(0.dp)),
RoundedCornerShape(CornerSize(0.dp), CornerSize(0.dp), CornerSize(0.dp), AppTheme.shapes.medium.bottomEnd),
RoundedCornerShape(CornerSize(0.dp), CornerSize(0.dp), AppTheme.shapes.medium.bottomStart, CornerSize(0.dp)),
)

Column(modifier = Modifier.fillMaxWidth()) {
mediaAttachments.chunked(2).forEachIndexed { rowIndex, rowAttachments ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
rowAttachments.forEachIndexed { index, attachment ->
NoteMediaAttachment(
modifier = Modifier
.weight(1f)
.aspectRatio(1f)
.clip(shapeMatrix[rowIndex * 2 + index]),
attachment = attachment,
blossoms = blossoms,
imageSizeDp = imageSizeDp,
onClick = { positionMs ->
onMediaClick(
MediaClickEvent(
noteId = attachment.noteId,
noteAttachmentType = attachment.type,
mediaUrl = attachment.url,
positionMs = positionMs,
),
)
},
)
}
}
}
}
}

@Composable
private fun ThreeImageGallery(
mediaAttachments: List<NoteAttachmentUi>,
blossoms: List<String>,
imageSizeDp: DpSize,
onMediaClick: (MediaClickEvent) -> Unit,
) {
Column(
modifier = Modifier.fillMaxWidth(),
) {
NoteMediaAttachment(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(2f / 1f)
.clip(
RoundedCornerShape(
topStart = AppTheme.shapes.medium.topStart,
topEnd = AppTheme.shapes.medium.topEnd,
bottomStart = CornerSize(0.dp),
bottomEnd = CornerSize(0.dp),
),
),
attachment = mediaAttachments[0],
blossoms = blossoms,
imageSizeDp = imageSizeDp,
onClick = { positionMs ->
onMediaClick(
MediaClickEvent(
noteId = mediaAttachments[0].noteId,
noteAttachmentType = mediaAttachments[0].type,
mediaUrl = mediaAttachments[0].url,
positionMs = positionMs,
),
)
},
)

Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
mediaAttachments.drop(1).take(2).forEachIndexed { index, attachment ->
NoteMediaAttachment(
modifier = Modifier
.weight(1f)
.aspectRatio(1f)
.clip(
if (index == 0) {
RoundedCornerShape(
topStart = CornerSize(0.dp),
topEnd = CornerSize(0.dp),
bottomStart = AppTheme.shapes.medium.bottomStart,
bottomEnd = CornerSize(0.dp),
)
} else {
RoundedCornerShape(
topStart = CornerSize(0.dp),
topEnd = CornerSize(0.dp),
bottomStart = CornerSize(0.dp),
bottomEnd = AppTheme.shapes.medium.bottomEnd,
)
},
),
attachment = attachment,
blossoms = blossoms,
imageSizeDp = imageSizeDp,
onClick = { positionMs ->
onMediaClick(
MediaClickEvent(
noteId = attachment.noteId,
noteAttachmentType = attachment.type,
mediaUrl = attachment.url,
positionMs = positionMs,
),
)
},
)
}
}
}
}

@Composable
private fun TwoImageGallery(
mediaAttachments: List<NoteAttachmentUi>,
blossoms: List<String>,
imageSizeDp: DpSize,
onMediaClick: (MediaClickEvent) -> Unit,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
mediaAttachments.take(2).forEachIndexed { index, attachment ->
NoteMediaAttachment(
modifier = Modifier
.weight(1f)
.aspectRatio(1f)
.clip(
if (index == 0) {
RoundedCornerShape(
topStart = AppTheme.shapes.medium.topStart,
topEnd = CornerSize(0.dp),
bottomStart = AppTheme.shapes.medium.bottomStart,
bottomEnd = CornerSize(0.dp),
)
} else {
RoundedCornerShape(
topStart = CornerSize(0.dp),
topEnd = AppTheme.shapes.medium.topEnd,
bottomStart = CornerSize(0.dp),
bottomEnd = AppTheme.shapes.medium.bottomEnd,
)
},
),
attachment = attachment,
blossoms = blossoms,
imageSizeDp = imageSizeDp,
onClick = { positionMs ->
onMediaClick(
MediaClickEvent(
noteId = attachment.noteId,
noteAttachmentType = attachment.type,
mediaUrl = attachment.url,
positionMs = positionMs,
),
)
},
)
}
}
}

@Composable
private fun SingleImageGallery(
mediaAttachments: List<NoteAttachmentUi>,
blossoms: List<String>,
imageSizeDp: DpSize,
onMediaClick: (MediaClickEvent) -> Unit,
) {
val attachment = mediaAttachments.first()
NoteMediaAttachment(
modifier = Modifier.clip(AppTheme.shapes.medium),
attachment = attachment,
blossoms = blossoms,
imageSizeDp = imageSizeDp,
onClick = { positionMs ->
onMediaClick(
MediaClickEvent(
noteId = attachment.noteId,
noteAttachmentType = attachment.type,
mediaUrl = attachment.url,
positionMs = positionMs,
),
)
},
)
}

@Composable
private fun NoteMediaAttachment(
attachment: NoteAttachmentUi,
blossoms: List<String>,
imageSizeDp: DpSize,
onClick: (positionMs: Long) -> Unit,
modifier: Modifier = Modifier,
) {
BoxWithConstraints(
modifier = Modifier
.padding(vertical = 4.dp)
.clip(AppTheme.shapes.medium),
modifier = modifier
.padding(vertical = 4.dp),
contentAlignment = Alignment.Center,
) {
when (attachment.type) {
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ telephoto = "0.14.0"
timber = "5.0.1"
url-detector = "0.1.23"
webkit = "1.12.1"
storage = "1.5.0"

[libraries]
# Core libs
Expand Down Expand Up @@ -193,6 +194,7 @@ androidx-camera-core = { group = "androidx.camera", name = "camera-core", versio
androidx-camera-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "camera" }
androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "camera" }
androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "camera" }
androidx-storage = { group = "androidx.test.services", name = "storage", version.ref = "storage" }

[bundles]
hilt = ["hilt-android", "navigation-hilt"]
Expand Down

0 comments on commit 2002b04

Please sign in to comment.