reminders: adopt floret-kit multi-value override model

Bumps the floret-kit submodule to the multi-value core-reminders API
(ReminderOverride.Minutes now a List<Int>) and adapts the per-list override
plumbing + pickers to it. Agendula stays single-reminder: overrides are
one-element (or empty) lists, reminderLeadFor takes firstOrNull, and the
single-select picker wraps/unwraps listOf. On-disk format is unchanged (single
values round-trip byte-identically). Verified: :app:compileDebugKotlin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-06 21:25:40 +02:00
parent b266653e4e
commit 976d496d21
4 changed files with 19 additions and 20 deletions

View File

@@ -12,7 +12,7 @@ import de.jeanlucmakiola.agendula.domain.TaskFormField
import de.jeanlucmakiola.floret.reminders.ReminderOverride import de.jeanlucmakiola.floret.reminders.ReminderOverride
import de.jeanlucmakiola.floret.reminders.ReminderOverrideCodec import de.jeanlucmakiola.floret.reminders.ReminderOverrideCodec
import de.jeanlucmakiola.floret.reminders.applyReminderOverride import de.jeanlucmakiola.floret.reminders.applyReminderOverride
import de.jeanlucmakiola.floret.reminders.reminderLeadFor import de.jeanlucmakiola.floret.reminders.reminderLeadsFor
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import javax.inject.Inject import javax.inject.Inject
@@ -39,15 +39,17 @@ data class Settings(
val bottomAddBar: Boolean = false, val bottomAddBar: Boolean = false,
/** /**
* Per-list overrides of [reminderLeadMinutes]: a list present in the map * Per-list overrides of [reminderLeadMinutes]: a list present in the map
* overrides the global default (a null value = no reminder); absent = inherit. * overrides the global default (an empty list = no reminder); absent =
* inherit. Agendula offers a single reminder, so each override is a
* one-element (or empty) list.
*/ */
val perListReminderOverride: Map<Long, Int?> = emptyMap(), val perListReminderOverride: Map<Long, List<Int>> = emptyMap(),
/** Optional edit-form fields shown by default; the rest sit behind "More fields". */ /** Optional edit-form fields shown by default; the rest sit behind "More fields". */
val defaultEditFields: Set<TaskFormField> = emptySet(), val defaultEditFields: Set<TaskFormField> = emptySet(),
) { ) {
/** The lead time for a task in [listId]: its override if set, else the global default. */ /** The lead time for a task in [listId]: its override if set, else the global default. */
fun reminderLeadFor(listId: Long): Int? = fun reminderLeadFor(listId: Long): Int? =
perListReminderOverride.reminderLeadFor(listId, reminderLeadMinutes) perListReminderOverride.reminderLeadsFor(listId, listOf(reminderLeadMinutes)).firstOrNull()
} }
/** App preferences, backed by DataStore. Mirrors Calendula's prefs shape. */ /** App preferences, backed by DataStore. Mirrors Calendula's prefs shape. */

View File

