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'; import 'package:household_keeper/features/tasks/domain/task_sort_option.dart'; import 'package:household_keeper/features/tasks/presentation/sort_preference_notifier.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, ); /// Sort a list of [TaskWithRoom] by the given [sortOption]. /// /// Returns a new sorted list; never mutates the original. /// Only [dayTasks] are sorted — the overdue section stays in its existing /// order per user decision. List _sortTasks( List tasks, TaskSortOption sortOption, ) { final sorted = List.from(tasks); switch (sortOption) { case TaskSortOption.alphabetical: sorted.sort((a, b) => a.task.name.toLowerCase().compareTo( b.task.name.toLowerCase(), )); case TaskSortOption.interval: sorted.sort((a, b) { final cmp = a.task.intervalType.index.compareTo( b.task.intervalType.index, ); if (cmp != 0) return cmp; return a.task.intervalDays.compareTo(b.task.intervalDays); }); case TaskSortOption.effort: sorted.sort((a, b) => a.task.effortLevel.index.compareTo( b.task.effortLevel.index, )); } return sorted; } /// 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. /// /// dayTasks are sorted in-memory according to the active [sortPreferenceProvider]. /// overdueTasks retain their existing order (pinned at top, unsorted per design). /// /// 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 sortOption = ref.watch(sortPreferenceProvider); 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 []; } final totalTaskCount = await db.calendarDao.getTaskCount(); return CalendarDayState( selectedDate: selectedDate, dayTasks: _sortTasks(dayTasks, sortOption), overdueTasks: overdueTasks, totalTaskCount: totalTaskCount, ); }); }); /// Room-scoped calendar day state: tasks for the selected date within a room. /// /// Mirrors [calendarDayProvider] but filters by [roomId]. /// Uses the shared [selectedDateProvider] so date selection is consistent /// across HomeScreen and room views. final roomCalendarDayProvider = StreamProvider.autoDispose.family((ref, roomId) { final db = ref.watch(appDatabaseProvider); final selectedDate = ref.watch(selectedDateProvider); final sortOption = ref.watch(sortPreferenceProvider); final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); final isToday = selectedDate == today; final dayTasksStream = db.calendarDao.watchTasksForDateInRoom(selectedDate, roomId); return dayTasksStream.asyncMap((dayTasks) async { final List overdueTasks; if (isToday) { overdueTasks = await db.calendarDao .watchOverdueTasksInRoom(selectedDate, roomId) .first; } else { overdueTasks = const []; } final totalTaskCount = await db.calendarDao.getTaskCountInRoom(roomId); return CalendarDayState( selectedDate: selectedDate, dayTasks: _sortTasks(dayTasks, sortOption), overdueTasks: overdueTasks, totalTaskCount: totalTaskCount, ); }); });