179 lines
5.1 KiB
Dart
179 lines
5.1 KiB
Dart
// 日历 BLoC — 管理日历视图状态,通过 JournalRepository 加载数据
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
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<DateTime, List<JournalEntry>> journalsByDate;
|
|
final List<JournalEntry> 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<DateTime, List<JournalEntry>>? journalsByDate,
|
|
List<JournalEntry>? 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<CalendarEvent, CalendarState> {
|
|
final JournalRepository _journalRepo;
|
|
|
|
CalendarBloc({required JournalRepository journalRepository})
|
|
: _journalRepo = journalRepository,
|
|
super(const CalendarInitial()) {
|
|
on<CalendarMonthChanged>(_onMonthChanged);
|
|
on<CalendarDaySelected>(_onDaySelected);
|
|
on<CalendarViewModeChanged>(_onViewModeChanged);
|
|
}
|
|
|
|
Future<void> _onMonthChanged(
|
|
CalendarMonthChanged event,
|
|
Emitter<CalendarState> 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 = <DateTime, List<JournalEntry>>{};
|
|
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;
|
|
// 根据当前选中日期查找日记,避免进入页面时空白
|
|
final dayKey = DateTime(
|
|
current.selectedDay.year,
|
|
current.selectedDay.month,
|
|
current.selectedDay.day,
|
|
);
|
|
final selectedJournals = byDate[dayKey] ?? [];
|
|
emit(current.copyWith(
|
|
journalsByDate: byDate,
|
|
selectedDayJournals: selectedJournals,
|
|
isLoading: false,
|
|
));
|
|
}
|
|
} catch (e) {
|
|
debugPrint('CalendarBloc._onMonthChanged 失败: $e');
|
|
if (state is CalendarLoaded) {
|
|
emit((state as CalendarLoaded).copyWith(isLoading: false));
|
|
}
|
|
}
|
|
}
|
|
|
|
void _onDaySelected(
|
|
CalendarDaySelected event,
|
|
Emitter<CalendarState> 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<CalendarState> emit,
|
|
) {
|
|
if (state is! CalendarLoaded) return;
|
|
emit((state as CalendarLoaded).copyWith(viewMode: event.mode));
|
|
}
|
|
}
|