From c840abc29eedb3b075595614e389e3b4a837b05c Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 30 Aug 2021 10:17:02 +0200 Subject: [PATCH 1/4] feat(stream_chat_v1): implement foreground notifications --- packages/stream_chat_v1/lib/channel_list.dart | 10 +++- packages/stream_chat_v1/lib/home_page.dart | 56 +++++++++++++++++++ .../stream_chat_v1/lib/routes/app_routes.dart | 28 ++++++---- 3 files changed, 82 insertions(+), 12 deletions(-) diff --git a/packages/stream_chat_v1/lib/channel_list.dart b/packages/stream_chat_v1/lib/channel_list.dart index 5bac6cf..0586b92 100644 --- a/packages/stream_chat_v1/lib/channel_list.dart +++ b/packages/stream_chat_v1/lib/channel_list.dart @@ -145,6 +145,15 @@ class _ChannelList extends State { ) : ChannelsBloc( child: ChannelListView( + onChannelTap: (channel, _) { + Navigator.pushNamed( + context, + Routes.CHANNEL_PAGE, + arguments: ChannelPageArgs( + channel: channel, + ), + ); + }, onStartChatPressed: () { Navigator.pushNamed(context, Routes.NEW_CHAT); }, @@ -154,7 +163,6 @@ class _ChannelList extends State { pagination: PaginationParams( limit: 20, ), - channelWidget: ChannelPage(), onViewInfoTap: (channel) { Navigator.pop(context); if (channel.memberCount == 2 && channel.isDistinct) { diff --git a/packages/stream_chat_v1/lib/home_page.dart b/packages/stream_chat_v1/lib/home_page.dart index 828230f..2345423 100644 --- a/packages/stream_chat_v1/lib/home_page.dart +++ b/packages/stream_chat_v1/lib/home_page.dart @@ -1,9 +1,56 @@ +import 'dart:async'; + +import 'package:example/channel_page.dart'; +import 'package:example/notifications_service.dart'; import 'package:example/routes/app_routes.dart'; import 'package:example/routes/routes.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +class MyObserver extends NavigatorObserver { + Route? currentRoute; + late final StreamSubscription _subscription; + + MyObserver(StreamChatClient client, BuildContext context) { + _subscription = client.on(EventType.messageNew).listen((event) { + final channelId = event.channelId; + if (currentRoute?.settings.name == Routes.CHANNEL_PAGE) { + final args = currentRoute?.settings.arguments as ChannelPageArgs; + if (args.channel?.id == channelId) { + return; + } + } + + showLocalNotification(event, client.state.currentUser!.id, context); + }); + } + + @override + void didPop(Route route, Route? previousRoute) { + currentRoute = route; + } + + @override + void didPush(Route route, Route? previousRoute) { + currentRoute = route; + } + + @override + void didRemove(Route route, Route? previousRoute) { + currentRoute = route; + } + + @override + void didReplace({Route? newRoute, Route? oldRoute}) { + currentRoute = newRoute; + } + + void dispose() { + _subscription.cancel(); + } +} + class HomePageArgs { final StreamChatClient chatClient; @@ -24,6 +71,7 @@ class HomePage extends StatefulWidget { class _HomePageState extends State { final GlobalKey _navigatorKey = GlobalKey(); + MyObserver? _observer; @override Widget build(BuildContext context) { @@ -38,8 +86,16 @@ class _HomePageState extends State { key: _navigatorKey, onGenerateRoute: AppRoutes.generateRoute, initialRoute: Routes.CHANNEL_LIST_PAGE, + observers: [_observer!], ), ), ); } + + @override + void didChangeDependencies() { + _observer?.dispose(); + _observer = MyObserver(widget.chatClient, context); + super.didChangeDependencies(); + } } diff --git a/packages/stream_chat_v1/lib/routes/app_routes.dart b/packages/stream_chat_v1/lib/routes/app_routes.dart index 145fa8e..a9beedc 100644 --- a/packages/stream_chat_v1/lib/routes/app_routes.dart +++ b/packages/stream_chat_v1/lib/routes/app_routes.dart @@ -21,13 +21,13 @@ class AppRoutes { switch (settings.name) { case Routes.APP: return MaterialPageRoute( - settings: const RouteSettings(name: Routes.APP), + settings: RouteSettings(arguments: args, name: Routes.APP), builder: (_) { return MyApp(); }); case Routes.HOME: return MaterialPageRoute( - settings: const RouteSettings(name: Routes.HOME), + settings: RouteSettings(arguments: args, name: Routes.HOME), builder: (_) { final homePageArgs = args as HomePageArgs; return HomePage( @@ -36,18 +36,19 @@ class AppRoutes { }); case Routes.CHOOSE_USER: return MaterialPageRoute( - settings: const RouteSettings(name: Routes.CHOOSE_USER), + settings: RouteSettings(arguments: args, name: Routes.CHOOSE_USER), builder: (_) { return ChooseUserPage(); }); case Routes.ADVANCED_OPTIONS: return MaterialPageRoute( - settings: const RouteSettings(name: Routes.ADVANCED_OPTIONS), + settings: + RouteSettings(arguments: args, name: Routes.ADVANCED_OPTIONS), builder: (_) => AdvancedOptionsPage(), ); case Routes.CHANNEL_PAGE: return MaterialPageRoute( - settings: const RouteSettings(name: Routes.CHANNEL_PAGE), + settings: RouteSettings(arguments: args, name: Routes.CHANNEL_PAGE), builder: (_) { final channelPageArgs = args as ChannelPageArgs; return StreamChannel( @@ -61,19 +62,21 @@ class AppRoutes { }); case Routes.NEW_CHAT: return MaterialPageRoute( - settings: const RouteSettings(name: Routes.NEW_CHAT), + settings: RouteSettings(arguments: args, name: Routes.NEW_CHAT), builder: (_) { return NewChatScreen(); }); case Routes.NEW_GROUP_CHAT: return MaterialPageRoute( - settings: const RouteSettings(name: Routes.NEW_GROUP_CHAT), + settings: + RouteSettings(arguments: args, name: Routes.NEW_GROUP_CHAT), builder: (_) { return NewGroupChatScreen(); }); case Routes.NEW_GROUP_CHAT_DETAILS: return MaterialPageRoute( - settings: const RouteSettings(name: Routes.NEW_GROUP_CHAT_DETAILS), + settings: RouteSettings( + arguments: args, name: Routes.NEW_GROUP_CHAT_DETAILS), builder: (_) { return GroupChatDetailsScreen( selectedUsers: args as List?, @@ -81,7 +84,8 @@ class AppRoutes { }); case Routes.CHAT_INFO_SCREEN: return MaterialPageRoute( - settings: const RouteSettings(name: Routes.CHAT_INFO_SCREEN), + settings: + RouteSettings(arguments: args, name: Routes.CHAT_INFO_SCREEN), builder: (context) { return ChatInfoScreen( user: args as User?, @@ -90,7 +94,8 @@ class AppRoutes { }); case Routes.GROUP_INFO_SCREEN: return MaterialPageRoute( - settings: const RouteSettings(name: Routes.GROUP_INFO_SCREEN), + settings: + RouteSettings(arguments: args, name: Routes.GROUP_INFO_SCREEN), builder: (context) { return GroupInfoScreen( messageTheme: StreamChatTheme.of(context).ownMessageTheme, @@ -98,7 +103,8 @@ class AppRoutes { }); case Routes.CHANNEL_LIST_PAGE: return MaterialPageRoute( - settings: const RouteSettings(name: Routes.CHANNEL_LIST_PAGE), + settings: + RouteSettings(arguments: args, name: Routes.CHANNEL_LIST_PAGE), builder: (context) { return ChannelListPage(); }); From eeb6b2a6d0296cecdb9393869783f2375f2ee66e Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 30 Aug 2021 10:21:40 +0200 Subject: [PATCH 2/4] use notification.message_new event --- packages/stream_chat_v1/lib/home_page.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat_v1/lib/home_page.dart b/packages/stream_chat_v1/lib/home_page.dart index 2345423..2567802 100644 --- a/packages/stream_chat_v1/lib/home_page.dart +++ b/packages/stream_chat_v1/lib/home_page.dart @@ -13,7 +13,12 @@ class MyObserver extends NavigatorObserver { late final StreamSubscription _subscription; MyObserver(StreamChatClient client, BuildContext context) { - _subscription = client.on(EventType.messageNew).listen((event) { + _subscription = client + .on( + EventType.messageNew, + EventType.notificationMessageNew, + ) + .listen((event) { final channelId = event.channelId; if (currentRoute?.settings.name == Routes.CHANNEL_PAGE) { final args = currentRoute?.settings.arguments as ChannelPageArgs; From c8cc0d759b01557b50a7a7e890672c1bc642d41d Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 30 Aug 2021 10:24:07 +0200 Subject: [PATCH 3/4] add user check --- packages/stream_chat_v1/lib/home_page.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/stream_chat_v1/lib/home_page.dart b/packages/stream_chat_v1/lib/home_page.dart index 2567802..803a80f 100644 --- a/packages/stream_chat_v1/lib/home_page.dart +++ b/packages/stream_chat_v1/lib/home_page.dart @@ -19,6 +19,9 @@ class MyObserver extends NavigatorObserver { EventType.notificationMessageNew, ) .listen((event) { + if (event.message?.user?.id == client.state.currentUser?.id) { + return; + } final channelId = event.channelId; if (currentRoute?.settings.name == Routes.CHANNEL_PAGE) { final args = currentRoute?.settings.arguments as ChannelPageArgs; From 14d340c6b6529b13f125b5ba3bb0f2de4711efb8 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 1 Sep 2021 10:55:18 +0200 Subject: [PATCH 4/4] feat(stream_chat_v1): open channel on foreground notification press --- packages/stream_chat_v1/lib/home_page.dart | 13 +++++-- .../lib/notifications_service.dart | 37 ++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/packages/stream_chat_v1/lib/home_page.dart b/packages/stream_chat_v1/lib/home_page.dart index 803a80f..a53dd2c 100644 --- a/packages/stream_chat_v1/lib/home_page.dart +++ b/packages/stream_chat_v1/lib/home_page.dart @@ -12,7 +12,10 @@ class MyObserver extends NavigatorObserver { Route? currentRoute; late final StreamSubscription _subscription; - MyObserver(StreamChatClient client, BuildContext context) { + MyObserver( + StreamChatClient client, + GlobalKey navigatorKey, + ) { _subscription = client .on( EventType.messageNew, @@ -30,7 +33,11 @@ class MyObserver extends NavigatorObserver { } } - showLocalNotification(event, client.state.currentUser!.id, context); + showLocalNotification( + event, + client.state.currentUser!.id, + navigatorKey.currentState!.context, + ); }); } @@ -103,7 +110,7 @@ class _HomePageState extends State { @override void didChangeDependencies() { _observer?.dispose(); - _observer = MyObserver(widget.chatClient, context); + _observer = MyObserver(widget.chatClient, _navigatorKey); super.didChangeDependencies(); } } diff --git a/packages/stream_chat_v1/lib/notifications_service.dart b/packages/stream_chat_v1/lib/notifications_service.dart index d9ad7c5..86d6268 100644 --- a/packages/stream_chat_v1/lib/notifications_service.dart +++ b/packages/stream_chat_v1/lib/notifications_service.dart @@ -1,11 +1,16 @@ +import 'package:example/channel_page.dart'; import 'package:example/localizations.dart'; +import 'package:example/routes/routes.dart'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart' hide Message; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; void showLocalNotification( - Event event, String currentUserId, BuildContext context) async { + Event event, + String currentUserId, + BuildContext context, +) async { if (![ EventType.messageNew, EventType.notificationMessageNew, @@ -22,7 +27,34 @@ void showLocalNotification( android: initializationSettingsAndroid, iOS: initializationSettingsIOS, ); - await flutterLocalNotificationsPlugin.initialize(initializationSettings); + await flutterLocalNotificationsPlugin.initialize( + initializationSettings, + onSelectNotification: (channelCid) async { + if (channelCid != null) { + final client = StreamChat.of(context).client; + var channel = client.state.channels[channelCid]; + + if (channel == null) { + final splits = channelCid.split(':'); + final type = splits[0]; + final id = splits[1]; + channel = client.channel( + type, + id: id, + ); + await channel.watch(); + } + + Navigator.pushNamed( + context, + Routes.CHANNEL_PAGE, + arguments: ChannelPageArgs( + channel: channel, + ), + ); + } + }, + ); await flutterLocalNotificationsPlugin.show( event.message!.id.hashCode, event.message!.user!.name, @@ -37,5 +69,6 @@ void showLocalNotification( ), iOS: IOSNotificationDetails(), ), + payload: '${event.channelType}:${event.channelId}', ); }