Files
dailyou/lib/core/providers/mood_provider.dart
T
stefwill 6f3fb74e7e feat: implement multi-step mood logging wizard
Adds sleep/energy/positivity/self-worth tracking columns to the database
(schema v2 migration), a new 5-option StepScale widget, per-answer chips
at the top of the screen, a 2×2 activity grid chip, and a full step-by-step
wizard flow: mood → sleep (first entry today only) → energy → positivity →
self-worth → activities → review+save.
2026-04-26 12:14:45 +10:00

124 lines
4.1 KiB
Dart

import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../database/app_database.dart';
import 'database_provider.dart';
// ── All entries stream ────────────────────────────────────────────────────────
final moodEntriesProvider = StreamProvider.autoDispose<List<MoodEntry>>((ref) {
return ref.watch(moodDaoProvider).watchAllEntries();
});
// ── Entries for a specific day ────────────────────────────────────────────────
final moodEntriesForDayProvider =
StreamProvider.autoDispose.family<List<MoodEntry>, DateTime>((ref, day) {
return ref.watch(moodDaoProvider).watchEntriesForDay(day);
});
// ── Daily averages for calendar heatmap ──────────────────────────────────────
final dailyAveragesProvider = FutureProvider.autoDispose
.family<Map<DateTime, double>, ({DateTime start, DateTime end})>(
(ref, range) =>
ref.watch(moodDaoProvider).getDailyAverages(range.start, range.end),
);
// ── Is this the first entry today? ───────────────────────────────────────────
final isFirstEntryTodayProvider = FutureProvider.autoDispose<bool>((ref) async {
return !(await ref.read(moodDaoProvider).hasEntryToday());
});
// ── Selected day (drives calendar + day detail) ───────────────────────────────
class SelectedDay extends Notifier<DateTime> {
@override
DateTime build() => DateTime.now();
void select(DateTime day) {
state = DateTime(day.year, day.month, day.day);
}
void selectToday() {
final now = DateTime.now();
state = DateTime(now.year, now.month, now.day);
}
}
final selectedDayProvider =
NotifierProvider.autoDispose<SelectedDay, DateTime>(SelectedDay.new);
// ── Mood entry notifier (create / update / delete) ────────────────────────────
class MoodEntryNotifier extends Notifier<AsyncValue<void>> {
@override
AsyncValue<void> build() => const AsyncData(null);
Future<void> addEntry({
required int moodLevel,
String? note,
List<int> activityIds = const [],
List<int> tagIds = const [],
DateTime? timestamp,
int? sleepMinutes,
int? energyLevel,
int? positivityLevel,
int? selfWorthLevel,
}) async {
state = const AsyncLoading();
final next = await AsyncValue.guard(() async {
final dao = ref.read(moodDaoProvider);
await dao.insertEntryWithActivities(
entry: MoodEntriesCompanion.insert(
moodLevel: moodLevel,
timestamp: timestamp ?? DateTime.now(),
note: Value(note),
sleepMinutes: Value(sleepMinutes),
energyLevel: Value(energyLevel),
positivityLevel: Value(positivityLevel),
selfWorthLevel: Value(selfWorthLevel),
),
activityIds: activityIds,
tagIds: tagIds,
);
});
if (!ref.mounted) return;
state = next;
}
Future<void> deleteEntry(int id) async {
state = const AsyncLoading();
final next = await AsyncValue.guard(
() => ref.read(moodDaoProvider).deleteEntry(id),
);
if (!ref.mounted) return;
state = next;
}
Future<void> updateEntry({
required int id,
required int moodLevel,
String? note,
DateTime? timestamp,
}) async {
state = const AsyncLoading();
final next = await AsyncValue.guard(
() => ref.read(moodDaoProvider).updateEntry(
MoodEntriesCompanion(
id: Value(id),
moodLevel: Value(moodLevel),
note: Value(note),
timestamp: Value(timestamp ?? DateTime.now()),
),
),
);
if (!ref.mounted) return;
state = next;
}
}
final moodEntryNotifierProvider =
NotifierProvider.autoDispose<MoodEntryNotifier, AsyncValue<void>>(
MoodEntryNotifier.new);