// 日历 BLoC — 管理日历视图状态,通过 JournalRepository 加载数据 import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:nuanji_app/data/models/journal_entry.dart'; import 'package:nuanji_app/data/repositories/journal_repository.dart'; // ===== Events ===== sealed class CalendarEvent { const CalendarEvent(); } /// 切换到指定月份 final class CalendarMonthChanged extends CalendarEvent { final DateTime month; const CalendarMonthChanged(this.month); } /// 选择某一天 final class CalendarDaySelected extends CalendarEvent { final DateTime day; const CalendarDaySelected(this.day); } /// 切换视图模式 final class CalendarViewModeChanged extends CalendarEvent { final CalendarViewMode mode; const CalendarViewModeChanged(this.mode); } // ===== State ===== /// 日历视图模式 enum CalendarViewMode { month, week, timeline } /// 日历状态 sealed class CalendarState { const CalendarState(); } final class CalendarInitial extends CalendarState { const CalendarInitial(); } /// 日历已加载 final class CalendarLoaded extends CalendarState { final DateTime focusedMonth; final DateTime selectedDay; final Map> journalsByDate; final List selectedDayJournals; final CalendarViewMode viewMode; final bool isLoading; const CalendarLoaded({ required this.focusedMonth, required this.selectedDay, required this.journalsByDate, required this.selectedDayJournals, this.viewMode = CalendarViewMode.month, this.isLoading = false, }); CalendarLoaded copyWith({ DateTime? focusedMonth, DateTime? selectedDay, Map>? journalsByDate, List? selectedDayJournals, CalendarViewMode? viewMode, bool? isLoading, }) => CalendarLoaded( focusedMonth: focusedMonth ?? this.focusedMonth, selectedDay: selectedDay ?? this.selectedDay, journalsByDate: journalsByDate ?? this.journalsByDate, selectedDayJournals: selectedDayJournals ?? this.selectedDayJournals, viewMode: viewMode ?? this.viewMode, isLoading: isLoading ?? this.isLoading, ); } final class CalendarError extends CalendarState { final String message; const CalendarError(this.message); } // ===== BLoC ===== class CalendarBloc extends Bloc { final JournalRepository _journalRepo; CalendarBloc({required JournalRepository journalRepository}) : _journalRepo = journalRepository, super(const CalendarInitial()) { on(_onMonthChanged); on(_onDaySelected); on(_onViewModeChanged); } Future _onMonthChanged( CalendarMonthChanged event, Emitter emit, ) async { final currentState = state is CalendarLoaded ? state as CalendarLoaded : null; emit(CalendarLoaded( focusedMonth: event.month, selectedDay: event.month, journalsByDate: currentState?.journalsByDate ?? {}, selectedDayJournals: [], viewMode: currentState?.viewMode ?? CalendarViewMode.month, isLoading: true, )); try { // 加载当月日记 final startOfMonth = DateTime(event.month.year, event.month.month, 1); final endOfMonth = DateTime(event.month.year, event.month.month + 1, 0); final journals = await _journalRepo.getJournals( dateFrom: startOfMonth, dateTo: endOfMonth, ); // 按日期索引 final byDate = >{}; for (final journal in journals) { final key = DateTime(journal.date.year, journal.date.month, journal.date.day); byDate.putIfAbsent(key, () => []).add(journal); } if (state is CalendarLoaded) { final current = state as CalendarLoaded; emit(current.copyWith( journalsByDate: byDate, isLoading: false, )); } } catch (e) { if (state is CalendarLoaded) { emit((state as CalendarLoaded).copyWith(isLoading: false)); } } } void _onDaySelected( CalendarDaySelected event, Emitter emit, ) { if (state is! CalendarLoaded) return; final current = state as CalendarLoaded; final dayKey = DateTime(event.day.year, event.day.month, event.day.day); final dayJournals = current.journalsByDate[dayKey] ?? []; emit(current.copyWith( selectedDay: event.day, selectedDayJournals: dayJournals, )); } void _onViewModeChanged( CalendarViewModeChanged event, Emitter emit, ) { if (state is! CalendarLoaded) return; emit((state as CalendarLoaded).copyWith(viewMode: event.mode)); } }