* Push Notification Setup and attempt
* Office Ordering * Allow editing of Task and Ticket Details after creation
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
|
||||
import '../models/notification_item.dart';
|
||||
import '../providers/notifications_provider.dart';
|
||||
|
||||
/// Wraps the app and installs both a Supabase realtime listener and the
|
||||
/// FCM handlers described in the frontend design.
|
||||
///
|
||||
/// The navigator key is required so that snackbars and navigation can be
|
||||
/// triggered from outside the widget tree (e.g. from realtime callbacks).
|
||||
class NotificationBridge extends ConsumerStatefulWidget {
|
||||
const NotificationBridge({
|
||||
required this.navigatorKey,
|
||||
required this.child,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final GlobalKey<NavigatorState> navigatorKey;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
ConsumerState<NotificationBridge> createState() => _NotificationBridgeState();
|
||||
}
|
||||
|
||||
class _NotificationBridgeState extends ConsumerState<NotificationBridge> {
|
||||
// store previous notifications to diff
|
||||
List<NotificationItem> _prevList = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_setupFcmHandlers();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _showBanner(String type, NotificationItem item) {
|
||||
final ctx = widget.navigatorKey.currentState?.overlay?.context;
|
||||
if (ctx == null) return;
|
||||
|
||||
ScaffoldMessenger.of(ctx).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('New $type received!'),
|
||||
action: SnackBarAction(
|
||||
label: 'View',
|
||||
onPressed: () => _navigateToNotification(item),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _navigateToNotification(NotificationItem item) {
|
||||
widget.navigatorKey.currentState?.pushNamed(
|
||||
'/notification-detail',
|
||||
arguments: item,
|
||||
);
|
||||
}
|
||||
|
||||
void _setupFcmHandlers() {
|
||||
// ignore foreground messages; realtime websocket will surface them
|
||||
FirebaseMessaging.onMessage.listen((_) {});
|
||||
|
||||
// handle taps when the app is backgrounded
|
||||
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessageTap);
|
||||
|
||||
// handle a tap that launched the app from a terminated state
|
||||
FirebaseMessaging.instance.getInitialMessage().then((msg) {
|
||||
if (msg != null) _handleMessageTap(msg);
|
||||
});
|
||||
}
|
||||
|
||||
void _handleMessageTap(RemoteMessage message) {
|
||||
final data = message.data.cast<String, dynamic>();
|
||||
final item = NotificationItem.fromMap(data);
|
||||
_navigateToNotification(item);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// listen inside build; safe with ConsumerState
|
||||
ref.listen<AsyncValue<List<NotificationItem>>>(notificationsProvider, (
|
||||
previous,
|
||||
next,
|
||||
) {
|
||||
final prevList = _prevList;
|
||||
final nextList = next.maybeWhen(
|
||||
data: (d) => d,
|
||||
orElse: () => <NotificationItem>[],
|
||||
);
|
||||
if (nextList.length > prevList.length) {
|
||||
final newItem = nextList.last;
|
||||
_showBanner(newItem.type, newItem);
|
||||
}
|
||||
_prevList = nextList;
|
||||
});
|
||||
return widget.child;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user