- ownerUid je Haushalt (Alt-Haushalte: erster memberUids-Eintrag = Ersteller); Besitzer-Anzeige in Mitglieder- und Haushaltsliste - Haushalt umbenennen (Dialog im Haushalts-Screen); Rules erlauben Clients am Haushalts-Dokument nur noch das Namensfeld - Cloud Functions leaveHousehold (selbst austreten, auch als Sitter) und removeMember (nur Besitzer); beide biegen den aktiven Zeiger des Betroffenen um und legen notfalls einen frischen eigenen Haushalt an - Haushalts-Wechsler im Drawer (PopupMenu, ab zwei Haushalten) - Tages-Checkliste und nächste Fälligkeit aggregieren über alle Haushalte, mit Zwischenüberschrift je Haushalt; Bestätigen schreibt in den Herkunfts-Haushalt der Aufgabe - Widget-Tests: Gruppierung, Besitzer-Rechte, Umbenennen Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
107 lines
3.9 KiB
Dart
107 lines
3.9 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
|
||
import '../../household/data/household_providers.dart';
|
||
import '../../household/domain/household.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, Household household, 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,
|
||
householdId: household.id,
|
||
householdName: household.name,
|
||
);
|
||
}
|
||
final fertilizing =
|
||
_nextDue(plant.lastFertilized, plant.fertilizingIntervalDays, today);
|
||
if (fertilizing != null && !fertilizing.isAfter(today)) {
|
||
yield DueTask(
|
||
plant: plant,
|
||
type: CareTaskType.fertilizing,
|
||
dueDate: fertilizing,
|
||
householdId: household.id,
|
||
householdName: household.name,
|
||
);
|
||
}
|
||
}
|
||
|
||
/// Alle heute fälligen und überfälligen Aufgaben aus **allen** Haushalten
|
||
/// des Nutzers. Sortiert nach Haushalt (aktiver zuerst), darin Überfälliges
|
||
/// zuerst – so gruppiert die Checkliste ohne weitere Logik.
|
||
final dueTasksProvider = Provider<List<DueTask>>((ref) {
|
||
final households = ref.watch(myHouseholdsProvider).value ?? const [];
|
||
final activeId = ref.watch(householdIdProvider).value;
|
||
final today = _dateOnly(DateTime.now());
|
||
|
||
final ordered = [...households]..sort((a, b) {
|
||
if (a.id == activeId) return -1;
|
||
if (b.id == activeId) return 1;
|
||
return 0; // myHouseholdsProvider liefert bereits nach Name sortiert.
|
||
});
|
||
|
||
final tasks = <DueTask>[];
|
||
for (final household in ordered) {
|
||
final plants =
|
||
ref.watch(householdPlantsProvider(household.id)).value ?? const [];
|
||
final householdTasks = [
|
||
for (final plant in plants) ..._tasksForPlant(plant, household, today),
|
||
]..sort((a, b) {
|
||
final byDate = a.dueDate.compareTo(b.dueDate);
|
||
if (byDate != 0) return byDate;
|
||
return a.plant.nickname.compareTo(b.plant.nickname);
|
||
});
|
||
tasks.addAll(householdTasks);
|
||
}
|
||
return tasks;
|
||
});
|
||
|
||
/// True, solange die Haushalte oder deren Pflanzen noch erstmalig laden –
|
||
/// damit die Checkliste nicht kurz „Alles versorgt“ zeigt, bevor Daten da sind.
|
||
final dueTasksLoadingProvider = Provider<bool>((ref) {
|
||
final householdsAsync = ref.watch(myHouseholdsProvider);
|
||
if (householdsAsync.isLoading && !householdsAsync.hasValue) return true;
|
||
final households = householdsAsync.value ?? const [];
|
||
for (final household in households) {
|
||
if (!ref.watch(householdPlantsProvider(household.id)).hasValue) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
});
|
||
|
||
/// Das nächste zukünftige Fälligkeitsdatum über alle Haushalte – für den
|
||
/// "Alles versorgt"-Screen ("Nächste Aufgabe: Freitag").
|
||
final nextDueDateProvider = Provider<DateTime?>((ref) {
|
||
final households = ref.watch(myHouseholdsProvider).value ?? const [];
|
||
final today = _dateOnly(DateTime.now());
|
||
|
||
DateTime? next;
|
||
for (final household in households) {
|
||
final plants =
|
||
ref.watch(householdPlantsProvider(household.id)).value ?? const [];
|
||
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;
|
||
});
|