diff --git a/README.md b/README.md index 96786e599dd2f5ce0098110e077a14292a741638..da8ed6973d234a8b47699221704eed71aee5858a 100644 --- a/README.md +++ b/README.md @@ -6,5 +6,4 @@ Handle push notifications for your app, including remote and local notifications ## TODO - Add permissions management. -- Add interactive notifications support. - Better support of local notifications. diff --git a/RNNotifications/RNNotifications.m b/RNNotifications/RNNotifications.m index 5f18ed0d3db0d0e66c409863a1cb82ff28195358..d0d78d34df7c40f719637cf10b6a379280f1fd68 100644 --- a/RNNotifications/RNNotifications.m +++ b/RNNotifications/RNNotifications.m @@ -5,6 +5,7 @@ #import "RNNotifications.h" #import "RCTConvert.h" #import "RCTUtils.h" +#import "RNNotificationsBridgeQueue.h" NSString* const RNNotificationCreateAction = @"CREATE"; NSString* const RNNotificationClearAction = @"CLEAR"; @@ -110,6 +111,15 @@ RCT_EXPORT_MODULE() selector:@selector(handleNotificationActionTriggered:) name:RNNotificationActionTriggered object:nil]; + + NSDictionary* lastActionInfo = [RNNotificationsBridgeQueue sharedInstance].lastAction; + if (lastActionInfo) { + [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered + object:self + userInfo:lastActionInfo]; + [RNNotificationsBridgeQueue sharedInstance].lastAction = nil; + } + } /* @@ -146,14 +156,12 @@ RCT_EXPORT_MODULE() + (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler { - [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:notification.userInfo]; - completionHandler(); + [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:notification.userInfo completionHandler:completionHandler]; } + (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler { - [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:userInfo]; - completionHandler(); + [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:userInfo completionHandler:completionHandler]; } /* @@ -270,7 +278,7 @@ RCT_EXPORT_MODULE() [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; } -+ (void)emitNotificationActionForIdentifier:(NSString *)identifier responseInfo:(NSDictionary *)responseInfo userInfo:(NSDictionary *)userInfo ++ (void)emitNotificationActionForIdentifier:(NSString *)identifier responseInfo:(NSDictionary *)responseInfo userInfo:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler { NSMutableDictionary* info = [[NSMutableDictionary alloc] initWithDictionary:@{ @"identifier": identifier }]; @@ -289,6 +297,9 @@ RCT_EXPORT_MODULE() [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered object:self userInfo:info]; + + [RNNotificationsBridgeQueue sharedInstance].lastAction = info; + [RNNotificationsBridgeQueue sharedInstance].lastCompletionHandler = completionHandler; } /* @@ -322,4 +333,18 @@ RCT_EXPORT_METHOD(updateNotificationCategories:(NSArray *)json) [RNNotifications updateNotificationCategories:json]; } +RCT_EXPORT_METHOD(log:(NSString *)message) +{ + NSLog(message); +} + +RCT_EXPORT_METHOD(completionHandler) +{ + void (^completionHandler)() = [RNNotificationsBridgeQueue sharedInstance].lastCompletionHandler; + if (completionHandler) { + completionHandler(); + [RNNotificationsBridgeQueue sharedInstance].lastCompletionHandler = nil; + } +} + @end diff --git a/RNNotifications/RNNotifications.xcodeproj/project.pbxproj b/RNNotifications/RNNotifications.xcodeproj/project.pbxproj index 816d451e3315e571a9883eed7a71cbecd58f7855..761eb17d0e7b357ae2f91fcb63cfd9d21e03e78a 100644 --- a/RNNotifications/RNNotifications.xcodeproj/project.pbxproj +++ b/RNNotifications/RNNotifications.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + D85B37451CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */; }; D8A2F7551CB57F1A002CC8F5 /* RNNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */; }; /* End PBXBuildFile section */ @@ -24,6 +25,8 @@ /* Begin PBXFileReference section */ 134814201AA4EA6300B7C361 /* libRNNotifications.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNNotifications.a; sourceTree = BUILT_PRODUCTS_DIR; }; + D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNotificationsBridgeQueue.m; sourceTree = ""; }; + D85B37461CC05A1200DE9EB6 /* RNNotificationsBridgeQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationsBridgeQueue.h; sourceTree = ""; }; D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNotifications.m; sourceTree = ""; }; D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNotifications.h; sourceTree = ""; }; /* End PBXFileReference section */ @@ -50,6 +53,8 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( + D85B37461CC05A1200DE9EB6 /* RNNotificationsBridgeQueue.h */, + D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */, D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */, D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */, 134814211AA4EA7D00B7C361 /* Products */, @@ -113,6 +118,7 @@ buildActionMask = 2147483647; files = ( D8A2F7551CB57F1A002CC8F5 /* RNNotifications.m in Sources */, + D85B37451CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/RNNotifications/RNNotificationsBridgeQueue.h b/RNNotifications/RNNotificationsBridgeQueue.h new file mode 100644 index 0000000000000000000000000000000000000000..6e0503e03417da351c771df89de5f5da839ca346 --- /dev/null +++ b/RNNotifications/RNNotificationsBridgeQueue.h @@ -0,0 +1,10 @@ +#import + +@interface RNNotificationsBridgeQueue : NSObject + +@property (nonatomic, retain) NSDictionary* lastAction; +@property (nonatomic, copy) void (^lastCompletionHandler)(); + ++ (nonnull instancetype)sharedInstance; + +@end \ No newline at end of file diff --git a/RNNotifications/RNNotificationsBridgeQueue.m b/RNNotifications/RNNotificationsBridgeQueue.m new file mode 100644 index 0000000000000000000000000000000000000000..5167f97c2fe818225b3d76fed0c71ff87acb5d42 --- /dev/null +++ b/RNNotifications/RNNotificationsBridgeQueue.m @@ -0,0 +1,14 @@ +#import "RNNotificationsBridgeQueue.h" + +@implementation RNNotificationsBridgeQueue + ++ (nonnull instancetype)sharedInstance { + static RNNotificationsBridgeQueue* sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [self new]; + }); + + return sharedInstance; +} +@end \ No newline at end of file diff --git a/example/index.ios.js b/example/index.ios.js index 63e846886a644b56337349726f4a9212f990c037..049174e09f5b3b044973abf012ff5a8b24416a2d 100644 --- a/example/index.ios.js +++ b/example/index.ios.js @@ -14,6 +14,38 @@ import React, { import NotificationsIOS, { NotificationAction, NotificationCategory } from 'react-native-notifications'; +let upvoteAction = new NotificationAction({ + activationMode: "background", + title: String.fromCodePoint(0x1F44D), + identifier: "UPVOTE_ACTION" +}, (action, completed) => { + NotificationsIOS.log("ACTION RECEIVED"); + NotificationsIOS.log(JSON.stringify(action)); + + completed(); +}); + +let replyAction = new NotificationAction({ + activationMode: "background", + title: "Reply", + behavior: "textInput", + authenticationRequired: true, + identifier: "REPLY_ACTION" +}, (action, completed) => { + console.log("ACTION RECEIVED"); + console.log(action); + + completed(); +}); + +let cat = new NotificationCategory({ + identifier: "SOME_CATEGORY", + actions: [upvoteAction, replyAction], + context: "default" +}); + +NotificationsIOS.setCategories([cat]); + class NotificationsExampleApp extends Component { constructor() { @@ -26,35 +58,7 @@ class NotificationsExampleApp extends Component { } componentDidMount() { - PushNotificationIOS.requestPermissions(); - - let upvoteAction = new NotificationAction({ - activationMode: "background", - title: String.fromCodePoint(0x1F44D), - identifier: "UPVOTE_ACTION" - }, (action) => { - console.log("ACTION RECEIVED"); - console.log(action); - }); - - let replyAction = new NotificationAction({ - activationMode: "background", - title: "Reply", - behavior: "textInput", - authenticationRequired: true, - identifier: "REPLY_ACTION" - }, (action) => { - console.log("ACTION RECEIVED"); - console.log(action); - }); - - let cat = new NotificationCategory({ - identifier: "SOME_CATEGORY", - actions: [upvoteAction, replyAction], - context: "default" - }); - - NotificationsIOS.setCategories([cat]); + // PushNotificationIOS.requestPermissions(); } onPushRegistered(deviceToken) { @@ -94,7 +98,7 @@ class NotificationsExampleApp extends Component { NotificationsIOS.removeEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); NotificationsIOS.removeEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this)); NotificationsIOS.removeEventListener('notificationOpened', this.onNotificationOpened.bind(this)); - NotificationsIOS.resetCategories(); + // NotificationsIOS.resetCategories(); } _onNotification(notification) { diff --git a/index.ios.js b/index.ios.js index 5d3a29ff948d2ac82e36b68f4ba311fb209f5f25..d313d51610fbfe2d792c04fd7d4c8ec10a92e38a 100644 --- a/index.ios.js +++ b/index.ios.js @@ -76,7 +76,9 @@ export default class NotificationsIOS { let actionHandler = _actionHandlers.get(action.identifier); if (actionHandler) { - actionHandler(action); + action.notification = new IOSNotification(action.notification); + + actionHandler(action, () => { NativeRNNotifications.completionHandler(); }); } } @@ -89,7 +91,7 @@ export default class NotificationsIOS { if (categories) { // subscribe once for all actions - _actionListener = NativeAppEventEmitter.addListener(DEVICE_NOTIFICATION_ACTION_RECEIVED, this._actionHandlerDispatcher); + _actionListener = NativeAppEventEmitter.addListener(DEVICE_NOTIFICATION_ACTION_RECEIVED, this._actionHandlerDispatcher.bind(this)); notificationCategories = categories.map(category => { return Object.assign({}, category.options, { @@ -117,4 +119,8 @@ export default class NotificationsIOS { _actionHandlers.clear(); } + + static log(message) { + NativeRNNotifications.log(message); + } } diff --git a/test/index.ios.spec.js b/test/index.ios.spec.js index 57dfeb2d2b8642384c11eae9ca90393b88d5f68d..4c98a868e9e31bc3e7171af611714a54959f19db 100644 --- a/test/index.ios.spec.js +++ b/test/index.ios.spec.js @@ -149,7 +149,7 @@ describe("NotificationsIOS", () => { NotificationsIOS.setCategories([someCategory]); expect(nativeAppAddEventListener).to.have.been.calledOnce; - expect(nativeAppAddEventListener).to.have.been.calledWith("notificationActionReceived", NotificationsIOS._actionHandlerDispatcher); + expect(nativeAppAddEventListener).to.have.been.calledWith("notificationActionReceived", sinon.match.func); }); });