From 30181846ecbc2fc3c9ed52d0d40ddda2d08434de Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 2 Jun 2026 07:12:11 +0000 Subject: [PATCH 1/3] Upgrade Compose BOM to 2024.12.01 (Compose 1.7.6) (v0.21.1-beta.1) Bumps composeBom from 2024.06.00 to 2024.12.01, bringing Compose UI 1.7.6 and Material3 1.3.1. No user-visible changes. Unblocks use of stable Compose 1.7 APIs (e.g. animateItem() instead of animateItemPlacement()). https://claude.ai/code/session_01AGmvoYfqqkLA9SUWV6myXW --- CHANGELOG.md | 5 +++++ app/build.gradle.kts | 4 ++-- gradle/libs.versions.toml | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2e039e..812e5e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,11 @@ Rules: --- +## [0.21.1-beta.1] - 2026-06-02 + +### Changed +- Compose BOM upgraded from `2024.06.00` to `2024.12.01` (Compose UI 1.7.6, Material3 1.3.1). No user-visible changes; enables use of stable Compose 1.7 APIs internally. + ## [0.21.0-beta.1] - 2026-06-01 ### Added diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cfc2758..6bcc54e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "com.mapgie.goflo" minSdk = 26 targetSdk = 34 - versionCode = 55 - versionName = "0.21.0-beta.1" + versionCode = 56 + versionName = "0.21.1-beta.1" } signingConfigs { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 34ed307..74e8fc6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ ksp = "2.0.0-1.0.21" coreKtx = "1.13.1" lifecycleRuntime = "2.8.1" activityCompose = "1.9.0" -composeBom = "2024.06.00" +composeBom = "2024.12.01" navigationCompose = "2.7.7" room = "2.6.1" datastorePreferences = "1.1.1" From 83ed96067db41d49f3b2fdb398ed81306fb8dc5c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 2 Jun 2026 07:14:56 +0000 Subject: [PATCH 2/3] Add missing libraries to Licenses screen androidx.compose.ui:ui-graphics and ui-tooling-preview are both implementation deps (shipped in the release APK) but were absent from the Open Source Licences screen. https://claude.ai/code/session_01AGmvoYfqqkLA9SUWV6myXW --- .../java/com/mapgie/goflo/ui/screens/licenses/LicensesScreen.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/mapgie/goflo/ui/screens/licenses/LicensesScreen.kt b/app/src/main/java/com/mapgie/goflo/ui/screens/licenses/LicensesScreen.kt index 69071ef..609daca 100644 --- a/app/src/main/java/com/mapgie/goflo/ui/screens/licenses/LicensesScreen.kt +++ b/app/src/main/java/com/mapgie/goflo/ui/screens/licenses/LicensesScreen.kt @@ -39,6 +39,8 @@ private val apache2Libraries = listOf( Library("AndroidX Activity Compose", "The Android Open Source Project"), Library("AndroidX Compose BOM", "The Android Open Source Project"), Library("AndroidX Compose UI", "The Android Open Source Project"), + Library("AndroidX Compose UI Graphics", "The Android Open Source Project"), + Library("AndroidX Compose UI Tooling Preview", "The Android Open Source Project"), Library("AndroidX Compose Material3", "The Android Open Source Project"), Library("AndroidX Compose Material Icons Extended", "The Android Open Source Project"), Library("AndroidX Compose UI Text Google Fonts", "The Android Open Source Project"), From 1ecf6ecfc770a9766affc626d2ad3512bd457300 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 2 Jun 2026 07:18:45 +0000 Subject: [PATCH 3/3] Drag to reorder active tracking categories (v0.22.0-beta.1) Long-press the drag handle on any active tracking category to pick it up and drag it to a new position. Items animate into place as you drag. The new order is written to displayOrder on release and persists across app restarts. Works alongside the existing swipe-to-archive / delete gestures. https://claude.ai/code/session_01AGmvoYfqqkLA9SUWV6myXW --- CHANGELOG.md | 5 + app/build.gradle.kts | 4 +- .../data/repository/TrackingRepository.kt | 9 ++ .../categories/ManageCategoriesScreen.kt | 102 ++++++++++++++++-- .../categories/ManageCategoriesViewModel.kt | 4 + 5 files changed, 116 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 812e5e5..17a3bdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,11 @@ Rules: --- +## [0.22.0-beta.1] - 2026-06-02 + +### Added +- **Drag to reorder categories** — long-press the drag handle on any active tracking category to reorder it. The new order is persisted immediately. Works alongside existing swipe-to-archive and swipe-to-delete gestures. + ## [0.21.1-beta.1] - 2026-06-02 ### Changed diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6bcc54e..a260a6c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "com.mapgie.goflo" minSdk = 26 targetSdk = 34 - versionCode = 56 - versionName = "0.21.1-beta.1" + versionCode = 57 + versionName = "0.22.0-beta.1" } signingConfigs { diff --git a/app/src/main/java/com/mapgie/goflo/data/repository/TrackingRepository.kt b/app/src/main/java/com/mapgie/goflo/data/repository/TrackingRepository.kt index 29f9788..2ae0a96 100644 --- a/app/src/main/java/com/mapgie/goflo/data/repository/TrackingRepository.kt +++ b/app/src/main/java/com/mapgie/goflo/data/repository/TrackingRepository.kt @@ -499,4 +499,13 @@ class TrackingRepository( } } } + + suspend fun reorderCategories(orderedIds: List) { + orderedIds.forEachIndexed { newOrder, id -> + val cat = categoryDao.getCategoryByIdOnce(id) ?: return@forEachIndexed + if (cat.displayOrder != newOrder) { + categoryDao.updateCategory(cat.copy(displayOrder = newOrder)) + } + } + } } diff --git a/app/src/main/java/com/mapgie/goflo/ui/screens/categories/ManageCategoriesScreen.kt b/app/src/main/java/com/mapgie/goflo/ui/screens/categories/ManageCategoriesScreen.kt index 76b157a..ba3bb17 100644 --- a/app/src/main/java/com/mapgie/goflo/ui/screens/categories/ManageCategoriesScreen.kt +++ b/app/src/main/java/com/mapgie/goflo/ui/screens/categories/ManageCategoriesScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown +import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress import androidx.compose.foundation.gestures.drag import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -28,6 +29,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -40,6 +42,7 @@ import androidx.compose.material.icons.filled.Archive import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.ChevronRight import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.DragHandle import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material.icons.filled.Unarchive @@ -67,9 +70,12 @@ import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberSwipeToDismissBoxState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable @@ -302,20 +308,77 @@ fun ManageCategoriesScreen( val archived = state.categories.filter { it.isArchived } var archivedExpanded by rememberSaveable { mutableStateOf(false) } + val lazyListState = rememberLazyListState() + val localActive = remember { mutableStateListOf() } + var draggedIndex by remember { mutableStateOf(null) } + var dragOffsetY by remember { mutableFloatStateOf(0f) } + + LaunchedEffect(active) { + if (draggedIndex == null) { + localActive.clear() + localActive.addAll(active) + } + } + LazyColumn( + state = lazyListState, modifier = Modifier .fillMaxSize() .padding(padding) .padding(horizontal = 16.dp, vertical = 12.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { - items(active, key = { it.id }) { category -> + items(localActive, key = { it.id }) { category -> + val dragModifier = Modifier.pointerInput(category.id) { + detectDragGesturesAfterLongPress( + onDragStart = { + draggedIndex = localActive.indexOfFirst { it.id == category.id } + .takeIf { it >= 0 } + dragOffsetY = 0f + }, + onDrag = { change, dragAmount -> + change.consume() + val idx = draggedIndex ?: return@detectDragGesturesAfterLongPress + dragOffsetY += dragAmount.y + val itemH = lazyListState.layoutInfo.visibleItemsInfo + .firstOrNull { it.key == localActive.getOrNull(idx)?.id } + ?.size?.toFloat() ?: 0f + if (itemH > 0f) { + when { + dragOffsetY > itemH / 2 && idx < localActive.size - 1 -> { + localActive.add(idx + 1, localActive.removeAt(idx)) + draggedIndex = idx + 1 + dragOffsetY -= itemH + } + dragOffsetY < -(itemH / 2) && idx > 0 -> { + localActive.add(idx - 1, localActive.removeAt(idx)) + draggedIndex = idx - 1 + dragOffsetY += itemH + } + } + } + }, + onDragEnd = { + draggedIndex = null + dragOffsetY = 0f + viewModel.reorderCategories(localActive.map { it.id }) + }, + onDragCancel = { + draggedIndex = null + dragOffsetY = 0f + localActive.clear() + localActive.addAll(active) + } + ) + } SwipeableCategoryRow( category = category, onClick = { onNavigateToCategory(category.id) }, onEditAppearance = { pendingEditAppearance = category.id }, onArchiveToggle = { requestArchive(category) }, - onDelete = { pendingDelete = category.id } + onDelete = { pendingDelete = category.id }, + modifier = Modifier.animateItem(), + dragModifier = dragModifier, ) } @@ -369,10 +432,18 @@ private fun SwipeableCategoryRow( onClick: () -> Unit, onEditAppearance: () -> Unit, onArchiveToggle: () -> Unit, - onDelete: () -> Unit + onDelete: () -> Unit, + modifier: Modifier = Modifier, + dragModifier: Modifier? = null, ) { if (category.isSystem) { - CategoryRow(category = category, onClick = onClick, onEditAppearance = onEditAppearance) + CategoryRow( + category = category, + onClick = onClick, + onEditAppearance = onEditAppearance, + modifier = modifier, + dragModifier = dragModifier, + ) return } @@ -388,6 +459,7 @@ private fun SwipeableCategoryRow( SwipeToDismissBox( state = dismissState, + modifier = modifier, enableDismissFromStartToEnd = true, enableDismissFromEndToStart = true, backgroundContent = { @@ -451,7 +523,12 @@ private fun SwipeableCategoryRow( } } ) { - CategoryRow(category = category, onClick = onClick, onEditAppearance = onEditAppearance) + CategoryRow( + category = category, + onClick = onClick, + onEditAppearance = onEditAppearance, + dragModifier = dragModifier, + ) } } @@ -460,13 +537,15 @@ private fun CategoryRow( category: TrackingCategory, onClick: () -> Unit, onEditAppearance: () -> Unit, + modifier: Modifier = Modifier, + dragModifier: Modifier? = null, ) { val bubbleColor = category.colorToken.toCategoryColor() val iconTint = category.colorToken.toCategoryOnColor() Card( onClick = onClick, - modifier = Modifier + modifier = modifier .fillMaxWidth() .alpha(if (category.isArchived) 0.55f else 1f), colors = CardDefaults.cardColors( @@ -524,6 +603,17 @@ private fun CategoryRow( contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant ) + + if (dragModifier != null) { + Icon( + imageVector = Icons.Default.DragHandle, + contentDescription = "Drag to reorder", + tint = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = dragModifier + .size(44.dp) + .padding(10.dp) + ) + } } } } diff --git a/app/src/main/java/com/mapgie/goflo/ui/screens/categories/ManageCategoriesViewModel.kt b/app/src/main/java/com/mapgie/goflo/ui/screens/categories/ManageCategoriesViewModel.kt index baef657..cc6f282 100644 --- a/app/src/main/java/com/mapgie/goflo/ui/screens/categories/ManageCategoriesViewModel.kt +++ b/app/src/main/java/com/mapgie/goflo/ui/screens/categories/ManageCategoriesViewModel.kt @@ -87,6 +87,10 @@ class ManageCategoriesViewModel( viewModelScope.launch { repository.deleteCategory(category) } } + fun reorderCategories(orderedIds: List) { + viewModelScope.launch { repository.reorderCategories(orderedIds) } + } + fun setArchiveWarningDisabled(disabled: Boolean) { viewModelScope.launch { store.setArchiveWarningDisabled(disabled) } }