diff --git a/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListScreen.kt b/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListScreen.kt index bbca99e..d4abd4f 100644 --- a/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListScreen.kt +++ b/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListScreen.kt @@ -234,7 +234,7 @@ fun ChoreListScreen( else -> { val displayed = uiState.displayed - val distantChores = uiState.distantChores + val hiddenChores = uiState.hiddenChores LazyColumn( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(bottom = 88.dp) @@ -281,24 +281,28 @@ fun ChoreListScreen( } } - if (distantChores.isNotEmpty()) { + if (hiddenChores.isNotEmpty()) { item { TextButton( - onClick = { viewModel.toggleShowDistant() }, + onClick = { viewModel.toggleShowHidden() }, modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp) ) { Text( - if (uiState.showDistant) - "Hide distant (${distantChores.size})" - else - "${distantChores.size} not due for 60+ days", + when { + uiState.showHidden -> + "Collapse hidden chores (${hiddenChores.size})" + uiState.smartVisibility -> + "${hiddenChores.size} hidden until closer to due" + else -> + "${hiddenChores.size} not due for 60+ days" + }, style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } - if (uiState.showDistant) { - items(distantChores, key = { "distant_${it.id}" }) { chore -> + if (uiState.showHidden) { + items(hiddenChores, key = { "hidden_${it.id}" }) { chore -> SwipeToLogCard( chore = chore, showOwner = uiState.ownerFilter == OwnerFilter.ALL, diff --git a/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListViewModel.kt b/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListViewModel.kt index b40e895..b54aa46 100644 --- a/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListViewModel.kt +++ b/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListViewModel.kt @@ -51,7 +51,7 @@ data class ChoreUiState( val zenMode: Boolean = false, val zenSortAscending: Boolean = true, val showDueCountdown: Boolean = false, - val showDistant: Boolean = false, + val showHidden: Boolean = false, // Off until settings load so chores aren't hidden with unconfigured lead times val smartVisibility: Boolean = false, val choreLeadDays: Map = emptyMap(), @@ -60,31 +60,43 @@ data class ChoreUiState( val pinnedChoreId: String? = null, val scanHistory: List = emptyList() ) { - val distantChores: List + private val ownerFiltered: List get() { var result = active if (ownerFilter == OwnerFilter.ME && ownerHandle.isNotBlank()) { result = result.filter { it.owner == null || it.owner == ownerHandle } } - return result.filter { it.isDistant() } + return result + } + + /** True if this chore belongs in the main list under its cadence bucket's lead time. */ + private fun withinLeadTime(chore: Chore): Boolean { + val last = chore.lastScanned ?: return true + val intervalDays = chore.intervalDays ?: return true + val bucket = CadenceBucket.forInterval(intervalDays) + val leadDays = choreLeadDays[bucket] ?: bucket.defaultLeadDays + val dueInstant = last.plus((intervalDays * 24).toLong(), ChronoUnit.HOURS) + return Duration.between(Instant.now(), dueInstant).toDays() <= leadDays + } + + /** + * Chores kept out of the main list but revealable via the collapsed section: + * everything beyond its lead time when smart visibility is on, otherwise only + * the legacy distant (due 60+ days out) chores. + */ + val hiddenChores: List + get() = if (smartVisibility) { + ownerFiltered.filterNot(::withinLeadTime) + } else { + ownerFiltered.filter { it.isDistant() } } val displayed: List get() { - var result = active - if (ownerFilter == OwnerFilter.ME && ownerHandle.isNotBlank()) { - result = result.filter { it.owner == null || it.owner == ownerHandle } - } - result = result.filter { !it.isDistant() } - if (smartVisibility) { - result = result.filter { chore -> - val last = chore.lastScanned ?: return@filter true - val intervalDays = chore.intervalDays ?: return@filter true - val bucket = CadenceBucket.forInterval(intervalDays) - val leadDays = choreLeadDays[bucket] ?: bucket.defaultLeadDays - val dueInstant = last.plus((intervalDays * 24).toLong(), ChronoUnit.HOURS) - Duration.between(Instant.now(), dueInstant).toDays() <= leadDays - } + var result = if (smartVisibility) { + ownerFiltered.filter(::withinLeadTime) + } else { + ownerFiltered.filter { !it.isDistant() } } result = when (filter) { ChoreFilter.ALL -> result @@ -320,7 +332,7 @@ class ChoreListViewModel @Inject constructor( viewModelScope.launch { settingsRepository.setShowDueCountdown(enabled) } } - fun toggleShowDistant() { - _uiState.update { it.copy(showDistant = !it.showDistant) } + fun toggleShowHidden() { + _uiState.update { it.copy(showHidden = !it.showHidden) } } } \ No newline at end of file diff --git a/changelog/unreleased/hidden-chores-section.json b/changelog/unreleased/hidden-chores-section.json new file mode 100644 index 0000000..cd2846b --- /dev/null +++ b/changelog/unreleased/hidden-chores-section.json @@ -0,0 +1,9 @@ +{ + "bump": "patch", + "changed": [ + "The collapsed section at the bottom of the chores list now reveals everything hidden by smart visibility, not just chores due 60+ days out, and its label reflects why chores are hidden" + ], + "fixed": [ + "Chores hidden by smart visibility could not be viewed or logged from the chores screen" + ] +}