Files
nj/app/lib/app.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

119 lines
4.2 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.
// 暖记 App 根组件 — MaterialApp + BLoC Provider 注入
//
// 依赖注入结构:
// MultiRepositoryProvider
// ├─ ApiClient
// ├─ AuthRepository
// ├─ JournalRepository (IsarJournalRepository — 离线优先)
// ├─ RemoteJournalRepository (供 SyncEngine 使用)
// └─ ClassRepository
// └─ BlocProvider<AuthBloc>
// └─ MaterialApp.router
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart' show ListenableProvider;
import 'config/app_config.dart';
import 'core/theme/app_theme.dart';
import 'core/routing/app_router.dart';
import 'data/remote/api_client.dart';
import 'data/repositories/auth_repository.dart';
import 'data/repositories/journal_repository.dart';
import 'data/repositories/isar_journal_repository.dart';
import 'data/repositories/remote_journal_repository.dart';
import 'data/repositories/class_repository.dart';
import 'data/services/sync_engine.dart';
import 'features/auth/bloc/auth_bloc.dart';
import 'features/profile/bloc/settings_bloc.dart';
/// 暖记 App — 根组件
class NuanjiApp extends StatelessWidget {
const NuanjiApp({super.key});
@override
Widget build(BuildContext context) {
// 创建全局依赖App 生命周期内单例)
final config = AppConfig.fromEnvironment();
final apiClient = ApiClient(baseUrl: config.apiBaseUrl);
final authRepository = AuthRepository(apiClient: apiClient);
// 离线优先Isar 为主要本地仓库Remote 供 SyncEngine 推送
// Web 平台Isar 3.x 不支持 Web直接使用远程仓库
final journalRepository = kIsWeb
? RemoteJournalRepository(api: apiClient)
: IsarJournalRepository();
final remoteJournalRepository = RemoteJournalRepository(api: apiClient);
final syncEngine = SyncEngine(apiClient: apiClient);
final classRepository = ClassRepository(api: apiClient);
final settingsBloc = SettingsBloc();
final authBloc = AuthBloc(
authRepository: authRepository,
classRepository: classRepository,
);
// 启动时检查认证状态
authBloc.add(const AppStarted());
// 异步恢复 SyncEngine 持久化队列fire-and-forget不阻塞 UI
syncEngine.restorePendingQueue();
// 启动网络监听 — 网络恢复时自动触发 trySync()
syncEngine.startAutoSync();
// 认证状态监听:登出时清除 token
// 注意:登录时 token 由 AuthRepository.login() 直接注入 ApiClient
authBloc.stream.listen((state) {
if (state is! Authenticated) {
apiClient.clearToken();
}
});
return MultiRepositoryProvider(
providers: [
RepositoryProvider<ApiClient>.value(value: apiClient),
RepositoryProvider<AuthRepository>.value(value: authRepository),
RepositoryProvider<JournalRepository>.value(value: journalRepository),
RepositoryProvider<RemoteJournalRepository>.value(value: remoteJournalRepository),
RepositoryProvider<SyncEngine>.value(value: syncEngine),
RepositoryProvider<ClassRepository>.value(value: classRepository),
],
child: ListenableProvider<SettingsBloc>.value(
value: settingsBloc,
child: Builder(
builder: (context) {
final settings = context.watch<SettingsBloc>();
return BlocProvider<AuthBloc>.value(
value: authBloc,
child: _AppView(
router: createAppRouter(authBloc),
themeMode: settings.state.themeMode,
),
);
},
),
),
);
}
}
/// App 视图 — MaterialApp.router 包装
class _AppView extends StatelessWidget {
final GoRouter router;
final ThemeMode themeMode;
const _AppView({required this.router, this.themeMode = ThemeMode.system});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: '暖记',
debugShowCheckedModeBanner: false,
theme: AppTheme.light(),
darkTheme: AppTheme.dark(),
themeMode: themeMode,
routerConfig: router,
);
}
}