diff --git a/app/src/main/java/no/naiv/meddetsamme/alarm/SupplyCheckReceiver.kt b/app/src/main/java/no/naiv/meddetsamme/alarm/SupplyCheckReceiver.kt index cbdd916..5807211 100644 --- a/app/src/main/java/no/naiv/meddetsamme/alarm/SupplyCheckReceiver.kt +++ b/app/src/main/java/no/naiv/meddetsamme/alarm/SupplyCheckReceiver.kt @@ -12,6 +12,7 @@ import no.naiv.meddetsamme.data.MedDatabase import no.naiv.meddetsamme.domain.ScheduleEngine import no.naiv.meddetsamme.domain.ScheduleText import no.naiv.meddetsamme.notify.Notifications +import no.naiv.meddetsamme.settings.SettingsStore /** * Daily derived-state check: low stock (inventory ÷ scheduled rate) and @@ -96,7 +97,7 @@ class SupplyCheckReceiver : BroadcastReceiver() { /** * Per-med seasonal nudge, post-or-cancel like the digests above: * - resting and within [SEASON_START_LEAD_DAYS] of the next window → "Start nå"; - * - in season and within [SEASON_END_LEAD_DAYS] of its close → "Forleng". + * - in season and within [SEASON_END_LEAD_DAYS] of its close → "Forleng" (once per close). * Acting on either (or the season simply passing) clears it the next day. */ private fun checkSeason( @@ -130,14 +131,20 @@ class SupplyCheckReceiver : BroadcastReceiver() { val end = ScheduleEngine.currentSeasonEnd(doseTimes, today) val days = end?.let { ChronoUnit.DAYS.between(today, it) } if (end != null && days != null && days in 0..SEASON_END_LEAD_DAYS) { - Notifications.post( - context, notifId, - Notifications.buildSeasonNotification( - context, medId, name, - "Sesongen er snart over (${ScheduleText.dateLabel(end)}). Fortsatt plaget? Forleng to uker.", - starting = false, - ), - ) + // Once per season close, not daily: a dismissed card stays dismissed. + // Keyed on the end date, so "Forleng" (new end) earns one fresh nudge. + val settings = SettingsStore(context) + if (settings.seasonEndNudgedEpochDay(medId) != end.toEpochDay()) { + Notifications.post( + context, notifId, + Notifications.buildSeasonNotification( + context, medId, name, + "Sesongen er snart over (${ScheduleText.dateLabel(end)}). Fortsatt plaget? Forleng to uker.", + starting = false, + ), + ) + settings.setSeasonEndNudgedEpochDay(medId, end.toEpochDay()) + } } else { Notifications.cancel(context, notifId) } diff --git a/app/src/main/java/no/naiv/meddetsamme/settings/SettingsStore.kt b/app/src/main/java/no/naiv/meddetsamme/settings/SettingsStore.kt index ae1cb1e..a79ac10 100644 --- a/app/src/main/java/no/naiv/meddetsamme/settings/SettingsStore.kt +++ b/app/src/main/java/no/naiv/meddetsamme/settings/SettingsStore.kt @@ -66,6 +66,16 @@ class SettingsStore(context: Context) { get() = prefs.getBoolean("repeat_reminders", true) set(v) = prefs.edit { putBoolean("repeat_reminders", v) } + /** + * Epoch day of the season close a med was last nudged "Forleng" for. The + * end-of-season card posts once per close, not daily for the whole lead + * window: keyed on the end date so next year's (or an extended) season + * gets a fresh nudge. -1 = never. + */ + fun seasonEndNudgedEpochDay(medId: Long): Long = prefs.getLong("season_end_nudged_$medId", -1) + fun setSeasonEndNudgedEpochDay(medId: Long, endEpochDay: Long) = + prefs.edit { putLong("season_end_nudged_$medId", endEpochDay) } + /** S3/Garage credentials complete — gates the S3 upload path only. */ val isS3Configured: Boolean get() = !s3Endpoint.isNullOrBlank() && !s3Bucket.isNullOrBlank() &&