first commit - migrated from codeberg
This commit is contained in:
commit
5ead03e1f7
567 changed files with 102721 additions and 0 deletions
244
lib/src/features/mood_tracker/providers/mood_state.dart
Normal file
244
lib/src/features/mood_tracker/providers/mood_state.dart
Normal file
|
@ -0,0 +1,244 @@
|
|||
// SPDX-FileCopyrightText: © 2025 Nøkken.io <nokken.io@proton.me>
|
||||
// SPDX-License-Identifier: AGPL-3.0
|
||||
//
|
||||
// mood_state.dart
|
||||
// State management for mood entries using Riverpod
|
||||
//
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:nokken/src/features/mood_tracker/models/mood_entry.dart';
|
||||
import 'package:nokken/src/core/services/database/database_service.dart';
|
||||
import 'package:nokken/src/core/services/database/database_service_mood.dart';
|
||||
import 'package:nokken/src/features/medication_tracker/providers/medication_state.dart';
|
||||
|
||||
/// State class to handle loading and error states for mood entry data
|
||||
class MoodState {
|
||||
final List<MoodEntry> entries;
|
||||
final bool isLoading;
|
||||
final String? error;
|
||||
|
||||
const MoodState({
|
||||
this.entries = const [],
|
||||
this.isLoading = false,
|
||||
this.error,
|
||||
});
|
||||
|
||||
/// Create a new state object with updated fields
|
||||
MoodState copyWith({
|
||||
List<MoodEntry>? entries,
|
||||
bool? isLoading,
|
||||
String? error,
|
||||
}) {
|
||||
return MoodState(
|
||||
entries: entries ?? this.entries,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
error: error, // Pass null to clear error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Notifier class to handle mood state changes
|
||||
class MoodNotifier extends StateNotifier<MoodState> {
|
||||
final DatabaseService _databaseService;
|
||||
|
||||
MoodNotifier({required DatabaseService databaseService})
|
||||
: _databaseService = databaseService,
|
||||
super(const MoodState()) {
|
||||
// Load mood entries when initialized
|
||||
loadMoodEntries();
|
||||
}
|
||||
|
||||
/// Load mood entries from the database
|
||||
Future<void> loadMoodEntries() async {
|
||||
try {
|
||||
state = state.copyWith(isLoading: true, error: null);
|
||||
final entries = await _databaseService.getAllMoodEntries();
|
||||
state = state.copyWith(
|
||||
entries: entries,
|
||||
isLoading: false,
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
error: 'Failed to load mood entries: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a new mood entry to the database
|
||||
Future<void> addMoodEntry(MoodEntry entry) async {
|
||||
try {
|
||||
state = state.copyWith(isLoading: true, error: null);
|
||||
|
||||
// Save to database
|
||||
await _databaseService.insertMoodEntry(entry);
|
||||
|
||||
// Update state immediately with new entry
|
||||
state = state.copyWith(
|
||||
entries: [...state.entries, entry],
|
||||
isLoading: false,
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
error: 'Failed to add mood entry: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update an existing mood entry in the database
|
||||
Future<void> updateMoodEntry(MoodEntry entry) async {
|
||||
try {
|
||||
state = state.copyWith(isLoading: true, error: null);
|
||||
|
||||
// Update in database
|
||||
await _databaseService.updateMoodEntry(entry);
|
||||
|
||||
// Update state immediately
|
||||
state = state.copyWith(
|
||||
entries:
|
||||
state.entries.map((e) => e.id == entry.id ? entry : e).toList(),
|
||||
isLoading: false,
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
error: 'Failed to update mood entry: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a mood entry
|
||||
Future<void> deleteMoodEntry(String id) async {
|
||||
try {
|
||||
state = state.copyWith(isLoading: true, error: null);
|
||||
|
||||
// Delete from database
|
||||
await _databaseService.deleteMoodEntry(id);
|
||||
|
||||
// Update state immediately by filtering out the deleted entry
|
||||
state = state.copyWith(
|
||||
entries: state.entries.where((e) => e.id != id).toList(),
|
||||
isLoading: false,
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
error: 'Failed to delete mood entry: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a mood entry for a specific date
|
||||
Future<MoodEntry?> getMoodEntryForDate(DateTime date) async {
|
||||
try {
|
||||
// Check if we already have it in state
|
||||
final normalizedDate = DateTime(date.year, date.month, date.day);
|
||||
final existingEntry = state.entries.firstWhere(
|
||||
(entry) => DateTime(entry.date.year, entry.date.month, entry.date.day)
|
||||
.isAtSameMomentAs(normalizedDate),
|
||||
orElse: () => throw Exception('Not found in state'),
|
||||
);
|
||||
return existingEntry;
|
||||
} catch (_) {
|
||||
try {
|
||||
// Try to fetch from database
|
||||
return await _databaseService.getMoodEntryForDate(date);
|
||||
} catch (e) {
|
||||
// Handle error but don't update state
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// PROVIDER DEFINITIONS
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/// Main state notifier provider for mood entries
|
||||
final moodStateProvider = StateNotifierProvider<MoodNotifier, MoodState>((ref) {
|
||||
final databaseService = ref.watch(databaseServiceProvider);
|
||||
return MoodNotifier(databaseService: databaseService);
|
||||
});
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// CONVENIENCE PROVIDERS
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/// Provider for accessing the list of mood entries
|
||||
final moodEntriesProvider = Provider<List<MoodEntry>>((ref) {
|
||||
return ref.watch(moodStateProvider).entries;
|
||||
});
|
||||
|
||||
/// Provider for checking if mood entries are loading
|
||||
final moodEntriesLoadingProvider = Provider<bool>((ref) {
|
||||
return ref.watch(moodStateProvider).isLoading;
|
||||
});
|
||||
|
||||
/// Provider for accessing mood entry loading errors
|
||||
final moodEntriesErrorProvider = Provider<String?>((ref) {
|
||||
return ref.watch(moodStateProvider).error;
|
||||
});
|
||||
|
||||
/// Provider for getting a mood entry for a specific date
|
||||
final moodEntryForDateProvider =
|
||||
FutureProvider.family<MoodEntry?, DateTime>((ref, date) async {
|
||||
final moodNotifier = ref.watch(moodStateProvider.notifier);
|
||||
return await moodNotifier.getMoodEntryForDate(date);
|
||||
});
|
||||
|
||||
/// Provider for getting mood entry dates
|
||||
final moodEntryDatesProvider = Provider<Set<DateTime>>((ref) {
|
||||
final entries = ref.watch(moodStateProvider).entries;
|
||||
return entries.map((entry) {
|
||||
final date = entry.date;
|
||||
return DateTime(date.year, date.month, date.day);
|
||||
}).toSet();
|
||||
});
|
||||
|
||||
final filteredMoodEntriesProvider =
|
||||
Provider.family<List<MoodEntry>, String>((ref, timeframe) {
|
||||
final entries = ref.watch(moodEntriesProvider);
|
||||
|
||||
if (timeframe == 'All Time') {
|
||||
return entries;
|
||||
}
|
||||
|
||||
// Extract start date based on timeframe
|
||||
final now = DateTime.now();
|
||||
final DateTime startDate;
|
||||
|
||||
switch (timeframe) {
|
||||
case 'Last 7 Days':
|
||||
startDate = now.subtract(const Duration(days: 7));
|
||||
break;
|
||||
case 'Last 30 Days':
|
||||
startDate = now.subtract(const Duration(days: 30));
|
||||
break;
|
||||
case 'Last 90 Days':
|
||||
startDate = now.subtract(const Duration(days: 90));
|
||||
break;
|
||||
case 'Last 6 Months':
|
||||
startDate = DateTime(now.year, now.month - 6, now.day);
|
||||
break;
|
||||
case 'Last Year':
|
||||
startDate = DateTime(now.year - 1, now.month, now.day);
|
||||
break;
|
||||
default:
|
||||
return entries;
|
||||
}
|
||||
|
||||
return entries.where((entry) => entry.date.isAfter(startDate)).toList();
|
||||
});
|
||||
|
||||
/// Provider for mood icon colors
|
||||
final moodColorsProvider = Provider<Map<MoodRating, Color>>((ref) {
|
||||
return {
|
||||
MoodRating.great: Colors.green.shade400,
|
||||
MoodRating.good: Colors.lightGreen.shade400,
|
||||
MoodRating.okay: Colors.amber.shade400,
|
||||
MoodRating.meh: Colors.orange.shade400,
|
||||
MoodRating.bad: Colors.red.shade400,
|
||||
};
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue