Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions intercom_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

* Bump Intercom Android SDK version to [18.10.0](https://github.com/intercom/intercom-android/releases/tag/18.10.0)
* Bump Intercom iOS SDK version to [19.9.0](https://github.com/intercom/intercom-ios/releases/tag/19.9.0)
* Added API `setUserJwtProvider` so the SDK can request a fresh user JWT when the current one expires. Not supported on web.
* Added API `removeUserJwtProvider`. Not supported on web.
* Updated dependency `intercom_flutter_platform_interface: ^2.0.9`
* Updated dependency `intercom_flutter_web: ^1.1.14`

## 9.6.31

Expand Down
2 changes: 2 additions & 0 deletions intercom_flutter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ But you can pre-define some Intercom settings, if you want (optional).
- [ ] displayHelpCenterCollections
- [ ] getWindowDidHideStream
- [ ] suppressProactiveContent
- [ ] setUserJwtProvider
- [ ] removeUserJwtProvider

## Using Intercom keys with `--dart-define`

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.maido.intercom

import android.app.Application
import android.os.Handler
import android.os.Looper
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
Expand All @@ -13,6 +15,7 @@ import io.intercom.android.sdk.*
import io.intercom.android.sdk.identity.Registration
import io.intercom.android.sdk.push.IntercomPushClient
import io.intercom.android.sdk.ui.theme.ThemeMode
import java.util.concurrent.atomic.AtomicBoolean

// No-op stream handler for windowDidHide event since it's only supported on iOS.
class WindowDidHideStreamHandler : EventChannel.StreamHandler {
Expand All @@ -33,10 +36,13 @@ class IntercomFlutterPlugin : FlutterPlugin, MethodCallHandler, EventChannel.Str

private val intercomPushClient = IntercomPushClient()
private var unreadConversationCountListener: UnreadConversationCountListener? = null
private var channel: MethodChannel? = null

override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val channel = MethodChannel(flutterPluginBinding.binaryMessenger, "maido.io/intercom")
channel.setMethodCallHandler(IntercomFlutterPlugin())
val methodChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "maido.io/intercom")
val plugin = IntercomFlutterPlugin()
plugin.channel = methodChannel
methodChannel.setMethodCallHandler(plugin)
val unreadEventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "maido.io/intercom/unread")
unreadEventChannel.setStreamHandler(IntercomFlutterPlugin())
val windowDidHideEventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "maido.io/intercom/windowDidHide")
Expand Down Expand Up @@ -281,6 +287,48 @@ class IntercomFlutterPlugin : FlutterPlugin, MethodCallHandler, EventChannel.Str
result.success("Jwt added")
}
}
"setUserJwtProvider" -> {
val methodChannel = channel
Intercom.client().setUserJwtProvider(object : IntercomUserJwtProvider {
override fun provideUserJwt(callback: IntercomUserJwtCallback) {
if (methodChannel == null) {
callback.onFailure()
return
}
Handler(Looper.getMainLooper()).post {
val completed = AtomicBoolean(false)
methodChannel.invokeMethod("provideUserJwt", null, object : MethodChannel.Result {
override fun success(result: Any?) {
if (!completed.compareAndSet(false, true)) return
val jwt = result as? String
if (!jwt.isNullOrBlank()) {
callback.onSuccess(jwt)
} else {
callback.onFailure()
}
}

override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
if (completed.compareAndSet(false, true)) {
callback.onFailure()
}
}

override fun notImplemented() {
if (completed.compareAndSet(false, true)) {
callback.onFailure()
}
}
})
}
}
})
result.success("User JWT provider set")
}
"removeUserJwtProvider" -> {
Intercom.client().removeUserJwtProvider()
result.success("User JWT provider removed")
}
"setAuthTokens" -> {
val tokens = call.argument<Map<String, String>>("tokens")
if (!tokens.isNullOrEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,16 @@ - (FlutterError*)onCancelWithArguments:(id)arguments {
}
@end

@implementation IntercomFlutterPlugin
@implementation IntercomFlutterPlugin {
FlutterMethodChannel *_channel;
}

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
IntercomFlutterPlugin* instance = [[IntercomFlutterPlugin alloc] init];
FlutterMethodChannel* channel =
[FlutterMethodChannel methodChannelWithName:@"maido.io/intercom"
binaryMessenger:[registrar messenger]];
instance->_channel = channel;
[registrar addMethodCallDelegate:instance channel:channel];
FlutterEventChannel* unreadChannel = [FlutterEventChannel eventChannelWithName:@"maido.io/intercom/unread"
binaryMessenger:[registrar messenger]];
Expand Down Expand Up @@ -287,6 +291,31 @@ - (void) handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
NSString *jwt = call.arguments[@"jwt"];
[Intercom setUserJwt:jwt];
result(@"Jwt added");
} else if([@"setUserJwtProvider" isEqualToString:call.method]) {
__weak FlutterMethodChannel *channel = _channel;
[Intercom setUserJwtProvider:^(void (^completion)(NSString * _Nullable jwt)) {
FlutterMethodChannel *strongChannel = channel;
if (strongChannel == nil) {
completion(nil);
return;
}
__block BOOL completed = NO;
[strongChannel invokeMethod:@"provideUserJwt" arguments:nil result:^(id result) {
if (completed) {
return;
}
completed = YES;
if ([result isKindOfClass:[NSString class]] && [(NSString *)result length] > 0) {
completion(result);
} else {
completion(nil);
}
}];
}];
result(@"User JWT provider set");
} else if([@"removeUserJwtProvider" isEqualToString:call.method]) {
[Intercom removeUserJwtProvider];
result(@"User JWT provider removed");
} else if([@"setAuthTokens" isEqualToString:call.method]) {
NSDictionary *tokens = call.arguments[@"tokens"];
if (tokens != nil && [tokens count] > 0) {
Expand Down
31 changes: 31 additions & 0 deletions intercom_flutter/lib/intercom_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import 'package:intercom_flutter_platform_interface/intercom_status_callback.dar
/// export the [IntercomVisibility] enum
export 'package:intercom_flutter_platform_interface/enumeral.dart'
show IntercomVisibility, IntercomTheme, IntercomProactiveContentType;
export 'package:intercom_flutter_platform_interface/intercom_flutter_platform_interface.dart'
show UserJwtProvider;
export 'package:intercom_flutter_platform_interface/intercom_status_callback.dart'
show IntercomStatusCallback, IntercomError;

Expand Down Expand Up @@ -297,6 +299,35 @@ class Intercom {
return IntercomFlutterPlatform.instance.setUserJwt(jwt);
}

/// Registers a callback that supplies a fresh user JWT when the current one
/// is about to expire or has been rejected.
///
/// The native SDK calls [provider] and retries the failed request once with
/// the returned token. Return `null` when a token cannot be supplied.
/// An empty string or a thrown error is treated the same as `null`.
///
/// Set the first token with [setUserJwt] before login. This callback only
/// supplies later tokens. Call [removeUserJwtProvider] to stop asking.
///
/// On iOS and Android, the SDK stops waiting after 10 seconds. This plugin
/// does not add its own timeout.
///
/// Note: this is not supported on web. Call [setUserJwt] again to refresh
/// the token there.
Future<void> setUserJwtProvider(UserJwtProvider provider) {
return IntercomFlutterPlatform.instance.setUserJwtProvider(provider);
}

/// Stops asking for fresh user JWTs.
///
/// Requests rejected for an expired JWT fail until [setUserJwt] is called
/// with a new token.
///
/// Note: this is not supported on web.
Future<void> removeUserJwtProvider() {
return IntercomFlutterPlatform.instance.removeUserJwtProvider();
}

/// Set up the authentication (user-defined token) to secure your Data
/// connectors. These tokens can be used for functionality such as Fin Actions.
/// You can provide multiple tokens at once. Please ensure you have created
Expand Down
4 changes: 2 additions & 2 deletions intercom_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ dependencies:
sdk: flutter
flutter_web_plugins:
sdk: flutter
intercom_flutter_platform_interface: ^2.0.8
intercom_flutter_web: ^1.1.13
intercom_flutter_platform_interface: ^2.0.9
intercom_flutter_web: ^1.1.14

dev_dependencies:
flutter_test:
Expand Down
10 changes: 10 additions & 0 deletions intercom_flutter/test/intercom_flutter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,16 @@ void main() {
});
});

