Route reminder notification taps into app UI
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import 'dart:async';
|
||||
|
||||
/// Broadcasts reminder notification tap intents to the UI layer.
|
||||
class ReminderNotificationIntentBus {
|
||||
final StreamController<String> _controller =
|
||||
StreamController<String>.broadcast();
|
||||
|
||||
Stream<String> get intents => _controller.stream;
|
||||
|
||||
void emitTap(String reminderId) {
|
||||
if (reminderId.trim().isEmpty) {
|
||||
return;
|
||||
}
|
||||
_controller.add(reminderId.trim());
|
||||
}
|
||||
|
||||
Future<void> close() => _controller.close();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:relationship_saver/features/reminders/reminders_view.dart';
|
||||
import 'package:relationship_saver/features/reminders/scheduling/reminder_scheduler_provider.dart';
|
||||
|
||||
/// Listens for reminder notification taps and opens reminder management UI.
|
||||
class ReminderNotificationListener extends ConsumerStatefulWidget {
|
||||
const ReminderNotificationListener({required this.child, super.key});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
ConsumerState<ReminderNotificationListener> createState() =>
|
||||
_ReminderNotificationListenerState();
|
||||
}
|
||||
|
||||
class _ReminderNotificationListenerState
|
||||
extends ConsumerState<ReminderNotificationListener> {
|
||||
StreamSubscription<String>? _subscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_subscription = ref
|
||||
.read(reminderNotificationIntentBusProvider)
|
||||
.intents
|
||||
.listen(_handleIntent);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_subscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => widget.child;
|
||||
|
||||
Future<void> _handleIntent(String reminderId) async {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Navigator.of(context).push<void>(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (BuildContext context) =>
|
||||
_ReminderNotificationScreen(reminderId: reminderId),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ReminderNotificationScreen extends StatelessWidget {
|
||||
const _ReminderNotificationScreen({required this.reminderId});
|
||||
|
||||
final String reminderId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Reminder $reminderId')),
|
||||
body: const RemindersView(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:relationship_saver/features/local/local_models.dart';
|
||||
import 'package:relationship_saver/features/reminders/scheduling/reminder_notification_intent_bus.dart';
|
||||
import 'package:relationship_saver/features/reminders/scheduling/reminder_scheduler.dart';
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
@@ -14,7 +15,10 @@ class LocalNotificationsReminderScheduler implements ReminderScheduler {
|
||||
LocalNotificationsReminderScheduler({
|
||||
ReminderNotificationsDriver? driver,
|
||||
DateTime Function()? now,
|
||||
}) : _driver = driver ?? FlutterReminderNotificationsDriver(),
|
||||
ReminderNotificationIntentBus? intentBus,
|
||||
}) : _driver =
|
||||
driver ??
|
||||
FlutterReminderNotificationsDriver(onTapPayload: intentBus?.emitTap),
|
||||
_now = now ?? DateTime.now;
|
||||
|
||||
final ReminderNotificationsDriver _driver;
|
||||
@@ -122,10 +126,14 @@ abstract class ReminderNotificationsDriver {
|
||||
|
||||
class FlutterReminderNotificationsDriver
|
||||
implements ReminderNotificationsDriver {
|
||||
FlutterReminderNotificationsDriver({FlutterLocalNotificationsPlugin? plugin})
|
||||
: _plugin = plugin ?? FlutterLocalNotificationsPlugin();
|
||||
FlutterReminderNotificationsDriver({
|
||||
FlutterLocalNotificationsPlugin? plugin,
|
||||
void Function(String payload)? onTapPayload,
|
||||
}) : _plugin = plugin ?? FlutterLocalNotificationsPlugin(),
|
||||
_onTapPayload = onTapPayload;
|
||||
|
||||
final FlutterLocalNotificationsPlugin _plugin;
|
||||
final void Function(String payload)? _onTapPayload;
|
||||
Future<void>? _initializeFuture;
|
||||
|
||||
@override
|
||||
@@ -204,6 +212,13 @@ class FlutterReminderNotificationsDriver
|
||||
guid: 'ee23c0af-5898-4d96-b599-cf2d7d0d37d7',
|
||||
),
|
||||
),
|
||||
onDidReceiveNotificationResponse: (NotificationResponse response) {
|
||||
final String? payload = response.payload;
|
||||
if (payload == null || payload.isEmpty) {
|
||||
return;
|
||||
}
|
||||
_onTapPayload?.call(payload);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,30 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:relationship_saver/core/config/app_config.dart';
|
||||
import 'package:relationship_saver/features/reminders/scheduling/reminder_notification_intent_bus.dart';
|
||||
import 'package:relationship_saver/features/reminders/scheduling/reminder_scheduler.dart';
|
||||
import 'package:relationship_saver/features/reminders/scheduling/reminder_scheduler_local_notifications.dart';
|
||||
|
||||
/// Broadcasts reminder notification tap intents to interested UI listeners.
|
||||
final Provider<ReminderNotificationIntentBus>
|
||||
reminderNotificationIntentBusProvider = Provider<ReminderNotificationIntentBus>(
|
||||
(Ref ref) {
|
||||
final ReminderNotificationIntentBus bus = ReminderNotificationIntentBus();
|
||||
ref.onDispose(() {
|
||||
unawaited(bus.close());
|
||||
});
|
||||
return bus;
|
||||
},
|
||||
);
|
||||
|
||||
/// Provides reminder delivery scheduler implementation.
|
||||
final Provider<ReminderScheduler> reminderSchedulerProvider =
|
||||
Provider<ReminderScheduler>((Ref ref) {
|
||||
if (AppConfig.enableLocalNotifications) {
|
||||
return LocalNotificationsReminderScheduler();
|
||||
return LocalNotificationsReminderScheduler(
|
||||
intentBus: ref.watch(reminderNotificationIntentBusProvider),
|
||||
);
|
||||
}
|
||||
return const NoopReminderScheduler();
|
||||
});
|
||||
|
||||
+4
-1
@@ -4,6 +4,7 @@ import 'package:relationship_saver/core/config/app_theme.dart';
|
||||
import 'package:relationship_saver/features/auth/session_controller.dart';
|
||||
import 'package:relationship_saver/features/auth/sign_in_view.dart';
|
||||
import 'package:relationship_saver/features/home/app_shell.dart';
|
||||
import 'package:relationship_saver/features/reminders/scheduling/reminder_notification_listener.dart';
|
||||
import 'package:relationship_saver/features/sync/sync_background_runner.dart';
|
||||
import 'package:relationship_saver/integrations/backend/models/backend_models.dart';
|
||||
|
||||
@@ -28,7 +29,9 @@ class RelationshipSaverApp extends ConsumerWidget {
|
||||
body: sessionState.when(
|
||||
data: (session) => session == null
|
||||
? const SignInView()
|
||||
: const SyncBackgroundRunner(child: AppShell()),
|
||||
: const ReminderNotificationListener(
|
||||
child: SyncBackgroundRunner(child: AppShell()),
|
||||
),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (Object error, StackTrace stackTrace) => const SignInView(),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user