feat(11-02): add DAO queries, update CalendarDayState, implement pre-population provider
- Add watchAllActiveRecurringTasks() and watchAllActiveRecurringTasksInRoom() to CalendarDao - Add watchCompletionsInRange() for period-completion filtering - Extend CalendarDayState with prePopulatedTasks field (default empty, backward compat) - Update isEmpty getter to include prePopulatedTasks.isEmpty - Add _isInCurrentIntervalWindow() and _calculatePreviousDueDate() helpers - Rewrite calendarDayProvider and roomCalendarDayProvider with pre-population logic - Fix _subtractMonths() year-boundary bug using total-month arithmetic - Add 9 new DAO tests for watchAllActiveRecurringTasks, watchAllActiveRecurringTasksInRoom, watchCompletionsInRange
This commit is contained in:
@@ -165,4 +165,59 @@ class CalendarDao extends DatabaseAccessor<AppDatabase>
|
||||
final result = await query.getSingle();
|
||||
return result.read(countExp) ?? 0;
|
||||
}
|
||||
|
||||
/// Watch ALL active tasks with their rooms.
|
||||
///
|
||||
/// Used by the pre-population logic to determine which tasks should appear
|
||||
/// on days within their current interval window (before their next due date).
|
||||
Stream<List<TaskWithRoom>> watchAllActiveRecurringTasks() {
|
||||
final query = select(tasks).join([
|
||||
innerJoin(rooms, rooms.id.equalsExp(tasks.roomId)),
|
||||
]);
|
||||
query.where(tasks.isActive.equals(true));
|
||||
query.orderBy([OrderingTerm.asc(tasks.name)]);
|
||||
|
||||
return query.watch().map((rows) {
|
||||
return rows.map((row) {
|
||||
final task = row.readTable(tasks);
|
||||
final room = row.readTable(rooms);
|
||||
return TaskWithRoom(task: task, roomName: room.name, roomId: room.id);
|
||||
}).toList();
|
||||
});
|
||||
}
|
||||
|
||||
/// Watch ALL active tasks with their rooms, filtered to a specific [roomId].
|
||||
///
|
||||
/// Room-scoped variant of [watchAllActiveRecurringTasks], used by
|
||||
/// [roomCalendarDayProvider] to pre-populate tasks for a single room.
|
||||
Stream<List<TaskWithRoom>> watchAllActiveRecurringTasksInRoom(int roomId) {
|
||||
final query = select(tasks).join([
|
||||
innerJoin(rooms, rooms.id.equalsExp(tasks.roomId)),
|
||||
]);
|
||||
query.where(tasks.isActive.equals(true) & tasks.roomId.equals(roomId));
|
||||
query.orderBy([OrderingTerm.asc(tasks.name)]);
|
||||
|
||||
return query.watch().map((rows) {
|
||||
return rows.map((row) {
|
||||
final task = row.readTable(tasks);
|
||||
final room = row.readTable(rooms);
|
||||
return TaskWithRoom(task: task, roomName: room.name, roomId: room.id);
|
||||
}).toList();
|
||||
});
|
||||
}
|
||||
|
||||
/// Watch completions for a given [taskId] within a date range [start]..[end].
|
||||
///
|
||||
/// Used for period-completion filtering: if a task was completed in the
|
||||
/// current interval window, it should not appear as a pre-populated task.
|
||||
/// [start] is inclusive; [end] is exclusive.
|
||||
Stream<List<TaskCompletion>> watchCompletionsInRange(
|
||||
int taskId, DateTime start, DateTime end) {
|
||||
return (select(taskCompletions)
|
||||
..where((c) =>
|
||||
c.taskId.equals(taskId) &
|
||||
c.completedAt.isBiggerOrEqualValue(start) &
|
||||
c.completedAt.isSmallerThanValue(end)))
|
||||
.watch();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user