Files
social-app/apps/lib/main.dart
T
qzl 20b9e70e84 fix(apps): 修复通知点击不显示ReminderOverlay和日历编辑后不刷新问题
- AppDelegate: 只存储payload字段而非整个userInfo字典
- LocalNotificationService: 移除旧的取消/稍后提醒action按钮配置
- ReminderNotificationCallbacks: 添加onNotificationPayloadReceived静态回调
- IOSNotificationPayloadBridge: 添加setPendingPayload方法
- main.dart: 设置onNotificationPayloadReceived触发ReminderOverlay显示,添加WidgetsBindingObserver处理后台恢复
- CalendarEventDetailScreen: 编辑保存后正确传递刷新信号给日视图
2026-03-20 19:34:06 +08:00

242 lines
7.4 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'core/constants/app_constants.dart';
import 'core/cache/cache_refresh_coordinator.dart';
import 'core/di/injection.dart';
import 'core/notifications/local_notification_service.dart';
import 'core/notifications/reminder_notification_callbacks.dart';
import 'core/notifications/ios_notification_payload_bridge.dart';
import 'core/router/app_router.dart';
import 'core/startup/auth_session_bootstrapper.dart';
import 'core/theme/app_theme.dart';
import 'features/auth/presentation/bloc/auth_bloc.dart';
import 'features/auth/presentation/bloc/auth_event.dart';
import 'features/auth/presentation/bloc/auth_state.dart';
import 'features/calendar/data/services/calendar_service.dart';
import 'features/calendar/data/services/calendar_repository.dart';
import 'features/calendar/reminders/reminder_queue_manager.dart';
import 'features/calendar/reminders/ui/reminder_overlay.dart';
import 'features/calendar/ui/calendar_state_manager.dart';
import 'features/chat/presentation/bloc/chat_bloc.dart';
import 'features/settings/data/services/settings_user_cache.dart';
import 'features/todo/data/todo_repository.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await configureDependencies();
await AppConstants.init();
final rootNavigatorKey = GlobalKey<NavigatorState>();
await sl<LocalNotificationService>().initialize();
final authBloc = sl<AuthBloc>();
authBloc.add(AuthStarted());
final cacheRefreshCoordinator = CacheRefreshCoordinator(
minInterval: const Duration(minutes: 5),
onRefresh: () {
final selected = sl<CalendarStateManager>().selectedDate;
unawaited(
sl<CalendarRepository>().getDayEvents(selected, forceRefresh: true),
);
unawaited(
sl<CalendarRepository>().getMonthEvents(
DateTime(selected.year, selected.month, 1),
forceRefresh: true,
),
);
unawaited(sl<TodoRepository>().getPendingTodos(forceRefresh: true));
unawaited(sl<SettingsUserCache>().getProfile(forceRefresh: true));
},
);
WidgetsBinding.instance.addObserver(cacheRefreshCoordinator);
final prefs = await SharedPreferences.getInstance();
final payloadBridge = IOSNotificationPayloadBridge(prefs);
final pendingPayload = await payloadBridge.getPendingPayload();
final queueManager = ReminderQueueManager();
if (pendingPayload != null) {
queueManager.enqueueFromClick(pendingPayload);
await payloadBridge.clearPendingPayload();
}
final linksyAppKey = GlobalKey();
ReminderNotificationCallbacks.onNotificationPayloadReceived =
(payload) async {
await payloadBridge.setPendingPayload(payload);
queueManager.enqueueFromClick(payload);
WidgetsBinding.instance.addPostFrameCallback((_) {
final state = linksyAppKey.currentState;
if (state != null && state is _LinksyAppState) {
state.showReminderOverlayFromNotification();
}
});
};
runApp(
LinksyApp(
key: linksyAppKey,
authBloc: authBloc,
rootNavigatorKey: rootNavigatorKey,
sessionBootstrapper: AuthSessionBootstrapper(
calendarService: sl<CalendarService>(),
notificationService: sl<LocalNotificationService>(),
),
queueManager: queueManager,
payloadBridge: payloadBridge,
),
);
WidgetsBinding.instance.addPostFrameCallback((_) {
unawaited(
ReminderNotificationCallbacks.bindResponseHandler(
sl<LocalNotificationService>().handleNotificationResponse,
),
);
});
}
class LinksyApp extends StatefulWidget {
final AuthBloc authBloc;
final GlobalKey<NavigatorState> rootNavigatorKey;
final AuthSessionBootstrapper sessionBootstrapper;
final ReminderQueueManager queueManager;
final IOSNotificationPayloadBridge payloadBridge;
const LinksyApp({
super.key,
required this.authBloc,
required this.rootNavigatorKey,
required this.sessionBootstrapper,
required this.queueManager,
required this.payloadBridge,
});
@override
State<LinksyApp> createState() => _LinksyAppState();
}
class _LinksyAppState extends State<LinksyApp> with WidgetsBindingObserver {
OverlayEntry? _reminderOverlay;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_checkAndShowReminderOverlay();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_checkAndShowReminderOverlay();
}
}
Future<void> _checkAndShowReminderOverlay() async {
if (widget.queueManager.currentPayload != null) {
_showReminderOverlay();
return;
}
final pendingPayload = await widget.payloadBridge.getPendingPayload();
if (pendingPayload != null) {
widget.queueManager.enqueueFromClick(pendingPayload);
await widget.payloadBridge.clearPendingPayload();
_showReminderOverlay();
}
}
void _showReminderOverlay() {
if (_reminderOverlay != null) return;
_reminderOverlay = OverlayEntry(
builder: (context) => Positioned.fill(
child: Material(
color: Colors.black54,
child: ReminderOverlay(
queueManager: widget.queueManager,
onComplete: _onReminderComplete,
onSnooze: _onSnooze,
onArchive: _onArchive,
),
),
),
);
Overlay.of(context).insert(_reminderOverlay!);
}
void showReminderOverlayFromNotification() {
if (widget.queueManager.currentPayload != null) {
_showReminderOverlay();
}
}
void _onReminderComplete() {
_reminderOverlay?.remove();
_reminderOverlay = null;
if (!widget.queueManager.isEmpty) {
_showReminderOverlay();
}
}
Future<void> _onSnooze(int minutes) async {
final payload = widget.queueManager.currentPayload;
if (payload == null) return;
await sl<LocalNotificationService>().cancelEventReminder(payload.eventId);
final event = await sl<CalendarService>().getEventById(payload.eventId);
if (event != null) {
final snoozeTime = DateTime.now().add(Duration(minutes: minutes));
await sl<LocalNotificationService>().scheduleReminderAt(
event,
snoozeTime,
);
}
}
Future<void> _onArchive() async {
final payload = widget.queueManager.currentPayload;
if (payload == null) return;
try {
await sl<CalendarService>().archiveEvent(payload.eventId);
} catch (_) {
// archive failed, continue anyway
}
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AuthBloc>.value(value: widget.authBloc),
BlocProvider<ChatBloc>(create: (_) => ChatBloc(apiClient: sl())),
],
child: BlocListener<AuthBloc, AuthState>(
listenWhen: (previous, current) => previous != current,
listener: (context, state) {
unawaited(widget.sessionBootstrapper.syncForAuthState(state));
},
child: MaterialApp.router(
title: 'Linksy',
debugShowCheckedModeBanner: false,
theme: AppTheme.light,
routerConfig: createAppRouter(widget.authBloc),
),
),
);
}
}