diff --git a/apps/lib/main.dart b/apps/lib/main.dart index f165ece..1fe2bcf 100644 --- a/apps/lib/main.dart +++ b/apps/lib/main.dart @@ -2,10 +2,12 @@ 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/ios_notification_payload_bridge.dart'; import 'core/notifications/local_notification_service.dart'; import 'core/notifications/reminder_notification_callbacks.dart'; import 'core/router/app_router.dart'; @@ -17,7 +19,10 @@ 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/ui/reminder_foreground_presenter.dart'; +import 'features/calendar/reminders/reminder_queue_manager.dart'; +import 'features/calendar/reminders/models/reminder_action.dart'; +import 'features/calendar/reminders/models/reminder_payload.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'; @@ -28,10 +33,6 @@ void main() async { await configureDependencies(); await AppConstants.init(); final rootNavigatorKey = GlobalKey(); - final reminderForegroundPresenter = ReminderForegroundPresenter( - navigatorKey: rootNavigatorKey, - executor: sl(), - ); sl().bindActionHandler(({ required action, required payload, @@ -41,11 +42,17 @@ void main() async { payload: payload, ); }); - sl().bindInAppReminderHandler( - reminderForegroundPresenter.present, - ); await sl().initialize(); + final prefs = sl(); + final payloadBridge = IOSNotificationPayloadBridge(prefs); + final queueManager = ReminderQueueManager(); + final pendingPayload = await payloadBridge.getPendingPayload(); + if (pendingPayload != null) { + queueManager.enqueueFromClick(pendingPayload); + await payloadBridge.clearPendingPayload(); + } + final authBloc = sl(); authBloc.add(AuthStarted()); @@ -77,6 +84,8 @@ void main() async { notificationService: sl(), reminderActionExecutor: sl(), ), + pendingReminderPayload: pendingPayload, + reminderQueueManager: queueManager, ), ); @@ -89,35 +98,91 @@ void main() async { }); } -class LinksyApp extends StatelessWidget { +class LinksyApp extends StatefulWidget { final AuthBloc authBloc; final GlobalKey rootNavigatorKey; final AuthSessionBootstrapper sessionBootstrapper; + final ReminderPayload? pendingReminderPayload; + final ReminderQueueManager reminderQueueManager; const LinksyApp({ super.key, required this.authBloc, required this.rootNavigatorKey, required this.sessionBootstrapper, + this.pendingReminderPayload, + required this.reminderQueueManager, }); + @override + State createState() => _LinksyAppState(); +} + +class _LinksyAppState extends State { + OverlayEntry? _reminderOverlayEntry; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _maybeShowReminderOverlay(); + }); + } + + void _maybeShowReminderOverlay() { + if (widget.pendingReminderPayload == null) { + return; + } + final context = widget.rootNavigatorKey.currentContext; + if (context == null) { + return; + } + _reminderOverlayEntry = OverlayEntry( + builder: (context) => ReminderOverlay( + queueManager: widget.reminderQueueManager, + onComplete: _dismissReminderOverlay, + onSnooze: (minutes) { + final action = minutes >= 10 + ? ReminderAction.snooze10m + : ReminderAction.archive; + sl().handleAction( + action: action, + payload: widget.pendingReminderPayload!, + ); + }, + onArchive: () { + sl().handleAction( + action: ReminderAction.archive, + payload: widget.pendingReminderPayload!, + ); + }, + ), + ); + Overlay.of(context).insert(_reminderOverlayEntry!); + } + + void _dismissReminderOverlay() { + _reminderOverlayEntry?.remove(); + _reminderOverlayEntry = null; + } + @override Widget build(BuildContext context) { return MultiBlocProvider( providers: [ - BlocProvider.value(value: authBloc), + BlocProvider.value(value: widget.authBloc), BlocProvider(create: (_) => ChatBloc(apiClient: sl())), ], child: BlocListener( listenWhen: (previous, current) => previous != current, listener: (context, state) { - unawaited(sessionBootstrapper.syncForAuthState(state)); + unawaited(widget.sessionBootstrapper.syncForAuthState(state)); }, child: MaterialApp.router( title: 'Linksy', debugShowCheckedModeBanner: false, theme: AppTheme.light, - routerConfig: createAppRouter(authBloc), + routerConfig: createAppRouter(widget.authBloc), ), ), );