diff --git a/intercom_flutter/CHANGELOG.md b/intercom_flutter/CHANGELOG.md index 310f3452..2df02c48 100755 --- a/intercom_flutter/CHANGELOG.md +++ b/intercom_flutter/CHANGELOG.md @@ -3,6 +3,7 @@ ## 9.6.2 * Removed deprecated `handlePushMessage` API (removed from native Intercom SDK) +* Added `getWindowDidHideStream` to listen for when the Intercom window is hidden (iOS only). ## 9.6.1 diff --git a/intercom_flutter/README.md b/intercom_flutter/README.md index bc352d30..28918c8b 100755 --- a/intercom_flutter/README.md +++ b/intercom_flutter/README.md @@ -148,6 +148,7 @@ But you can pre-define some Intercom settings, if you want (optional). - [ ] handlePush - [ ] displayCarousel - [ ] displayHelpCenterCollections +- [ ] getWindowDidHideStream ## Using Intercom keys with `--dart-define` diff --git a/intercom_flutter/android/src/main/kotlin/io/maido/intercom/IntercomFlutterPlugin.kt b/intercom_flutter/android/src/main/kotlin/io/maido/intercom/IntercomFlutterPlugin.kt index 22ae91c8..a233b993 100644 --- a/intercom_flutter/android/src/main/kotlin/io/maido/intercom/IntercomFlutterPlugin.kt +++ b/intercom_flutter/android/src/main/kotlin/io/maido/intercom/IntercomFlutterPlugin.kt @@ -14,6 +14,12 @@ import io.intercom.android.sdk.identity.Registration import io.intercom.android.sdk.push.IntercomPushClient import io.intercom.android.sdk.ui.theme.ThemeMode +// No-op stream handler for windowDidHide event since it's only supported on iOS. +class WindowDidHideStreamHandler : EventChannel.StreamHandler { + override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {} + override fun onCancel(arguments: Any?) {} +} + class IntercomFlutterPlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler, ActivityAware { companion object { @JvmStatic @@ -33,6 +39,8 @@ class IntercomFlutterPlugin : FlutterPlugin, MethodCallHandler, EventChannel.Str channel.setMethodCallHandler(IntercomFlutterPlugin()) val unreadEventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "maido.io/intercom/unread") unreadEventChannel.setStreamHandler(IntercomFlutterPlugin()) + val windowDidHideEventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "maido.io/intercom/windowDidHide") + windowDidHideEventChannel.setStreamHandler(WindowDidHideStreamHandler()) application = flutterPluginBinding.applicationContext as Application } diff --git a/intercom_flutter/example/lib/main.dart b/intercom_flutter/example/lib/main.dart index 69ac660f..9487823f 100644 --- a/intercom_flutter/example/lib/main.dart +++ b/intercom_flutter/example/lib/main.dart @@ -1,3 +1,6 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:intercom_flutter/intercom_flutter.dart'; @@ -13,7 +16,41 @@ void main() async { runApp(SampleApp()); } -class SampleApp extends StatelessWidget { +class SampleApp extends StatefulWidget { + @override + _SampleAppState createState() => _SampleAppState(); +} + +class _SampleAppState extends State { + StreamSubscription? _windowDidHideSubscription; + + @override + void initState() { + super.initState(); + // Listen for when the Intercom window is hidden + if (!kIsWeb) { + _windowDidHideSubscription = + Intercom.instance.getWindowDidHideStream().listen((_) { + // This will be called when the Intercom window is closed + // Only works on iOS + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Intercom window was closed!'), + duration: Duration(seconds: 2), + ), + ); + } + }); + } + } + + @override + void dispose() { + _windowDidHideSubscription?.cancel(); + super.dispose(); + } + @override Widget build(BuildContext context) { return MaterialApp( @@ -22,18 +59,31 @@ class SampleApp extends StatelessWidget { title: const Text('Intercom example app'), ), body: Center( - child: TextButton( - onPressed: () { - // NOTE: - // Messenger will load the messages only if the user is registered - // in Intercom. - // Either identified or unidentified. - // So make sure to login the user in Intercom first before opening - // the intercom messenger. - // Otherwise messenger will not load. - Intercom.instance.displayMessenger(); - }, - child: Text('Show messenger'), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + onPressed: () { + // NOTE: + // Messenger will load the messages only if the user is registered + // in Intercom. + // Either identified or unidentified. + // So make sure to login the user in Intercom first before opening + // the intercom messenger. + // Otherwise messenger will not load. + Intercom.instance.displayMessenger(); + }, + child: Text('Show messenger'), + ), + if (!kIsWeb) ...[ + SizedBox(height: 20), + Text( + 'Close the Intercom window to see the notification!', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 16), + ), + ], + ], ), ), ), diff --git a/intercom_flutter/ios/intercom_flutter/Sources/intercom_flutter/IntercomFlutterPlugin.m b/intercom_flutter/ios/intercom_flutter/Sources/intercom_flutter/IntercomFlutterPlugin.m index 1fbffa9c..393cab6c 100644 --- a/intercom_flutter/ios/intercom_flutter/Sources/intercom_flutter/IntercomFlutterPlugin.m +++ b/intercom_flutter/ios/intercom_flutter/Sources/intercom_flutter/IntercomFlutterPlugin.m @@ -2,6 +2,7 @@ #import id unread; +id windowDidHide; @implementation UnreadStreamHandler - (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { @@ -18,6 +19,20 @@ - (FlutterError*)onCancelWithArguments:(id)arguments { } @end +@implementation WindowDidHideStreamHandler +- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { + windowDidHide = [[NSNotificationCenter defaultCenter] addObserverForName:IntercomWindowDidHideNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { + eventSink(@(YES)); + }]; + return nil; +} + +- (FlutterError*)onCancelWithArguments:(id)arguments { + [[NSNotificationCenter defaultCenter] removeObserver:windowDidHide]; + return nil; +} +@end + @implementation IntercomFlutterPlugin + (void)registerWithRegistrar:(NSObject*)registrar { IntercomFlutterPlugin* instance = [[IntercomFlutterPlugin alloc] init]; @@ -31,6 +46,12 @@ + (void)registerWithRegistrar:(NSObject*)registrar { [[UnreadStreamHandler alloc] init]; [unreadChannel setStreamHandler:unreadStreamHandler]; + FlutterEventChannel* windowDidHideChannel = [FlutterEventChannel eventChannelWithName:@"maido.io/intercom/windowDidHide" + binaryMessenger:[registrar messenger]]; + WindowDidHideStreamHandler* windowDidHideStreamHandler = + [[WindowDidHideStreamHandler alloc] init]; + [windowDidHideChannel setStreamHandler:windowDidHideStreamHandler]; + } - (void) handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result{ diff --git a/intercom_flutter/ios/intercom_flutter/Sources/intercom_flutter/include/intercom_flutter/IntercomFlutterPlugin.h b/intercom_flutter/ios/intercom_flutter/Sources/intercom_flutter/include/intercom_flutter/IntercomFlutterPlugin.h index 67677316..f6dd6067 100644 --- a/intercom_flutter/ios/intercom_flutter/Sources/intercom_flutter/include/intercom_flutter/IntercomFlutterPlugin.h +++ b/intercom_flutter/ios/intercom_flutter/Sources/intercom_flutter/include/intercom_flutter/IntercomFlutterPlugin.h @@ -5,3 +5,6 @@ @interface UnreadStreamHandler : NSObject @end + +@interface WindowDidHideStreamHandler : NSObject +@end diff --git a/intercom_flutter/lib/intercom_flutter.dart b/intercom_flutter/lib/intercom_flutter.dart index 525b69a8..af18f858 100755 --- a/intercom_flutter/lib/intercom_flutter.dart +++ b/intercom_flutter/lib/intercom_flutter.dart @@ -49,6 +49,15 @@ class Intercom { return IntercomFlutterPlatform.instance.getUnreadStream(); } + /// You can listen for when the Intercom window is hidden. + /// + /// This stream emits when the Intercom window (messenger, help center, etc.) is closed. + /// This allows developers to perform certain actions in their app when the Intercom window is closed. + /// Only available on iOS. + Stream getWindowDidHideStream() { + return IntercomFlutterPlatform.instance.getWindowDidHideStream(); + } + /// This method allows you to set a fixed bottom padding for in app messages and the launcher. /// /// It is useful if your app has a tab bar or similar UI at the bottom of your window. diff --git a/intercom_flutter/pubspec.yaml b/intercom_flutter/pubspec.yaml index cd0c870c..05fc6742 100644 --- a/intercom_flutter/pubspec.yaml +++ b/intercom_flutter/pubspec.yaml @@ -9,8 +9,8 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - intercom_flutter_platform_interface: ^2.0.6 - intercom_flutter_web: ^1.1.11 + intercom_flutter_platform_interface: ^2.0.7 + intercom_flutter_web: ^1.1.12 dev_dependencies: flutter_test: diff --git a/intercom_flutter/test/intercom_flutter_test.dart b/intercom_flutter/test/intercom_flutter_test.dart index 89c78a53..e611fcc5 100755 --- a/intercom_flutter/test/intercom_flutter_test.dart +++ b/intercom_flutter/test/intercom_flutter_test.dart @@ -242,6 +242,34 @@ void main() { }); }); + group('WindowDidHide', () { + const String channelName = 'maido.io/intercom/windowDidHide'; + const MethodChannel channel = MethodChannel(channelName); + final bool value = true; + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .handlePlatformMessage( + channelName, + const StandardMethodCodec().encodeSuccessEnvelope(value), + (ByteData? data) {}, + ); + return; + }); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('testStream', () async { + expect(await Intercom.instance.getWindowDidHideStream().first, value); + }); + }); + test('displayArticle', () async { final String testArticleId = "123456"; await Intercom.instance.displayArticle(testArticleId); diff --git a/intercom_flutter_platform_interface/CHANGELOG.md b/intercom_flutter_platform_interface/CHANGELOG.md index a8c491d3..e4ee1eff 100755 --- a/intercom_flutter_platform_interface/CHANGELOG.md +++ b/intercom_flutter_platform_interface/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 2.0.7 + +* Added method `getWindowDidHideStream`. +* Removed deprecated `handlePushMessage`. + ## 2.0.6 * Added method `setThemeMode`. diff --git a/intercom_flutter_platform_interface/lib/intercom_flutter_platform_interface.dart b/intercom_flutter_platform_interface/lib/intercom_flutter_platform_interface.dart index c3490e7c..6f0bdadc 100644 --- a/intercom_flutter_platform_interface/lib/intercom_flutter_platform_interface.dart +++ b/intercom_flutter_platform_interface/lib/intercom_flutter_platform_interface.dart @@ -46,6 +46,16 @@ abstract class IntercomFlutterPlatform extends PlatformInterface { throw UnimplementedError('getUnreadStream() has not been implemented.'); } + /// You can listen for when the Intercom window is hidden. + /// + /// This stream emits when the Intercom window (messenger, help center, etc.) is closed. + /// This allows developers to perform certain actions in their app when the Intercom window is closed. + /// Only available on iOS. + Stream getWindowDidHideStream() { + throw UnimplementedError( + 'getWindowDidHideStream() has not been implemented.'); + } + /// To make sure that conversations between you and your users are kept private /// and that one user can't impersonate another then you need you need to setup /// the identity verification. diff --git a/intercom_flutter_platform_interface/lib/method_channel_intercom_flutter.dart b/intercom_flutter_platform_interface/lib/method_channel_intercom_flutter.dart index a8ddbc2e..aee16ca2 100644 --- a/intercom_flutter_platform_interface/lib/method_channel_intercom_flutter.dart +++ b/intercom_flutter_platform_interface/lib/method_channel_intercom_flutter.dart @@ -5,6 +5,8 @@ import 'package:intercom_flutter_platform_interface/intercom_status_callback.dar const MethodChannel _channel = MethodChannel('maido.io/intercom'); const EventChannel _unreadChannel = EventChannel('maido.io/intercom/unread'); +const EventChannel _windowDidHideChannel = + EventChannel('maido.io/intercom/windowDidHide'); /// An implementation of [IntercomFlutterPlatform] that uses method channels. class MethodChannelIntercomFlutter extends IntercomFlutterPlatform { @@ -26,6 +28,11 @@ class MethodChannelIntercomFlutter extends IntercomFlutterPlatform { return _unreadChannel.receiveBroadcastStream(); } + @override + Stream getWindowDidHideStream() { + return _windowDidHideChannel.receiveBroadcastStream(); + } + @override Future setUserHash(String userHash) async { await _channel.invokeMethod('setUserHash', {'userHash': userHash}); diff --git a/intercom_flutter_platform_interface/pubspec.yaml b/intercom_flutter_platform_interface/pubspec.yaml index 1d86bb16..ad4e8d51 100644 --- a/intercom_flutter_platform_interface/pubspec.yaml +++ b/intercom_flutter_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: intercom_flutter_platform_interface description: A common platform interface for the intercom_flutter plugin. -version: 2.0.6 +version: 2.0.7 homepage: https://github.com/v3rm0n/intercom_flutter dependencies: diff --git a/intercom_flutter_web/CHANGELOG.md b/intercom_flutter_web/CHANGELOG.md index 0581878e..2d5f8905 100755 --- a/intercom_flutter_web/CHANGELOG.md +++ b/intercom_flutter_web/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.1.12 + +* Updated `intercom_flutter_platform_interface` to `^2.0.7`. +* Removed deprecated `handlePushMessage`. + ## 1.1.11 * JSON-encode `intercomSettings` before injecting the Intercom script to avoid invalid JavaScript diff --git a/intercom_flutter_web/pubspec.yaml b/intercom_flutter_web/pubspec.yaml index 6fbea3e7..868d7ff5 100644 --- a/intercom_flutter_web/pubspec.yaml +++ b/intercom_flutter_web/pubspec.yaml @@ -1,6 +1,6 @@ name: intercom_flutter_web description: Web platform implementation of intercom_flutter -version: 1.1.11 +version: 1.1.12 homepage: https://github.com/v3rm0n/intercom_flutter flutter: @@ -15,7 +15,7 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - intercom_flutter_platform_interface: ^2.0.6 + intercom_flutter_platform_interface: ^2.0.7 uuid: ^4.2.1 # to get the random uuid for loginUnidentifiedUser in web web: ^1.0.0