Transfer showLocalNotification, backgroundKeepAlive from LLC to Core

Signed-off-by: Sahil Kumar <[email protected]>
This commit is contained in:
Sahil Kumar
2021-01-29 17:23:00 +05:30
parent 4b9a7d6837
commit 5310287fdd
7 changed files with 72 additions and 98 deletions
-10
View File
@@ -79,8 +79,6 @@ class Client {
Duration connectTimeout = const Duration(seconds: 6), Duration connectTimeout = const Duration(seconds: 6),
Duration receiveTimeout = const Duration(seconds: 6), Duration receiveTimeout = const Duration(seconds: 6),
Dio httpClient, Dio httpClient,
this.showLocalNotification,
this.backgroundKeepAlive = const Duration(minutes: 1),
RetryPolicy retryPolicy, RetryPolicy retryPolicy,
}) { }) {
_retryPolicy ??= RetryPolicy( _retryPolicy ??= RetryPolicy(
@@ -110,14 +108,6 @@ class Client {
/// The retry policy options getter /// The retry policy options getter
RetryPolicy get retryPolicy => _retryPolicy; 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 /// This client state
ClientState state; ClientState state;
@@ -277,9 +277,6 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
final client = Client( final client = Client(
apiKey, apiKey,
logLevel: Level.INFO, logLevel: Level.INFO,
showLocalNotification: (!kIsWeb && Platform.isAndroid)
? showLocalNotification
: null,
)..chatPersistenceClient = chatPersistentClient; )..chatPersistenceClient = chatPersistentClient;
try { try {
@@ -33,8 +33,6 @@ void main() async {
final client = Client( final client = Client(
apiKey ?? kDefaultStreamApiKey, apiKey ?? kDefaultStreamApiKey,
logLevel: Level.INFO, logLevel: Level.INFO,
showLocalNotification:
(!kIsWeb && Platform.isAndroid) ? showLocalNotification : null,
)..chatPersistenceClient = chatPersistentClient; )..chatPersistenceClient = chatPersistentClient;
if (userId != null) { if (userId != null) {
@@ -73,6 +71,7 @@ class MyApp extends StatelessWidget {
builder: (context, child) { builder: (context, child) {
return StreamChat( return StreamChat(
client: client, client: client,
onBackgroundEventReceived: showLocalNotification,
child: Builder( child: Builder(
builder: (context) => AnnotatedRegion<SystemUiOverlayStyle>( builder: (context) => AnnotatedRegion<SystemUiOverlayStyle>(
child: child, child: child,
@@ -1,12 +1,14 @@
import 'dart:io'; import 'dart:io';
import 'package:example/main.dart'; import 'package:example/main.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_apns/flutter_apns.dart'; import 'package:flutter_apns/flutter_apns.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart' import 'package:flutter_local_notifications/flutter_local_notifications.dart'
hide Message; hide Message;
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; 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 flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
final initializationSettingsAndroid = final initializationSettingsAndroid =
AndroidInitializationSettings('launch_background'); AndroidInitializationSettings('launch_background');
@@ -17,9 +19,9 @@ void showLocalNotification(Message message, ChannelModel channel) async {
); );
await flutterLocalNotificationsPlugin.initialize(initializationSettings); await flutterLocalNotificationsPlugin.initialize(initializationSettings);
await flutterLocalNotificationsPlugin.show( await flutterLocalNotificationsPlugin.show(
message.id.hashCode, event.message.id.hashCode,
'${message.user.name} @ ${channel.name}', event.message.user.name,
message.text, event.message.text,
NotificationDetails( NotificationDetails(
android: AndroidNotificationDetails( android: AndroidNotificationDetails(
'message channel', 'message channel',
@@ -35,24 +37,24 @@ void showLocalNotification(Message message, ChannelModel channel) async {
Future backgroundHandler(Map<String, dynamic> notification) async { Future backgroundHandler(Map<String, dynamic> notification) async {
print('new notification ${notification}'); print('new notification ${notification}');
final messageId = notification['data']['id']; // final messageId = notification['data']['id'];
//
final notificationData = await NotificationService.getAndStoreMessage( // final notificationData = await NotificationService.getAndStoreMessage(
messageId: messageId, // messageId: messageId,
storeMessageHandler: (messageResponse) { // storeMessageHandler: (messageResponse) {
return chatPersistentClient.updateChannelState( // return chatPersistentClient.updateChannelState(
ChannelState( // ChannelState(
messages: [messageResponse.message], // messages: [messageResponse.message],
channel: messageResponse.channel, // channel: messageResponse.channel,
), // ),
); // );
}, // },
); // );
//
showLocalNotification( // showLocalNotification(
notificationData.message, // notificationData.message,
notificationData.channel, // notificationData.channel,
); // );
} }
void initNotifications(Client client) { void initNotifications(Client client) {
@@ -34,11 +34,21 @@ class StreamChat extends StatefulWidget {
final Widget child; final Widget child;
final StreamChatThemeData streamChatThemeData; 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({ StreamChat({
Key key, Key key,
@required this.client, @required this.client,
@required this.child, @required this.child,
this.streamChatThemeData, this.streamChatThemeData,
this.onBackgroundEventReceived,
this.backgroundKeepAlive = const Duration(minutes: 1),
}) : super( }) : super(
key: key, key: key,
); );
@@ -82,8 +92,10 @@ class StreamChatState extends State<StreamChat> {
scaffoldBackgroundColor: streamTheme.colorTheme.white, scaffoldBackgroundColor: streamTheme.colorTheme.white,
), ),
child: StreamChatCore( child: StreamChatCore(
child: widget.child,
client: client, client: client,
child: widget.child,
onBackgroundEventReceived: widget.onBackgroundEventReceived,
backgroundKeepAlive: widget.backgroundKeepAlive,
), ),
); );
}, },
@@ -4,6 +4,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.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 /// Widget used to provide information about the chat to the widget tree
/// ///
/// class MyApp extends StatelessWidget { /// class MyApp extends StatelessWidget {
@@ -28,17 +30,26 @@ import 'package:stream_chat/stream_chat.dart';
class StreamChatCore extends StatefulWidget { class StreamChatCore extends StatefulWidget {
// ignore: public_member_api_docs // ignore: public_member_api_docs
final Client client; final Client client;
// ignore: public_member_api_docs // ignore: public_member_api_docs
final Widget child; 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 // ignore: public_member_api_docs
StreamChatCore({ StreamChatCore({
Key key, Key key,
@required this.client, @required this.client,
@required this.child, @required this.child,
}) : super( this.onBackgroundEventReceived,
key: key, this.backgroundKeepAlive = const Duration(minutes: 1),
); }) : super(key: key);
@override @override
StreamChatCoreState createState() => StreamChatCoreState(); StreamChatCoreState createState() => StreamChatCoreState();
@@ -82,56 +93,28 @@ class StreamChatCoreState extends State<StreamChatCore>
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
} }
StreamSubscription _newMessageSubscription; StreamSubscription _eventSubscription;
@override @override
void didChangeAppLifecycleState(AppLifecycleState state) { void didChangeAppLifecycleState(AppLifecycleState state) {
if (client.state?.user != null) { if (client.state?.user != null) {
if (state == AppLifecycleState.paused) { if (state == AppLifecycleState.paused) {
if (client.showLocalNotification != null) { if (widget.onBackgroundEventReceived != null) {
_newMessageSubscription = client _eventSubscription =
.on(EventType.messageNew) client.on().listen(widget.onBackgroundEventReceived);
.where((e) => e.user?.id != user.id) _disconnectTimer = Timer(
.where((e) => e.message.silent != true) widget.backgroundKeepAlive,
.where((e) => e.message.shadowed != true) client.disconnect,
.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();
});
} else { } else {
client.disconnect(); client.disconnect();
} }
} else if (state == AppLifecycleState.resumed) { } else if (state == AppLifecycleState.resumed) {
_newMessageSubscription?.cancel(); _eventSubscription?.cancel();
if (_disconnectTimer?.isActive == true) { if (_disconnectTimer?.isActive == true) {
_disconnectTimer.cancel(); _disconnectTimer.cancel();
} else { } else {
if (client.wsConnectionStatus.value == if (client.wsConnectionStatus == ConnectionStatus.disconnected) {
ConnectionStatus.disconnected) {
NotificationService.handleIosMessageQueue(client);
client.connect(); client.connect();
} }
} }
@@ -142,6 +125,7 @@ class StreamChatCoreState extends State<StreamChatCore>
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_eventSubscription?.cancel();
_disconnectTimer?.cancel(); _disconnectTimer?.cancel();
super.dispose(); super.dispose();
} }
@@ -9,7 +9,7 @@ import 'package:mockito/mockito.dart';
import 'mocks.dart'; import 'mocks.dart';
class MockShowLocalNotifications extends Mock { class MockShowLocalNotifications extends Mock {
void call(Message m, ChannelModel cm); void call(Event event);
} }
void main() { void main() {
@@ -97,14 +97,8 @@ void main() {
), ),
); );
final showLocalNotificationMock = MockShowLocalNotifications().call; final showLocalNotificationMock = MockShowLocalNotifications().call;
when(client.showLocalNotification)
.thenReturn(showLocalNotificationMock);
when(client.backgroundKeepAlive).thenReturn(Duration(
seconds: 4,
));
final eventStreamController = StreamController<Event>(); final eventStreamController = StreamController<Event>();
when(client.on(EventType.messageNew)) when(client.on()).thenAnswer((_) => eventStreamController.stream);
.thenAnswer((_) => eventStreamController.stream);
when(client.channel('test', id: 'testid')).thenReturn(channel); when(client.channel('test', id: 'testid')).thenReturn(channel);
@@ -113,6 +107,8 @@ void main() {
StreamChatCore( StreamChatCore(
key: scKey, key: scKey,
client: client, client: client,
onBackgroundEventReceived: showLocalNotificationMock,
backgroundKeepAlive: const Duration(seconds: 4),
child: Builder( child: Builder(
builder: (context) { builder: (context) {
return Container(); return Container();
@@ -144,13 +140,8 @@ void main() {
), ),
); );
final showLocalNotificationMock = MockShowLocalNotifications().call; final showLocalNotificationMock = MockShowLocalNotifications().call;
when(client.showLocalNotification).thenReturn(showLocalNotificationMock);
when(client.backgroundKeepAlive).thenReturn(Duration(
seconds: 4,
));
final eventStreamController = StreamController<Event>(); final eventStreamController = StreamController<Event>();
when(client.on(EventType.messageNew)) when(client.on()).thenAnswer((_) => eventStreamController.stream);
.thenAnswer((_) => eventStreamController.stream);
when(client.channel('test', id: 'testid')).thenReturn(channel); when(client.channel('test', id: 'testid')).thenReturn(channel);
@@ -159,6 +150,8 @@ void main() {
StreamChatCore( StreamChatCore(
key: scKey, key: scKey,
client: client, client: client,
onBackgroundEventReceived: showLocalNotificationMock,
backgroundKeepAlive: const Duration(seconds: 4),
child: Builder( child: Builder(
builder: (context) { builder: (context) {
return Container(); return Container();
@@ -178,12 +171,9 @@ void main() {
); );
eventStreamController.add(event); eventStreamController.add(event);
await untilCalled(showLocalNotificationMock(any, any)); await untilCalled(showLocalNotificationMock(event));
verify(showLocalNotificationMock( verify(showLocalNotificationMock(event)).called(1);
event.message,
any,
)).called(1);
}, },
); );
} }