Files
nj/app/lib/features/home/bloc/home_bloc.dart
iven 49d4aa36a7
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
fix(app): Phase 1.1 紧急修复 — SyncEngine 接入 + authorId + catch 异常处理
- feat(sync): SyncEngine 接入 EditorPage, 保存时 enqueue + 网络恢复自动 trySync
- fix(editor): authorId 从 AuthBloc 获取, 替代硬编码 'local'
- fix(bloc): class_bloc/calendar/profile/parent catch(_).全部改为 debugPrint
- feat(editor): 编辑器工具栏拆分 (brush_panel/tag_panel/text_format_bar/dot_grid_painter)
- feat(editor): EditorBloc 扩展 + EditorPage 增强
- feat(search): SearchBloc 扩展搜索功能
- feat(home): HomeBloc/HomePage 增强
- feat(auth): LoginPage 增强
- feat(templates): TemplateGalleryPage 重构
- fix(web): 管理端班级/日记页面修复
- fix(server): comment_service + theme_handler 修复
- docs: 添加全链路审计报告和验证截图
2026-06-02 21:21:43 +08:00

171 lines
4.4 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 首页 BLoC — 加载最近日记和心情概览
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 HomeEvent {
const HomeEvent();
}
/// 加载首页数据(最近日记 + 心情概览)
final class HomeLoadData extends HomeEvent {
const HomeLoadData();
}
/// 刷新首页
final class HomeRefresh extends HomeEvent {
const HomeRefresh();
}
// ===== State =====
/// 首页状态
sealed class HomeState {
const HomeState();
}
final class HomeInitial extends HomeState {
const HomeInitial();
}
final class HomeLoading extends HomeState {
const HomeLoading();
}
final class HomeLoaded extends HomeState {
/// 最近日记(取最新 10 条)
final List<JournalEntry> recentJournals;
/// 今日是否已写日记
final bool hasTodayEntry;
/// 最近常用心情
final Mood? topMood;
/// 连续写日记天数(从日记列表推算)
final int streakDays;
/// 本月日记数spec §3.4 quick-stats
final int monthCount;
/// 总日记数spec §3.4 quick-stats
final int totalCount;
/// 今日天气从今日日记中提取null 则默认晴)
final Weather? todayWeather;
const HomeLoaded({
this.recentJournals = const [],
this.hasTodayEntry = false,
this.topMood,
this.streakDays = 0,
this.monthCount = 0,
this.totalCount = 0,
this.todayWeather,
});
}
final class HomeError extends HomeState {
final String message;
const HomeError(this.message);
}
// ===== BLoC =====
class HomeBloc extends Bloc<HomeEvent, HomeState> {
final JournalRepository _journalRepo;
HomeBloc({required JournalRepository journalRepository})
: _journalRepo = journalRepository,
super(const HomeInitial()) {
on<HomeLoadData>(_onLoadData);
on<HomeRefresh>(_onRefresh);
}
Future<void> _onLoadData(
HomeLoadData event,
Emitter<HomeState> emit,
) async {
emit(const HomeLoading());
try {
final journals = await _journalRepo.getJournals(
page: 1,
pageSize: 10,
);
// 检查今日是否已写日记
final today = DateTime.now();
final hasTodayEntry = journals.any((j) =>
j.date.year == today.year &&
j.date.month == today.month &&
j.date.day == today.day);
// 推算最常用心情
final moodCounts = <Mood, int>{};
for (final j in journals) {
moodCounts[j.mood] = (moodCounts[j.mood] ?? 0) + 1;
}
final topMood = moodCounts.entries
.fold<MapEntry<Mood, int>?>(null, (a, b) => a == null || b.value > a.value ? b : a)
?.key;
// 推算连续天数
final streakDays = _calculateStreak(journals);
// 本月日记数spec §3.4 quick-stats
final monthCount = journals.where((j) =>
j.date.year == today.year && j.date.month == today.month).length;
// 总日记数 — 使用仓库计数方法(不受分页限制)
final totalCount = await _journalRepo.getJournalCount();
// 今日天气 — 从今日日记中提取
final todayJournal = journals.firstWhere(
(j) => j.date.year == today.year &&
j.date.month == today.month &&
j.date.day == today.day,
orElse: () => journals.first, // fallback for type safety
);
final todayWeather = hasTodayEntry ? todayJournal.weather : null;
emit(HomeLoaded(
recentJournals: journals,
hasTodayEntry: hasTodayEntry,
topMood: topMood,
streakDays: streakDays,
monthCount: monthCount,
totalCount: totalCount,
todayWeather: todayWeather,
));
} catch (e) {
emit(const HomeLoaded()); // 空状态而非错误,离线友好
}
}
Future<void> _onRefresh(
HomeRefresh event,
Emitter<HomeState> emit,
) async {
add(const HomeLoadData());
}
/// 从日记列表推算连续写日记天数
int _calculateStreak(List<JournalEntry> journals) {
if (journals.isEmpty) return 0;
final dates = journals.map((j) => j.date).toSet();
var streak = 0;
var checkDate = DateTime.now();
while (dates.contains(DateTime(checkDate.year, checkDate.month, checkDate.day))) {
streak++;
checkDate = checkDate.subtract(const Duration(days: 1));
}
return streak;
}
}