diff --git a/packages/dart_client/lib/src/client.dart b/packages/dart_client/lib/src/client.dart index 988f0f8f..381d1cd6 100644 --- a/packages/dart_client/lib/src/client.dart +++ b/packages/dart_client/lib/src/client.dart @@ -79,8 +79,6 @@ class Client { Duration connectTimeout = const Duration(seconds: 6), Duration receiveTimeout = const Duration(seconds: 6), Dio httpClient, - this.showLocalNotification, - this.backgroundKeepAlive = const Duration(minutes: 1), RetryPolicy retryPolicy, }) { _retryPolicy ??= RetryPolicy( @@ -110,14 +108,6 @@ class Client { /// The retry policy options getter RetryPolicy get retryPolicy => _retryPolicy; - /// Method used to show a local notification while the app is in background - /// Switching to another application will not disconnect the client immediately - /// So, use this method to show the notification when receiving a new message via events - final void Function(Message, ChannelModel) showLocalNotification; - - /// The amount of time that will pass before disconnecting the client in the background - final Duration backgroundKeepAlive; - /// This client state ClientState state; diff --git a/packages/flutter_widgets/example/lib/advanced_options_page.dart b/packages/flutter_widgets/example/lib/advanced_options_page.dart index 25cbf51c..0b55212c 100644 --- a/packages/flutter_widgets/example/lib/advanced_options_page.dart +++ b/packages/flutter_widgets/example/lib/advanced_options_page.dart @@ -277,9 +277,6 @@ class _AdvancedOptionsPageState extends State { final client = Client( apiKey, logLevel: Level.INFO, - showLocalNotification: (!kIsWeb && Platform.isAndroid) - ? showLocalNotification - : null, )..chatPersistenceClient = chatPersistentClient; try { diff --git a/packages/flutter_widgets/example/lib/main.dart b/packages/flutter_widgets/example/lib/main.dart index caa11198..a44a8692 100644 --- a/packages/flutter_widgets/example/lib/main.dart +++ b/packages/flutter_widgets/example/lib/main.dart @@ -33,8 +33,6 @@ void main() async { final client = Client( apiKey ?? kDefaultStreamApiKey, logLevel: Level.INFO, - showLocalNotification: - (!kIsWeb && Platform.isAndroid) ? showLocalNotification : null, )..chatPersistenceClient = chatPersistentClient; if (userId != null) { @@ -73,6 +71,7 @@ class MyApp extends StatelessWidget { builder: (context, child) { return StreamChat( client: client, + onBackgroundEventReceived: showLocalNotification, child: Builder( builder: (context) => AnnotatedRegion( child: child, diff --git a/packages/flutter_widgets/example/lib/notifications_service.dart b/packages/flutter_widgets/example/lib/notifications_service.dart index 0eeb9cc0..e74a2a5a 100644 --- a/packages/flutter_widgets/example/lib/notifications_service.dart +++ b/packages/flutter_widgets/example/lib/notifications_service.dart @@ -1,12 +1,14 @@ import 'dart:io'; import 'package:example/main.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_apns/flutter_apns.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart' hide Message; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -void showLocalNotification(Message message, ChannelModel channel) async { +void showLocalNotification(Event event) async { + if (event.message == null) return; final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); final initializationSettingsAndroid = AndroidInitializationSettings('launch_background'); @@ -17,9 +19,9 @@ void showLocalNotification(Message message, ChannelModel channel) async { ); await flutterLocalNotificationsPlugin.initialize(initializationSettings); await flutterLocalNotificationsPlugin.show( - message.id.hashCode, - '${message.user.name} @ ${channel.name}', - message.text, + event.message.id.hashCode, + event.message.user.name, + event.message.text, NotificationDetails( android: AndroidNotificationDetails( 'message channel', @@ -35,24 +37,24 @@ void showLocalNotification(Message message, ChannelModel channel) async { Future backgroundHandler(Map notification) async { print('new notification ${notification}'); - final messageId = notification['data']['id']; - - final notificationData = await NotificationService.getAndStoreMessage( - messageId: messageId, - storeMessageHandler: (messageResponse) { - return chatPersistentClient.updateChannelState( - ChannelState( - messages: [messageResponse.message], - channel: messageResponse.channel, - ), - ); - }, - ); - - showLocalNotification( - notificationData.message, - notificationData.channel, - ); + // final messageId = notification['data']['id']; + // + // final notificationData = await NotificationService.getAndStoreMessage( + // messageId: messageId, + // storeMessageHandler: (messageResponse) { + // return chatPersistentClient.updateChannelState( + // ChannelState( + // messages: [messageResponse.message], + // channel: messageResponse.channel, + // ), + // ); + // }, + // ); + // + // showLocalNotification( + // notificationData.message, + // notificationData.channel, + // ); } void initNotifications(Client client) { diff --git a/packages/flutter_widgets/lib/src/stream_chat.dart b/packages/flutter_widgets/lib/src/stream_chat.dart index 37a163ec..0ea5efb2 100644 --- a/packages/flutter_widgets/lib/src/stream_chat.dart +++ b/packages/flutter_widgets/lib/src/stream_chat.dart @@ -34,11 +34,21 @@ class StreamChat extends StatefulWidget { final Widget child; final StreamChatThemeData streamChatThemeData; + /// The amount of time that will pass before disconnecting the client in the background + final Duration backgroundKeepAlive; + + /// Handler called whenever the [client] receives a new [Event] while the app + /// is in background. Can be used to display various notifications depending + /// upon the [Event.type] + final EventHandler onBackgroundEventReceived; + StreamChat({ Key key, @required this.client, @required this.child, this.streamChatThemeData, + this.onBackgroundEventReceived, + this.backgroundKeepAlive = const Duration(minutes: 1), }) : super( key: key, ); @@ -82,8 +92,10 @@ class StreamChatState extends State { scaffoldBackgroundColor: streamTheme.colorTheme.white, ), child: StreamChatCore( - child: widget.child, client: client, + child: widget.child, + onBackgroundEventReceived: widget.onBackgroundEventReceived, + backgroundKeepAlive: widget.backgroundKeepAlive, ), ); }, diff --git a/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart b/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart index 73f5eb61..1d2d4736 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart @@ -4,6 +4,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat/stream_chat.dart'; +typedef EventHandler = void Function(Event event); + /// Widget used to provide information about the chat to the widget tree /// /// class MyApp extends StatelessWidget { @@ -28,17 +30,26 @@ import 'package:stream_chat/stream_chat.dart'; class StreamChatCore extends StatefulWidget { // ignore: public_member_api_docs final Client client; + // ignore: public_member_api_docs final Widget child; + /// The amount of time that will pass before disconnecting the client in the background + final Duration backgroundKeepAlive; + + /// Handler called whenever the [client] receives a new [Event] while the app + /// is in background. Can be used to display various notifications depending + /// upon the [Event.type] + final EventHandler onBackgroundEventReceived; + // ignore: public_member_api_docs StreamChatCore({ Key key, @required this.client, @required this.child, - }) : super( - key: key, - ); + this.onBackgroundEventReceived, + this.backgroundKeepAlive = const Duration(minutes: 1), + }) : super(key: key); @override StreamChatCoreState createState() => StreamChatCoreState(); @@ -82,56 +93,28 @@ class StreamChatCoreState extends State WidgetsBinding.instance.addObserver(this); } - StreamSubscription _newMessageSubscription; + StreamSubscription _eventSubscription; @override void didChangeAppLifecycleState(AppLifecycleState state) { if (client.state?.user != null) { if (state == AppLifecycleState.paused) { - if (client.showLocalNotification != null) { - _newMessageSubscription = client - .on(EventType.messageNew) - .where((e) => e.user?.id != user.id) - .where((e) => e.message.silent != true) - .where((e) => e.message.shadowed != true) - .listen((event) async { - final channel = client.channel( - event.channelType, - id: event.channelId, - ); - - client.showLocalNotification( - event.message, - ChannelModel( - id: channel.id, - createdAt: channel.createdAt, - extraData: channel.extraData, - type: channel.type, - memberCount: channel.memberCount, - frozen: channel.frozen, - cid: channel.cid, - deletedAt: channel.deletedAt, - config: channel.config, - createdBy: channel.createdBy, - updatedAt: channel.updatedAt, - lastMessageAt: channel.lastMessageAt, - ), - ); - }); - _disconnectTimer = Timer(client.backgroundKeepAlive, () { - client.disconnect(); - }); + if (widget.onBackgroundEventReceived != null) { + _eventSubscription = + client.on().listen(widget.onBackgroundEventReceived); + _disconnectTimer = Timer( + widget.backgroundKeepAlive, + client.disconnect, + ); } else { client.disconnect(); } } else if (state == AppLifecycleState.resumed) { - _newMessageSubscription?.cancel(); + _eventSubscription?.cancel(); if (_disconnectTimer?.isActive == true) { _disconnectTimer.cancel(); } else { - if (client.wsConnectionStatus.value == - ConnectionStatus.disconnected) { - NotificationService.handleIosMessageQueue(client); + if (client.wsConnectionStatus == ConnectionStatus.disconnected) { client.connect(); } } @@ -142,6 +125,7 @@ class StreamChatCoreState extends State @override void dispose() { WidgetsBinding.instance.removeObserver(this); + _eventSubscription?.cancel(); _disconnectTimer?.cancel(); super.dispose(); } diff --git a/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart b/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart index 714375de..a8d7e227 100644 --- a/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart +++ b/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart @@ -9,7 +9,7 @@ import 'package:mockito/mockito.dart'; import 'mocks.dart'; class MockShowLocalNotifications extends Mock { - void call(Message m, ChannelModel cm); + void call(Event event); } void main() { @@ -97,14 +97,8 @@ void main() { ), ); final showLocalNotificationMock = MockShowLocalNotifications().call; - when(client.showLocalNotification) - .thenReturn(showLocalNotificationMock); - when(client.backgroundKeepAlive).thenReturn(Duration( - seconds: 4, - )); final eventStreamController = StreamController(); - when(client.on(EventType.messageNew)) - .thenAnswer((_) => eventStreamController.stream); + when(client.on()).thenAnswer((_) => eventStreamController.stream); when(client.channel('test', id: 'testid')).thenReturn(channel); @@ -113,6 +107,8 @@ void main() { StreamChatCore( key: scKey, client: client, + onBackgroundEventReceived: showLocalNotificationMock, + backgroundKeepAlive: const Duration(seconds: 4), child: Builder( builder: (context) { return Container(); @@ -144,13 +140,8 @@ void main() { ), ); final showLocalNotificationMock = MockShowLocalNotifications().call; - when(client.showLocalNotification).thenReturn(showLocalNotificationMock); - when(client.backgroundKeepAlive).thenReturn(Duration( - seconds: 4, - )); final eventStreamController = StreamController(); - when(client.on(EventType.messageNew)) - .thenAnswer((_) => eventStreamController.stream); + when(client.on()).thenAnswer((_) => eventStreamController.stream); when(client.channel('test', id: 'testid')).thenReturn(channel); @@ -159,6 +150,8 @@ void main() { StreamChatCore( key: scKey, client: client, + onBackgroundEventReceived: showLocalNotificationMock, + backgroundKeepAlive: const Duration(seconds: 4), child: Builder( builder: (context) { return Container(); @@ -178,12 +171,9 @@ void main() { ); eventStreamController.add(event); - await untilCalled(showLocalNotificationMock(any, any)); + await untilCalled(showLocalNotificationMock(event)); - verify(showLocalNotificationMock( - event.message, - any, - )).called(1); + verify(showLocalNotificationMock(event)).called(1); }, ); }