test('setUserJwtProvider', () {
Intercom.instance.setUserJwtProvider(() async => 'fresh-jwt');
expectMethodCall('setUserJwtProvider');
});

test('removeUserJwtProvider', () {
Intercom.instance.removeUserJwtProvider();
expectMethodCall('removeUserJwtProvider');
});

test('setAuthTokens', () {
Intercom.instance.setAuthTokens({'security_token': 'test'});
expectMethodCall('setAuthTokens', arguments: {
Expand Down
4 changes: 4 additions & 0 deletions intercom_flutter_platform_interface/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 2.0.9

* Added methods `setUserJwtProvider` and `removeUserJwtProvider`.

## 2.0.8

* Added method `suppressProactiveContent`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import 'package:intercom_flutter_platform_interface/intercom_status_callback.dar
import 'package:intercom_flutter_platform_interface/method_channel_intercom_flutter.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';

/// Supplies a fresh user JWT when the native SDK asks for one.
///
/// Return the new token, or `null` when one cannot be supplied.
typedef UserJwtProvider = Future<String?> Function();

abstract class IntercomFlutterPlatform extends PlatformInterface {
IntercomFlutterPlatform() : super(token: _token);

Expand Down Expand Up @@ -287,6 +292,36 @@ abstract class IntercomFlutterPlatform extends PlatformInterface {
throw UnimplementedError('setUserJwt() has not been implemented.');
}

/// Registers a callback that supplies a fresh user JWT when the current one
/// is about to expire or has been rejected.
///
/// The native SDK calls [provider] and retries the failed request once with
/// the returned token. Return `null` when a token cannot be supplied.
/// An empty string or a thrown error is treated the same as `null`.
///
/// Set the first token with [setUserJwt] before login. This callback only
/// supplies later tokens. Call [removeUserJwtProvider] to stop asking.
///
/// On iOS and Android, the SDK stops waiting after 10 seconds. This plugin
/// does not add its own timeout.
///
/// Note: this is not supported on web. Call [setUserJwt] again to refresh
/// the token there.
Future<void> setUserJwtProvider(UserJwtProvider provider) {
throw UnimplementedError('setUserJwtProvider() has not been implemented.');
}

/// Stops asking for fresh user JWTs.
///
/// Requests rejected for an expired JWT fail until [setUserJwt] is called
/// with a new token.
///
/// Note: this is not supported on web.
Future<void> removeUserJwtProvider() {
throw UnimplementedError(
'removeUserJwtProvider() has not been implemented.');
}

/// Set up the authentication (user-defined token) to secure your Data
/// connectors. These tokens can be used for functionality such as Fin Actions.
/// You can provide multiple tokens at once. Please ensure you have created
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,44 @@ const EventChannel _windowDidHideChannel =

/// An implementation of [IntercomFlutterPlatform] that uses method channels.
class MethodChannelIntercomFlutter extends IntercomFlutterPlatform {
static UserJwtProvider? _userJwtProvider;
static bool _isMethodCallHandlerSet = false;

/// Registers the single handler for calls from native code on [_channel].
///
/// A channel holds only one handler, so every inbound method must be
/// dispatched from [_handleMethodCall] and registered only through here.
static void _ensureMethodCallHandler() {
if (_isMethodCallHandlerSet) return;
_channel.setMethodCallHandler(_handleMethodCall);
_isMethodCallHandlerSet = true;
}

static Future<dynamic> _handleMethodCall(MethodCall call) async {
switch (call.method) {
case 'provideUserJwt':
return _provideUserJwt();
default:
throw MissingPluginException();
}
}

static Future<String?> _provideUserJwt() async {
final provider = _userJwtProvider;
if (provider == null) {
return null;
}
try {
final jwt = await provider();
if (jwt == null || jwt.isEmpty) {
return null;
}
return jwt;
} catch (_) {
return null;
}
}

@override
Future<void> initialize(
String appId, {
Expand Down Expand Up @@ -261,6 +299,19 @@ class MethodChannelIntercomFlutter extends IntercomFlutterPlatform {
await _channel.invokeMethod('setUserJwt', {'jwt': jwt});
}

@override
Future<void> setUserJwtProvider(UserJwtProvider provider) async {
_userJwtProvider = provider;
_ensureMethodCallHandler();
await _channel.invokeMethod('setUserJwtProvider');
}

@override
Future<void> removeUserJwtProvider() async {
_userJwtProvider = null;
await _channel.invokeMethod('removeUserJwtProvider');
}

@override
Future<void> setAuthTokens(Map<String, String> tokens) async {
await _channel.invokeMethod('setAuthTokens', {'tokens': tokens});
Expand Down
2 changes: 1 addition & 1 deletion intercom_flutter_platform_interface/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: intercom_flutter_platform_interface
description: A common platform interface for the intercom_flutter plugin.
version: 2.0.8
version: 2.0.9
homepage: https://github.com/v3rm0n/intercom_flutter

dependencies:
Expand Down
Loading
Loading