From 3c9a922eb4354228676c9de791d9a40ce34343ab Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 10 Sep 2026 20:47:13 +0000 Subject: [PATCH] Day sheet: delete any entry, group flow with the period, honour group colours The calendar day sheet was the one place every stored log is listed, and it offered no delete at all, so a Flow log on a day that is no longer a period day (or any system-category log) could never be removed. Each row now has a "delete" action with a confirmation; a row with several entries of one category deletes them all, and timed entries can be deleted one at a time. Deletion goes through HomeViewModel; the sheet's data is a live query so it refreshes itself. Flow and Symptoms now sit inside the period box on a period day (flow first) instead of under "Tracked", where they read as unrelated. Rows used the category's raw colour token, so a category that inherits its colour from its group rendered a neutral bubble and near-invisible value text ("No", "Yes"). The sheet now resolves the effective token through the groups, and a category with no colour at all shows its value in the normal text colour. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01ELZpYZSRB1DMaJiXvkcVSD --- .../mapgie/goflo/ui/components/DayLogSheet.kt | 109 +++++++++++++++--- .../goflo/ui/screens/home/HomeScreen.kt | 2 + .../goflo/ui/screens/home/HomeViewModel.kt | 20 +++- .../day-sheet-delete-and-colours.json | 6 + 4 files changed, 121 insertions(+), 16 deletions(-) create mode 100644 changelog/unreleased/day-sheet-delete-and-colours.json diff --git a/app/src/main/java/com/mapgie/goflo/ui/components/DayLogSheet.kt b/app/src/main/java/com/mapgie/goflo/ui/components/DayLogSheet.kt index 94dcb1a..e26e766 100644 --- a/app/src/main/java/com/mapgie/goflo/ui/components/DayLogSheet.kt +++ b/app/src/main/java/com/mapgie/goflo/ui/components/DayLogSheet.kt @@ -18,6 +18,8 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.ButtonDefaults import androidx.compose.material.icons.outlined.WaterDrop import androidx.compose.material3.Checkbox import androidx.compose.material3.DropdownMenu @@ -46,10 +48,14 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.unit.dp +import com.mapgie.goflo.data.database.entities.Group import com.mapgie.goflo.data.database.entities.PeriodEntry import com.mapgie.goflo.data.database.entities.TrackingCategory +import com.mapgie.goflo.data.database.entities.TrackingLog import com.mapgie.goflo.data.repository.TrackingLogWithValues +import com.mapgie.goflo.ui.util.COLOR_TOKEN_INHERIT import com.mapgie.goflo.ui.util.decodeScaleLabels +import com.mapgie.goflo.ui.util.effectiveColorToken import com.mapgie.goflo.ui.util.toCategoryColor import com.mapgie.goflo.ui.util.toCategoryIcon import com.mapgie.goflo.ui.util.toCategoryOnColor @@ -67,6 +73,10 @@ fun DayLogSheet( onDismiss: () -> Unit, onEditPeriod: (Long) -> Unit, onEditTrackingLog: (categoryId: Long, logId: Long) -> Unit, + /** Deletes stored logs straight from the sheet (after confirmation). */ + onDeleteTrackingLogs: (List) -> Unit, + /** Groups, so categories that inherit their colour render in it. */ + groups: List = emptyList(), /** Opens the full log menu so one category can be picked directly. */ onLogMore: () -> Unit, /** Opens the unified day screen for this day, the standard logging surface. */ @@ -84,6 +94,31 @@ fun DayLogSheet( } var showAgainstTime by rememberSaveable { mutableStateOf(false) } var showMenu by remember { mutableStateOf(false) } + /** Logs awaiting delete confirmation; empty when no dialog is open. */ + var pendingDelete by remember { mutableStateOf>(emptyList()) } + + if (pendingDelete.isNotEmpty()) { + val name = pendingDelete.first().category?.name ?: "this" + val n = pendingDelete.size + AlertDialog( + onDismissRequest = { pendingDelete = emptyList() }, + title = { Text(if (n == 1) "Delete this entry?" else "Delete these entries?") }, + text = { Text( + if (n == 1) "The $name entry for ${date.format(headerFormat)} will be permanently removed." + else "All $n $name entries for ${date.format(headerFormat)} will be permanently removed." + ) }, + confirmButton = { + TextButton( + onClick = { + onDeleteTrackingLogs(pendingDelete.map { it.log }) + pendingDelete = emptyList() + }, + colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.error), + ) { Text("Delete") } + }, + dismissButton = { TextButton(onClick = { pendingDelete = emptyList() }) { Text("Cancel") } }, + ) + } ModalBottomSheet( onDismissRequest = onDismiss, @@ -139,12 +174,23 @@ fun DayLogSheet( HorizontalDivider() - // Split tracked categories into those logged with the period (the - // ones pinned into the day screen's flow context) and everything else. + // Split tracked categories into those that belong with the period + // (flow and symptoms, plus the ones pinned into the day screen's + // period context) and everything else. Flow leads. val periodLinkedCats = if (period != null) { - categoryOrder.filter { catId -> - logsByCategory[catId]?.firstOrNull()?.category?.showInLogPeriod == true - } + categoryOrder + .filter { catId -> + val cat = logsByCategory[catId]?.firstOrNull()?.category + cat != null && (cat.isSystem || cat.showInLogPeriod) + } + .sortedBy { catId -> + val cat = logsByCategory[catId]?.firstOrNull()?.category + when { + cat?.systemKey == "flow" -> 0 + cat?.isSystem == true -> 1 + else -> 2 + } + } } else { emptyList() } @@ -186,8 +232,10 @@ fun DayLogSheet( ) CategoryLogEntry( entries = entries, + groups = groups, showAgainstTime = showAgainstTime, - onEditTrackingLog = onEditTrackingLog + onEditTrackingLog = onEditTrackingLog, + onDelete = { pendingDelete = it }, ) } } @@ -207,8 +255,10 @@ fun DayLogSheet( val entries = logsByCategory[catId] ?: return@forEach CategoryLogEntry( entries = entries, + groups = groups, showAgainstTime = showAgainstTime, - onEditTrackingLog = onEditTrackingLog + onEditTrackingLog = onEditTrackingLog, + onDelete = { pendingDelete = it }, ) } @@ -243,15 +293,22 @@ fun DayLogSheet( @Composable private fun CategoryLogEntry( entries: List, + groups: List, showAgainstTime: Boolean, onEditTrackingLog: (categoryId: Long, logId: Long) -> Unit, + /** Asks to delete the given logs (one timed entry, or the whole row). */ + onDelete: (List) -> Unit, ) { val first = entries.first() val category = first.category - val bubbleColor = category?.colorToken?.toCategoryColor() - ?: MaterialTheme.colorScheme.secondary - val onBubble = category?.colorToken?.toCategoryOnColor() - ?: MaterialTheme.colorScheme.onSecondary + // The group's colour when the category inherits it; a category with no + // colour of its own and no group is neutral, and its value text must + // then read as ordinary text rather than vanish into the surface tint. + val token = category?.effectiveColorToken(groups) + val bubbleColor = token?.toCategoryColor() ?: MaterialTheme.colorScheme.secondary + val onBubble = token?.toCategoryOnColor() ?: MaterialTheme.colorScheme.onSecondary + val valueColor = if (token == null || token == COLOR_TOKEN_INHERIT) + MaterialTheme.colorScheme.onSurface else bubbleColor val icon = category?.iconName?.toCategoryIcon()?.vector val hasTimedEntries = showAgainstTime && @@ -263,7 +320,8 @@ private fun CategoryLogEntry( iconColor = bubbleColor, iconOnColor = onBubble, label = category?.name ?: "Unknown", - onEdit = { onEditTrackingLog(first.log.categoryId, entries.last().log.id) } + onEdit = { onEditTrackingLog(first.log.categoryId, entries.last().log.id) }, + onDelete = { onDelete(entries) }, ) { if (hasTimedEntries) { // Show each entry with its timestamp on its own line @@ -297,6 +355,16 @@ private fun CategoryLogEntry( color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f) ) } + TextButton( + onClick = { onDelete(listOf(entry)) }, + contentPadding = PaddingValues(horizontal = 4.dp, vertical = 0.dp) + ) { + Text( + text = "delete", + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f) + ) + } } } } else { @@ -314,7 +382,7 @@ private fun CategoryLogEntry( Text( text = allDisplayValues[0], style = MaterialTheme.typography.titleMedium, - color = bubbleColor + color = valueColor ) } else { Text( @@ -357,7 +425,8 @@ private fun LogEntryRow( iconOnColor: Color, label: String, onEdit: () -> Unit, - content: @Composable ColumnScope.() -> Unit + onDelete: (() -> Unit)? = null, + content: @Composable ColumnScope.() -> Unit, ) { Row( modifier = Modifier.fillMaxWidth(), @@ -404,6 +473,18 @@ private fun LogEntryRow( color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f) ) } + if (onDelete != null) { + TextButton( + onClick = onDelete, + contentPadding = PaddingValues(horizontal = 4.dp, vertical = 0.dp) + ) { + Text( + text = "delete", + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f) + ) + } + } } } diff --git a/app/src/main/java/com/mapgie/goflo/ui/screens/home/HomeScreen.kt b/app/src/main/java/com/mapgie/goflo/ui/screens/home/HomeScreen.kt index 814ba99..316ed02 100644 --- a/app/src/main/java/com/mapgie/goflo/ui/screens/home/HomeScreen.kt +++ b/app/src/main/java/com/mapgie/goflo/ui/screens/home/HomeScreen.kt @@ -143,7 +143,9 @@ fun HomeScreen( date = data.date, period = data.period, trackingLogs = data.trackingLogs, + groups = data.groups, onDismiss = { viewModel.clearSelectedDay() }, + onDeleteTrackingLogs = { viewModel.deleteTrackingLogs(it) }, onEditPeriod = { viewModel.clearSelectedDay() // The unified day screen edits this specific day's own flow diff --git a/app/src/main/java/com/mapgie/goflo/ui/screens/home/HomeViewModel.kt b/app/src/main/java/com/mapgie/goflo/ui/screens/home/HomeViewModel.kt index 78c8ccd..1beb629 100644 --- a/app/src/main/java/com/mapgie/goflo/ui/screens/home/HomeViewModel.kt +++ b/app/src/main/java/com/mapgie/goflo/ui/screens/home/HomeViewModel.kt @@ -3,8 +3,10 @@ package com.mapgie.goflo.ui.screens.home import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import com.mapgie.goflo.data.database.entities.Group import com.mapgie.goflo.data.database.entities.PeriodEntry import com.mapgie.goflo.data.database.entities.TrackingCategory +import com.mapgie.goflo.data.database.entities.TrackingLog import com.mapgie.goflo.data.preferences.AppPreferencesStore import com.mapgie.goflo.data.repository.PeriodRepository import com.mapgie.goflo.data.repository.TrackingLogWithValues @@ -70,6 +72,8 @@ data class DayLogData( val date: LocalDate, val period: PeriodEntry?, val trackingLogs: List, + /** Groups, so categories that inherit their colour render in it. */ + val groups: List = emptyList(), ) @OptIn(ExperimentalCoroutinesApi::class) @@ -171,8 +175,9 @@ class HomeViewModel( combine( repository.getAllPeriods(), - trackingRepository.getLogsForDate(date) - ) { periods, trackingLogs -> + trackingRepository.getLogsForDate(date), + trackingRepository.getAllGroups(), + ) { periods, trackingLogs, groups -> val period = periods.firstOrNull { p -> val start = LocalDate.parse(p.startDate) val end = p.endDate?.let { LocalDate.parse(it) } ?: LocalDate.now() @@ -182,6 +187,7 @@ class HomeViewModel( date = date, period = period, trackingLogs = trackingLogs, + groups = groups, ) } } @@ -190,6 +196,16 @@ class HomeViewModel( fun selectDay(date: LocalDate) { _selectedDay.value = date } fun clearSelectedDay() { _selectedDay.value = null } + /** + * Deletes stored tracking logs from the day sheet. The sheet's data is a + * live query, so it refreshes on its own. + */ + fun deleteTrackingLogs(logs: List) { + viewModelScope.launch { + logs.forEach { trackingRepository.deleteLog(it) } + } + } + // ── Quick increment (Plus One categories) ─────────────────────────────────── /** Transient confirmation message after an instant increment; null when none pending. */ diff --git a/changelog/unreleased/day-sheet-delete-and-colours.json b/changelog/unreleased/day-sheet-delete-and-colours.json new file mode 100644 index 0000000..c39b67a --- /dev/null +++ b/changelog/unreleased/day-sheet-delete-and-colours.json @@ -0,0 +1,6 @@ +{ + "bump": "patch", + "fixed": [ + "The calendar day sheet now lets you delete any entry (including Flow and Symptoms) with a confirmation, shows Flow and Symptoms alongside the period instead of under \"Tracked\", and renders categories that inherit their group's colour in that colour so their values are readable" + ] +}