diff --git a/apps/lib/features/calendar/reminders/reminder_action_executor.dart b/apps/lib/features/calendar/reminders/reminder_action_executor.dart index c956cae..365c69d 100644 --- a/apps/lib/features/calendar/reminders/reminder_action_executor.dart +++ b/apps/lib/features/calendar/reminders/reminder_action_executor.dart @@ -1,5 +1,7 @@ import 'dart:math'; +import 'package:flutter/widgets.dart'; + import '../data/services/calendar_service.dart'; import '../../../core/notifications/local_notification_service.dart'; import 'models/reminder_action.dart'; @@ -11,16 +13,23 @@ class ReminderActionExecutor { final LocalNotificationService _notificationService; final ReminderOutboxStore _outboxStore; final Random _random; + final bool Function() _isAppActive; ReminderActionExecutor({ required CalendarService calendarService, required LocalNotificationService notificationService, required ReminderOutboxStore outboxStore, Random? random, + bool Function()? isAppActive, }) : _calendarService = calendarService, _notificationService = notificationService, _outboxStore = outboxStore, - _random = random ?? Random(); + _random = random ?? Random(), + _isAppActive = + isAppActive ?? + (() => + WidgetsBinding.instance.lifecycleState == + AppLifecycleState.resumed); Future handleAction({ required ReminderAction action, @@ -86,6 +95,11 @@ class ReminderActionExecutor { } Future _archiveEvent(String eventId, ReminderAction action) async { + if (_isAppActive()) { + await _calendarService.archiveEvent(eventId); + return; + } + final opId = '${DateTime.now().millisecondsSinceEpoch}-${_random.nextInt(1 << 32)}'; final outboxItem = ReminderOutboxItem( @@ -96,11 +110,5 @@ class ReminderActionExecutor { occurredAt: DateTime.now(), ); await _outboxStore.enqueue(outboxItem); - try { - await _calendarService.archiveEvent(eventId); - await _outboxStore.markDone(opId); - } catch (error) { - await _outboxStore.markRetry(opId, error.toString()); - } } } diff --git a/apps/test/features/calendar/reminders/reminder_action_executor_test.dart b/apps/test/features/calendar/reminders/reminder_action_executor_test.dart index 02a5a06..20b02ae 100644 --- a/apps/test/features/calendar/reminders/reminder_action_executor_test.dart +++ b/apps/test/features/calendar/reminders/reminder_action_executor_test.dart @@ -30,6 +30,7 @@ void main() { calendarService: calendarService, notificationService: notificationService, outboxStore: outboxStore, + isAppActive: () => true, ); }); @@ -57,13 +58,50 @@ void main() { expect(pending, isEmpty); }); - test('archive failure writes pending outbox item', () async { + test( + 'archive action should send remote archive immediately when app active', + () async { + when( + () => notificationService.cancelEventReminder('evt_live'), + ).thenAnswer((_) async {}); + when( + () => calendarService.archiveEvent('evt_live'), + ).thenAnswer((_) async => null); + + executor = ReminderActionExecutor( + calendarService: calendarService, + notificationService: notificationService, + outboxStore: outboxStore, + isAppActive: () => true, + ); + + await executor.handleAction( + action: ReminderAction.archive, + payload: ReminderPayload( + eventId: 'evt_live', + title: 'sync', + startAt: DateTime.parse('2026-03-18T16:00:00+08:00'), + timezone: 'Asia/Shanghai', + ), + ); + + verify(() => calendarService.archiveEvent('evt_live')).called(1); + final pending = await outboxStore.listPending(); + expect(pending, isEmpty); + }, + ); + + test('archive in inactive app writes pending outbox item', () async { when( () => notificationService.cancelEventReminder('evt_1'), ).thenAnswer((_) async {}); - when( - () => calendarService.archiveEvent('evt_1'), - ).thenThrow(Exception('offline')); + + executor = ReminderActionExecutor( + calendarService: calendarService, + notificationService: notificationService, + outboxStore: outboxStore, + isAppActive: () => false, + ); await executor.handleAction( action: ReminderAction.archive,