From 68ba7c65cef8acb8f2dd7094e14e50e14d32007d Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Mon, 16 Mar 2026 21:24:07 +0100 Subject: [PATCH] feat(05-01): add CalendarDayState model, Riverpod providers, and l10n strings - CalendarDayState: selectedDate, dayTasks, overdueTasks fields with isEmpty helper - selectedDateProvider: NotifierProvider with SelectedDateNotifier, defaults to today - calendarDayProvider: StreamProvider.autoDispose, overdue only when viewing today - Add calendarTodayButton l10n string ("Heute") to ARB and generated dart files --- lib/features/home/domain/calendar_models.dart | 19 ++++++ .../home/presentation/calendar_providers.dart | 66 +++++++++++++++++++ lib/l10n/app_de.arb | 3 +- lib/l10n/app_localizations.dart | 6 ++ lib/l10n/app_localizations_de.dart | 3 + 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 lib/features/home/domain/calendar_models.dart create mode 100644 lib/features/home/presentation/calendar_providers.dart diff --git a/lib/features/home/domain/calendar_models.dart b/lib/features/home/domain/calendar_models.dart new file mode 100644 index 0000000..c9c7d1d --- /dev/null +++ b/lib/features/home/domain/calendar_models.dart @@ -0,0 +1,19 @@ +import 'package:household_keeper/features/home/domain/daily_plan_models.dart'; + +/// State for the calendar day view: tasks for the selected date + overdue tasks. +class CalendarDayState { + final DateTime selectedDate; + final List dayTasks; + final List overdueTasks; + + const CalendarDayState({ + required this.selectedDate, + required this.dayTasks, + required this.overdueTasks, + }); + + /// True when both day tasks and overdue tasks are empty. + /// Determined by the UI layer (completion state vs. no tasks at all + /// is handled in the widget based on this flag and history context). + bool get isEmpty => dayTasks.isEmpty && overdueTasks.isEmpty; +} diff --git a/lib/features/home/presentation/calendar_providers.dart b/lib/features/home/presentation/calendar_providers.dart new file mode 100644 index 0000000..c980dce --- /dev/null +++ b/lib/features/home/presentation/calendar_providers.dart @@ -0,0 +1,66 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:household_keeper/core/providers/database_provider.dart'; +import 'package:household_keeper/features/home/domain/calendar_models.dart'; +import 'package:household_keeper/features/home/domain/daily_plan_models.dart'; + +/// Notifier that manages the currently selected date in the calendar strip. +/// +/// Defaults to today (start of day, time zeroed out). +/// NOT autoDispose — the selected date persists while the app is alive. +class SelectedDateNotifier extends Notifier { + @override + DateTime build() { + final now = DateTime.now(); + return DateTime(now.year, now.month, now.day); + } + + /// Update the selected date (always normalized to start of day). + void selectDate(DateTime date) { + state = DateTime(date.year, date.month, date.day); + } +} + +/// Provider for the currently selected date in the calendar strip. +final selectedDateProvider = + NotifierProvider( + SelectedDateNotifier.new, +); + +/// Reactive calendar day state: tasks for the selected date + overdue tasks. +/// +/// Overdue tasks are only included when the selected date is today. +/// Past and future dates show only tasks originally due on that day. +/// +/// Defined manually (not @riverpod) because riverpod_generator has trouble +/// with drift's generated [Task] type. Same pattern as [dailyPlanProvider]. +final calendarDayProvider = + StreamProvider.autoDispose((ref) { + final db = ref.watch(appDatabaseProvider); + final selectedDate = ref.watch(selectedDateProvider); + + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final isToday = selectedDate == today; + + final dayTasksStream = db.calendarDao.watchTasksForDate(selectedDate); + + return dayTasksStream.asyncMap((dayTasks) async { + final List overdueTasks; + + if (isToday) { + // When viewing today, include overdue tasks (due before today) + overdueTasks = + await db.calendarDao.watchOverdueTasks(selectedDate).first; + } else { + // Past or future dates: no overdue carry-over + overdueTasks = const []; + } + + return CalendarDayState( + selectedDate: selectedDate, + dayTasks: dayTasks, + overdueTasks: overdueTasks, + ); + }); +}); diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 778491e..31a6690 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -106,5 +106,6 @@ "count": { "type": "int" }, "overdue": { "type": "int" } } - } + }, + "calendarTodayButton": "Heute" } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 8094718..7f218ff 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -513,6 +513,12 @@ abstract class AppLocalizations { /// In de, this message translates to: /// **'{count} Aufgaben fällig ({overdue} überfällig)'** String notificationBodyWithOverdue(int count, int overdue); + + /// No description provided for @calendarTodayButton. + /// + /// In de, this message translates to: + /// **'Heute'** + String get calendarTodayButton; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index e063302..3fdfbae 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -236,4 +236,7 @@ class AppLocalizationsDe extends AppLocalizations { String notificationBodyWithOverdue(int count, int overdue) { return '$count Aufgaben fällig ($overdue überfällig)'; } + + @override + String get calendarTodayButton => 'Heute'; }