diff --git a/src/main/kotlin/com/bolenpad/photophetch/ui/BrowseScreen.kt b/src/main/kotlin/com/bolenpad/photophetch/ui/BrowseScreen.kt index 4d17891..de9c589 100644 --- a/src/main/kotlin/com/bolenpad/photophetch/ui/BrowseScreen.kt +++ b/src/main/kotlin/com/bolenpad/photophetch/ui/BrowseScreen.kt @@ -100,13 +100,23 @@ fun BrowseScreen(state: AppState) { } } - // Dense mode: folder scroll index (photo rows only, no day boundary items) + // Dense mode: folder scroll index (folder header + date labels + photo rows) val denseScrollIndex = remember(folderSections, columnCount) { var idx = 0 + val zoneId = java.time.ZoneId.systemDefault() folderSections.associate { folderSection -> val startIdx = idx idx += 1 // folder header - idx += ((folderSection.allPhotos.size + columnCount - 1) / columnCount) // photo rows + val sorted = folderSection.allPhotos.sortedByDescending { it.dateTaken } + var lastDate: java.time.LocalDate? = null + sorted.chunked(columnCount).forEach { row -> + val rowDate = row.first().dateTaken.atZone(zoneId).toLocalDate() + if (rowDate != lastDate) { + idx += 1 // date label row + lastDate = rowDate + } + idx += 1 // photo row + } folderSection.folder.name to startIdx } } diff --git a/src/main/kotlin/com/bolenpad/photophetch/ui/DensePhotoGrid.kt b/src/main/kotlin/com/bolenpad/photophetch/ui/DensePhotoGrid.kt index 97bf218..60393f2 100644 --- a/src/main/kotlin/com/bolenpad/photophetch/ui/DensePhotoGrid.kt +++ b/src/main/kotlin/com/bolenpad/photophetch/ui/DensePhotoGrid.kt @@ -60,16 +60,13 @@ fun DensePhotoGrid( val zoneId = ZoneId.systemDefault() val dayFmt = remember { DateTimeFormatter.ofPattern("MMM d") } - // Build items: FOLDER_HEADER | PHOTO_ROW (with optional dateLabel on first photo) - // dateLabel is set on the first photo of each new day within a folder - data class PhotoRow( - val photos: List, - val dateLabel: String?, // non-null on first row of a new day - ) + // Build items: FOLDER_HEADER | DATE_LABEL | PHOTO_ROW + data class PhotoRow(val photos: List) data class Item( val type: GridItemType, val folderSection: FolderSection? = null, val photoRow: PhotoRow? = null, + val dateLabel: String? = null, ) val items: List = buildList { @@ -82,11 +79,12 @@ fun DensePhotoGrid( sorted.chunked(columnCount).forEach { rowPhotos -> val rowDate = rowPhotos.first().dateTaken.atZone(zoneId).toLocalDate() - val label = if (rowDate != lastDate) { + if (rowDate != lastDate) { lastDate = rowDate - dayFmt.format(rowDate) - } else null - add(Item(GridItemType.PHOTO_ROW, photoRow = PhotoRow(rowPhotos, label))) + // Slim date label above every day's first row (including first row of folder) + add(Item(GridItemType.DAY_BOUNDARY, dateLabel = dayFmt.format(rowDate))) + } + add(Item(GridItemType.PHOTO_ROW, photoRow = PhotoRow(rowPhotos))) } } } @@ -102,6 +100,7 @@ fun DensePhotoGrid( val item = items[idx] when (item.type) { GridItemType.FOLDER_HEADER -> "hdr-${item.folderSection!!.folder.name}" + GridItemType.DAY_BOUNDARY -> "date-${item.dateLabel}-${idx}" else -> "row-${item.photoRow!!.photos.first().devicePath}" } }, @@ -120,21 +119,42 @@ fun DensePhotoGrid( onDeselectAll = { onDeselectAllFolder(section.allPhotos) }, ) } + GridItemType.DAY_BOUNDARY -> { + // Slim full-width date label — 18dp tall, no wasted space + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp) + .padding(top = 4.dp, bottom = 1.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp), + ) { + Text( + item.dateLabel!!, + fontSize = 10.sp, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + HorizontalDivider( + modifier = Modifier.weight(1f), + thickness = 0.5.dp, + color = MaterialTheme.colorScheme.outlineVariant, + ) + } + } else -> { val row = item.photoRow!! Row( modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp), horizontalArrangement = Arrangement.spacedBy(2.dp), ) { - row.photos.forEachIndexed { colIdx, photo -> + row.photos.forEach { photo -> DenseThumb( photo = photo, isSelected = photo in selectedPhotos, device = device, adbBin = adbBin, onToggle = { onTogglePhoto(photo) }, - // Show date label only on first photo of the row that starts a new day - dateLabel = if (colIdx == 0) row.dateLabel else null, modifier = Modifier.weight(1f), ) } @@ -154,7 +174,6 @@ private fun DenseThumb( device: DeviceInfo?, adbBin: String, onToggle: () -> Unit, - dateLabel: String?, modifier: Modifier = Modifier, ) { var thumbnail by remember(photo.devicePath) { mutableStateOf(null) } @@ -199,20 +218,6 @@ private fun DenseThumb( } } - // Date label overlaid on top-left — only on first photo of a new day - if (dateLabel != null) { - Text( - dateLabel, - fontSize = 9.sp, - fontWeight = FontWeight.SemiBold, - color = Color.White, - modifier = Modifier - .align(Alignment.TopStart) - .background(Color.Black.copy(alpha = 0.55f)) - .padding(horizontal = 4.dp, vertical = 2.dp), - ) - } - // Video badge if (photo.mediaType == MediaType.VIDEO) { Badge(