- Flutter/Dart (iOS + Android), Riverpod, go_router, i18n via ARB (deutsch) - Feature-Struktur: today, plants, locations, household (V2-Platzhalter), settings - Intervall-Modell: Aufgaben werden aus lastWatered/lastFertilized + Intervall berechnet - In-Memory-Demo-Daten, Firebase-Anbindung folgt im nächsten Block - Doku: Architektur, Firebase-Einrichtung, Forgejo-Git-Hosting Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
61 lines
2.1 KiB
Dart
61 lines
2.1 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
|
||
import '../../plants/data/plants_provider.dart';
|
||
import '../../plants/domain/plant.dart';
|
||
import '../domain/due_task.dart';
|
||
|
||
DateTime _dateOnly(DateTime dt) => DateTime(dt.year, dt.month, dt.day);
|
||
|
||
DateTime? _nextDue(DateTime? lastDone, int intervalDays, DateTime today) {
|
||
// Noch nie erledigt → sofort fällig.
|
||
if (lastDone == null) return today;
|
||
return _dateOnly(lastDone).add(Duration(days: intervalDays));
|
||
}
|
||
|
||
Iterable<DueTask> _tasksForPlant(Plant plant, DateTime today) sync* {
|
||
final watering = _nextDue(plant.lastWatered, plant.wateringIntervalDays, today);
|
||
if (watering != null && !watering.isAfter(today)) {
|
||
yield DueTask(plant: plant, type: CareTaskType.watering, dueDate: watering);
|
||
}
|
||
final fertilizing =
|
||
_nextDue(plant.lastFertilized, plant.fertilizingIntervalDays, today);
|
||
if (fertilizing != null && !fertilizing.isAfter(today)) {
|
||
yield DueTask(
|
||
plant: plant, type: CareTaskType.fertilizing, dueDate: fertilizing);
|
||
}
|
||
}
|
||
|
||
/// Alle heute fälligen und überfälligen Aufgaben, Überfälliges zuerst.
|
||
final dueTasksProvider = Provider<List<DueTask>>((ref) {
|
||
final plants = ref.watch(plantsProvider);
|
||
final today = _dateOnly(DateTime.now());
|
||
|
||
final tasks = [
|
||
for (final plant in plants) ..._tasksForPlant(plant, today),
|
||
];
|
||
tasks.sort((a, b) {
|
||
final byDate = a.dueDate.compareTo(b.dueDate);
|
||
if (byDate != 0) return byDate;
|
||
return a.plant.nickname.compareTo(b.plant.nickname);
|
||
});
|
||
return tasks;
|
||
});
|
||
|
||
/// Das nächste zukünftige Fälligkeitsdatum – für den "Alles versorgt"-Screen
|
||
/// ("Nächste Aufgabe: Freitag").
|
||
final nextDueDateProvider = Provider<DateTime?>((ref) {
|
||
final plants = ref.watch(plantsProvider);
|
||
final today = _dateOnly(DateTime.now());
|
||
|
||
DateTime? next;
|
||
for (final plant in plants) {
|
||
for (final candidate in [
|
||
_nextDue(plant.lastWatered, plant.wateringIntervalDays, today),
|
||
_nextDue(plant.lastFertilized, plant.fertilizingIntervalDays, today),
|
||
]) {
|
||
if (candidate == null || !candidate.isAfter(today)) continue;
|
||
if (next == null || candidate.isBefore(next)) next = candidate;
|
||
}
|
||
}
|
||
return next;
|
||
});
|