timetracker/.tasks/done/TT-005-riverpod-providers-notifiers.md
2026-08-03 21:51:48 +02:00

114 lines
3.3 KiB
Markdown

# TT-005 — Riverpod Providers & Notifiers
**Type:** Story
**Priority:** High
**Labels:** state-management, riverpod
**Depends on:** TT-003, TT-004
**Blocks:** TT-006, TT-007, TT-008, TT-009
---
## Summary
Implement all Riverpod providers and notifiers for each feature. This is the
domain/application layer that connects the data layer (repositories) to the
presentation layer (screens/widgets).
## Providers to implement
### Timer Feature
File: `lib/features/timer/domain/timer_notifier.dart`
```dart
@riverpod
class TimerNotifier extends _$TimerNotifier {
@override
TimerState build(); // reads DB for open entry on init
Future<void> start(Project project, {String? note});
Future<void> stop();
Future<void> discard();
void updateNote(String note);
}
```
- Uses a `dart:async Timer.periodic(1s)` while running
- On `build()`: calls `timeEntriesRepository.getActiveEntry()` to restore state
- See `.ai/features/timer.md` for full state-machine spec
### Projects Feature
File: `lib/features/projects/domain/projects_provider.dart`
```dart
@riverpod
Stream<List<Project>> activeProjects(ActiveProjectsRef ref);
@riverpod
Stream<List<Project>> allProjects(AllProjectsRef ref);
@riverpod
class ProjectsNotifier extends _$ProjectsNotifier {
Future<void> create({required String name, required int colorValue, String? description});
Future<void> update(Project project);
Future<void> archive(int id);
Future<void> delete(int id);
}
```
### Entries Feature
File: `lib/features/entries/domain/entries_provider.dart`
```dart
@riverpod
Stream<List<TimeEntry>> entriesByDateRange(
EntriesByDateRangeRef ref, {required DateTime from, required DateTime to});
@riverpod
class EntriesNotifier extends _$EntriesNotifier {
Future<void> create(TimeEntry entry);
Future<void> update(TimeEntry entry);
Future<void> delete(int id);
}
```
### Reports Feature
File: `lib/features/reports/domain/reports_provider.dart`
```dart
@riverpod
class ReportsNotifier extends _$ReportsNotifier {
@override
ReportData build();
void selectPeriod(ReportPeriod period); // day | week | month
void goToPrevious();
void goToNext();
void filterByProject(int? projectId);
}
@freezed
class ReportData with _$ReportData {
const factory ReportData({
required Duration totalDuration,
required Map<String, Duration> durationByProject,
required List<double> chartValues, // bars for fl_chart
required DateTime periodStart,
required DateTime periodEnd,
}) = _ReportData;
}
```
## Acceptance Criteria
- [ ] All providers use `@riverpod` annotation (code-gen)
- [ ] `TimerNotifier.build()` restores running timer from DB on app start
- [ ] `TimerNotifier` disposes its `dart:async Timer` via `ref.onDispose`
- [ ] All providers inject repositories via `ref.watch(xxxRepositoryProvider)`
- [ ] Unit tests for `TimerNotifier` (start/stop/discard/restore)
- [ ] Unit tests for `ProjectsNotifier` (CRUD operations)
## Files to create
- `lib/features/timer/domain/timer_notifier.dart`
- `lib/features/projects/domain/projects_provider.dart`
- `lib/features/entries/domain/entries_provider.dart`
- `lib/features/reports/domain/reports_provider.dart`
- `lib/features/reports/domain/report_data.dart`
- `test/features/timer/timer_notifier_test.dart`
- `test/features/projects/projects_notifier_test.dart`