@@ -54,7 +54,9 @@ fun ReminderLeadPicker(
onDismiss: () -> Unit, onDismiss: () -> Unit,
presets: List<Int> = REMINDER_PRESETS, presets: List<Int> = REMINDER_PRESETS,
) { ) {
val selectedMinutes = (selected as? ReminderOverride.Minutes)?.minutes // Agendula is single-reminder: an override carries a one-element list, so
// take the single value for this single-select picker.
val selectedMinutes = (selected as? ReminderOverride.Minutes)?.minutes?.firstOrNull()
val customSelected = selectedMinutes != null && selectedMinutes !in presets val customSelected = selectedMinutes != null && selectedMinutes !in presets
val seed = decomposeReminderMinutes(selectedMinutes?.takeIf { customSelected }) val seed = decomposeReminderMinutes(selectedMinutes?.takeIf { customSelected })
@@ -65,7 +67,7 @@ fun ReminderLeadPicker(
val options = buildList { val options = buildList {
if (allowInherit) add(ReminderOverride.Inherit) if (allowInherit) add(ReminderOverride.Inherit)
if (allowNone) add(ReminderOverride.None) if (allowNone) add(ReminderOverride.None)
presets.forEach { add(ReminderOverride.Minutes(it)) } presets.forEach { add(ReminderOverride.Minutes(listOf(it))) }
} }
val rowCount = options.size + 1 // + the custom row val rowCount = options.size + 1 // + the custom row
@@ -105,7 +107,7 @@ fun ReminderLeadPicker(
unit = unit, unit = unit,
onUnitChange = { unit = it }, onUnitChange = { unit = it },
onConfirm = { minutes -> onConfirm = { minutes ->
onSelect(ReminderOverride.Minutes(minutes)) onSelect(ReminderOverride.Minutes(listOf(minutes)))
onDismiss() onDismiss()
}, },
) )
@@ -168,5 +170,5 @@ private fun CustomReminderEditor(
private fun reminderOverrideLabel(override: ReminderOverride): String = when (override) { private fun reminderOverrideLabel(override: ReminderOverride): String = when (override) {
ReminderOverride.Inherit -> stringResource(R.string.reminder_use_default) ReminderOverride.Inherit -> stringResource(R.string.reminder_use_default)
ReminderOverride.None -> stringResource(R.string.reminder_none) ReminderOverride.None -> stringResource(R.string.reminder_none)
is ReminderOverride.Minutes -> reminderLeadTimeLabel(override.minutes) is ReminderOverride.Minutes -> reminderLeadTimeLabel(override.minutes.first())
} }

View File

@@ -94,6 +94,7 @@ import de.jeanlucmakiola.floret.components.positionOf
import de.jeanlucmakiola.floret.identity.collapseExit import de.jeanlucmakiola.floret.identity.collapseExit
import de.jeanlucmakiola.floret.identity.expandEnter import de.jeanlucmakiola.floret.identity.expandEnter
import de.jeanlucmakiola.floret.reminders.ReminderOverride import de.jeanlucmakiola.floret.reminders.ReminderOverride
import de.jeanlucmakiola.floret.reminders.reminderOverrideFor
import de.jeanlucmakiola.agendula.ui.common.reminderLeadTimeLabel import de.jeanlucmakiola.agendula.ui.common.reminderLeadTimeLabel
/** The settings sub-screens reached from the hub's category rows. */ /** The settings sub-screens reached from the hub's category rows. */
@@ -483,10 +484,10 @@ private fun RemindersScreen(
if (showOffset) { if (showOffset) {
ReminderLeadPicker( ReminderLeadPicker(
title = stringResource(R.string.settings_default_reminder), title = stringResource(R.string.settings_default_reminder),
selected = ReminderOverride.Minutes(state.settings.reminderLeadMinutes), selected = ReminderOverride.Minutes(listOf(state.settings.reminderLeadMinutes)),
allowInherit = false, allowInherit = false,
allowNone = false, allowNone = false,
onSelect = { if (it is ReminderOverride.Minutes) viewModel.setReminderLeadMinutes(it.minutes) }, onSelect = { if (it is ReminderOverride.Minutes) viewModel.setReminderLeadMinutes(it.minutes.first()) },
onDismiss = { showOffset = false }, onDismiss = { showOffset = false },
) )
} }
@@ -504,14 +505,8 @@ private fun RemindersScreen(
} }
/** The stored override for [listId], as a picker choice (absent → inherit). */ /** The stored override for [listId], as a picker choice (absent → inherit). */
private fun listOverrideChoice(state: SettingsUiState, listId: Long): ReminderOverride { private fun listOverrideChoice(state: SettingsUiState, listId: Long): ReminderOverride =
val map = state.settings.perListReminderOverride state.settings.perListReminderOverride.reminderOverrideFor(listId)
return when {
!map.containsKey(listId) -> ReminderOverride.Inherit
map[listId] == null -> ReminderOverride.None
else -> ReminderOverride.Minutes(map.getValue(listId)!!)
}
}
/** Row summary for a list: its override, or the inherited global default. */ /** Row summary for a list: its override, or the inherited global default. */
@Composable @Composable
@@ -519,7 +514,7 @@ private fun listOverrideSummary(choice: ReminderOverride, globalDefault: Int): S
ReminderOverride.Inherit -> ReminderOverride.Inherit ->
stringResource(R.string.settings_list_reminder_inherits, reminderLeadTimeLabel(globalDefault)) stringResource(R.string.settings_list_reminder_inherits, reminderLeadTimeLabel(globalDefault))
ReminderOverride.None -> stringResource(R.string.reminder_none) ReminderOverride.None -> stringResource(R.string.reminder_none)
is ReminderOverride.Minutes -> reminderLeadTimeLabel(choice.minutes) is ReminderOverride.Minutes -> reminderLeadTimeLabel(choice.minutes.first())
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------