From 20b9e70e8478b5f21c17bb069823419cd3e788de Mon Sep 17 00:00:00 2001 From: qzl Date: Fri, 20 Mar 2026 19:34:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(apps):=20=E4=BF=AE=E5=A4=8D=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E7=82=B9=E5=87=BB=E4=B8=8D=E6=98=BE=E7=A4=BAReminderO?= =?UTF-8?q?verlay=E5=92=8C=E6=97=A5=E5=8E=86=E7=BC=96=E8=BE=91=E5=90=8E?= =?UTF-8?q?=E4=B8=8D=E5=88=B7=E6=96=B0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AppDelegate: 只存储payload字段而非整个userInfo字典 - LocalNotificationService: 移除旧的取消/稍后提醒action按钮配置 - ReminderNotificationCallbacks: 添加onNotificationPayloadReceived静态回调 - IOSNotificationPayloadBridge: 添加setPendingPayload方法 - main.dart: 设置onNotificationPayloadReceived触发ReminderOverlay显示,添加WidgetsBindingObserver处理后台恢复 - CalendarEventDetailScreen: 编辑保存后正确传递刷新信号给日视图 --- apps/ios/Runner/AppDelegate.swift | 5 +- .../ios_notification_payload_bridge.dart | 4 ++ .../local_notification_service.dart | 61 +------------------ .../reminder_notification_callbacks.dart | 3 + .../screens/calendar_event_detail_screen.dart | 12 ++-- apps/lib/main.dart | 55 +++++++++++++---- 6 files changed, 62 insertions(+), 78 deletions(-) diff --git a/apps/ios/Runner/AppDelegate.swift b/apps/ios/Runner/AppDelegate.swift index 5c3053e..08435b4 100644 --- a/apps/ios/Runner/AppDelegate.swift +++ b/apps/ios/Runner/AppDelegate.swift @@ -25,9 +25,8 @@ import UserNotifications withCompletionHandler completionHandler: @escaping () -> Void ) { let userInfo = response.notification.request.content.userInfo - if let jsonData = try? JSONSerialization.data(withJSONObject: userInfo, options: []), - let jsonString = String(data: jsonData, encoding: .utf8) { - UserDefaults.standard.set(jsonString, forKey: "pending_notification_payload") + if let payloadString = userInfo["payload"] as? String { + UserDefaults.standard.set(payloadString, forKey: "pending_notification_payload") } completionHandler() } diff --git a/apps/lib/core/notifications/ios_notification_payload_bridge.dart b/apps/lib/core/notifications/ios_notification_payload_bridge.dart index 4842b34..3c2563e 100644 --- a/apps/lib/core/notifications/ios_notification_payload_bridge.dart +++ b/apps/lib/core/notifications/ios_notification_payload_bridge.dart @@ -21,6 +21,10 @@ class IOSNotificationPayloadBridge { } } + Future setPendingPayload(ReminderPayload payload) async { + await _prefs.setString(_key, jsonEncode(payload.toJson())); + } + Future clearPendingPayload() async { await _prefs.remove(_key); } diff --git a/apps/lib/core/notifications/local_notification_service.dart b/apps/lib/core/notifications/local_notification_service.dart index ea463d1..67674fa 100644 --- a/apps/lib/core/notifications/local_notification_service.dart +++ b/apps/lib/core/notifications/local_notification_service.dart @@ -7,31 +7,15 @@ import 'package:timezone/timezone.dart' as tz; import 'reminder_notification_callbacks.dart'; import '../../features/calendar/data/models/schedule_item_model.dart'; -import '../../features/calendar/reminders/models/reminder_action.dart'; import '../../features/calendar/reminders/models/reminder_payload.dart'; -typedef ReminderNotificationActionHandler = - Future Function({ - required ReminderAction action, - required ReminderPayload payload, - }); - class LocalNotificationService { - static const String _iosCategoryId = 'calendar_reminder_v2'; - static const String _actionCancel = 'cancel'; - static const String _actionSnooze = 'snooze10m'; - final FlutterLocalNotificationsPlugin _plugin; bool _initialized = false; - ReminderNotificationActionHandler? _actionHandler; LocalNotificationService({FlutterLocalNotificationsPlugin? plugin}) : _plugin = plugin ?? FlutterLocalNotificationsPlugin(); - void bindActionHandler(ReminderNotificationActionHandler handler) { - _actionHandler = handler; - } - Future initialize() async { if (_initialized) { return; @@ -39,25 +23,10 @@ class LocalNotificationService { tz_data.initializeTimeZones(); const android = AndroidInitializationSettings('@mipmap/ic_launcher'); - final ios = DarwinInitializationSettings( + const ios = DarwinInitializationSettings( requestAlertPermission: false, requestBadgePermission: false, requestSoundPermission: false, - notificationCategories: [ - DarwinNotificationCategory( - _iosCategoryId, - actions: [ - DarwinNotificationAction.plain(_actionCancel, '取消'), - DarwinNotificationAction.plain( - _actionSnooze, - '稍后提醒', - options: { - DarwinNotificationActionOption.foreground, - }, - ), - ], - ), - ], ); final settings = InitializationSettings(android: android, iOS: ios); @@ -190,20 +159,11 @@ class LocalNotificationService { timeoutAfter: 30000, autoCancel: true, groupKey: _getGroupKey(fireAt), - actions: [ - AndroidNotificationAction(_actionCancel, '取消'), - AndroidNotificationAction( - _actionSnooze, - '稍后提醒', - showsUserInterface: true, - ), - ], ), iOS: DarwinNotificationDetails( presentAlert: true, presentSound: true, presentBadge: true, - categoryIdentifier: _iosCategoryId, threadIdentifier: _getThreadIdentifier(fireAt), ), ); @@ -341,23 +301,6 @@ class LocalNotificationService { return; } - final actionId = response.actionId; - ReminderAction? action; - if (actionId == _actionCancel) { - action = ReminderAction.archive; - } else if (actionId == _actionSnooze) { - action = ReminderAction.snooze10m; - } - - if (action == null) { - return; - } - - final handler = _actionHandler; - if (handler == null) { - return; - } - - await handler(action: action, payload: payload); + ReminderNotificationCallbacks.onNotificationPayloadReceived?.call(payload); } } diff --git a/apps/lib/core/notifications/reminder_notification_callbacks.dart b/apps/lib/core/notifications/reminder_notification_callbacks.dart index 439ebeb..9d97d58 100644 --- a/apps/lib/core/notifications/reminder_notification_callbacks.dart +++ b/apps/lib/core/notifications/reminder_notification_callbacks.dart @@ -5,6 +5,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../features/calendar/reminders/models/reminder_payload.dart'; + typedef ReminderNotificationResponseHandler = Future Function(NotificationResponse response); @@ -13,6 +15,7 @@ class ReminderNotificationCallbacks { 'calendar_reminder_pending_notification_responses_v1'; static ReminderNotificationResponseHandler? _responseHandler; static Future _pendingStorageLock = Future.value(); + static void Function(ReminderPayload)? onNotificationPayloadReceived; @visibleForTesting static Future resetForTest() async { diff --git a/apps/lib/features/calendar/ui/screens/calendar_event_detail_screen.dart b/apps/lib/features/calendar/ui/screens/calendar_event_detail_screen.dart index 60104fb..7e4883d 100644 --- a/apps/lib/features/calendar/ui/screens/calendar_event_detail_screen.dart +++ b/apps/lib/features/calendar/ui/screens/calendar_event_detail_screen.dart @@ -218,15 +218,19 @@ class _CalendarEventDetailScreenState extends State { return event.startAt.isBefore(now); } - void _handleHeaderAction( + Future _handleHeaderAction( _CalendarHeaderAction action, ScheduleItemModel event, - ) { + ) async { switch (action) { case _CalendarHeaderAction.edit: - context.push(AppRoutes.calendarEventEdit(event.id)).then((_) { + final changed = await context.push( + AppRoutes.calendarEventEdit(event.id), + ); + if (changed == true) { _loadEvent(); - }); + if (mounted) context.pop(true); + } return; case _CalendarHeaderAction.delete: _showDeleteConfirmation(); diff --git a/apps/lib/main.dart b/apps/lib/main.dart index 6fee5af..f4bfe18 100644 --- a/apps/lib/main.dart +++ b/apps/lib/main.dart @@ -18,7 +18,6 @@ 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_action_executor.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'; @@ -32,15 +31,6 @@ void main() async { await AppConstants.init(); final rootNavigatorKey = GlobalKey(); - sl().bindActionHandler(({ - required action, - required payload, - }) { - return sl().handleAction( - action: action, - payload: payload, - ); - }); await sl().initialize(); final authBloc = sl(); @@ -74,8 +64,23 @@ void main() async { 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( @@ -116,18 +121,39 @@ class LinksyApp extends StatefulWidget { State createState() => _LinksyAppState(); } -class _LinksyAppState extends State { +class _LinksyAppState extends State 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 _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(); } } @@ -147,10 +173,15 @@ class _LinksyAppState extends State { ), ), ); - Overlay.of(context).insert(_reminderOverlay!); } + void showReminderOverlayFromNotification() { + if (widget.queueManager.currentPayload != null) { + _showReminderOverlay(); + } + } + void _onReminderComplete() { _reminderOverlay?.remove(); _reminderOverlay = null;