diff --git a/README.md b/README.md index 173f7a61a..e2fb11132 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,12 @@ Handle all the aspects of push notifications for your app, including remote and local notifications, interactive notifications, silent notifications, and more. -**All the native iOS notifications features are supported!** +**All the native iOS notifications features are supported!** _For information regarding proper integration with [react-native-navigation](https://github.com/wix/react-native-navigation), follow [this wiki](https://github.com/wix/react-native-notifications/wiki/Android:-working-with-RNN)._ - ### iOS -Interactive notifications example - - Remote (push) notifications - Local notifications - Background/Managed notifications (notifications that can be cleared from the server, like Facebook messenger and Whatsapp web) @@ -26,7 +23,6 @@ _For information regarding proper integration with [react-native-navigation](htt _Upcoming: local notifications, background-state Rx queue (iOS equivalent)_ - # Table of Content - [Installation and setup](./docs/installation.md) - Setting up the library in your app @@ -37,6 +33,7 @@ _Upcoming: local notifications, background-state Rx queue (iOS equivalent)_ - [Notifications layout control - Android (wiki page)](https://github.com/wix/react-native-notifications/wiki/Android:-Layout-Customization) - Learn how to fully customize your notifications layout on Android! # License + The MIT License. See [LICENSE](LICENSE) diff --git a/RNNotifications/RNNotifications.m b/RNNotifications/RNNotifications.m index 33786ed64..7bce859e9 100644 --- a/RNNotifications/RNNotifications.m +++ b/RNNotifications/RNNotifications.m @@ -23,6 +23,8 @@ NSString* const RNNotificationActionReceived = @"notificationActionReceived"; //NSString* const RNNotificationActionDismissed = @"RNNotificationActionDismissed"; +NSString* const RNNErrorDomain = @"RNNNotificationsError"; +static const int RNNErrorCode = -1; //////////////////////////////////////////////////////////////// #pragma mark conversions @@ -172,7 +174,11 @@ - (instancetype)init return self; } - ++ (BOOL)requiresMainQueueSetup +{ + // TODO: figure out if we can return NO + return YES; +} - (void)setBridge:(RCTBridge *)bridge { @@ -189,25 +195,40 @@ - (void)setBridge:(RCTBridge *)bridge #pragma mark private functions //////////////////////////////////////////////////////////////// -+ (void)requestPermissionsWithCategories:(NSMutableSet *)categories +- (void)requestPermissionsWithCategoriesInternal:(NSSet *)categories requestRemote: (BOOL) requestRemote { dispatch_async(dispatch_get_main_queue(), ^{ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error){ - if(!error) - { - if (granted) - { - dispatch_async(dispatch_get_main_queue(), ^{ - [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categories]; - [[UIApplication sharedApplication] registerForRemoteNotifications]; - }); - } + if (error) { + [self sendFailedToRegisterEvent:error]; + return; + } + + if (!granted) { + [self sendFailedToRegisterEvent:[RNNotifications errorWithMessage:@"UserDenied"]]; + return; } + + dispatch_async(dispatch_get_main_queue(), ^{ + [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categories]; + if (requestRemote) { + [[UIApplication sharedApplication] registerForRemoteNotifications]; + } else { + [self checkAndSendEvent:RNNotificationsRegistered body:@{}]; + } + }); }]; }); } ++ (NSError *)errorWithMessage:(NSString *)errorMessage +{ + return [NSError errorWithDomain:RNNErrorDomain + code:RNNErrorCode + userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(errorMessage, nil) }]; +} + //////////////////////////////////////////////////////////////// #pragma mark NRNNManagerDelegate //////////////////////////////////////////////////////////////// @@ -226,7 +247,8 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNot if (![RNNotificationsBridgeQueue sharedInstance].jsIsReady) { - [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = notification.request.content.userInfo; + // TODO: schedule emit foreground/background + // [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = notification.request.content.userInfo; return; } @@ -241,6 +263,26 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNot - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { NSString* identifier = response.actionIdentifier; + NSDictionary* userInfo = response.notification.request.content.userInfo; + if ([identifier isEqualToString:UNNotificationDefaultActionIdentifier]) { + if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self checkAndSendEvent:RNNotificationOpened body:userInfo]; + }); + } else { + [[RNNotificationsBridgeQueue sharedInstance] postNotification:userInfo]; + } + + completionHandler(); + return; + } + +// TODO: emit this +// if ([identifier isEqualToString:UNNotificationDismissActionIdentifier]) { +// [self checkAndSendEvent:RNNotificationDismissed body:userInfo]; +// return; +// } + NSString* completionKey = [NSString stringWithFormat:@"%@.%@", identifier, [NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970]]]; NSMutableDictionary* info = [[NSMutableDictionary alloc] initWithDictionary:@{ @"identifier": identifier, @"completionKey": completionKey }]; @@ -250,8 +292,6 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNoti info[@"text"] = text; } - NSDictionary* userInfo = response.notification.request.content.userInfo; - // add notification custom data if (userInfo != NULL) { info[@"notification"] = userInfo; @@ -276,11 +316,17 @@ - (void)application:(UIApplication *)application didRegisterForRemoteNotificatio } - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error +{ + [self sendFailedToRegisterEvent:error]; +} + +- (void)sendFailedToRegisterEvent:(NSError *)error { [self checkAndSendEvent:RNNotificationsRegistrationFailed body:@{@"code": [NSNumber numberWithInteger:error.code], @"domain": error.domain, @"localizedDescription": error.localizedDescription}]; } + //the system calls this method when your app is running in the foreground or background - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { @@ -300,12 +346,15 @@ -(void) handleReceiveNotification:(UIApplicationState)state userInfo:(NSDictiona { case (int)UIApplicationStateActive: [self checkAndSendEvent:RNNotificationReceivedForeground body:userInfo]; + break; case (int)UIApplicationStateInactive: [self checkAndSendEvent:RNNotificationOpened body:userInfo]; + break; default: [self checkAndSendEvent:RNNotificationReceivedBackground body:userInfo]; + break; } } @@ -351,18 +400,12 @@ - (void)startObserving #pragma mark exported method //////////////////////////////////////////////////////////////// -RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json) +RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSDictionary *)json) { - NSMutableSet* categories = nil; - if ([json count] > 0) - { - categories = [NSMutableSet new]; - for (NSDictionary* dic in json) - { - [categories addObject:[RCTConvert UNNotificationCategory:dic]]; - } - } - [RNNotifications requestPermissionsWithCategories:categories]; + NSArray *catsArr = [RCTConvert NSArray:json[@"categories"]]; + BOOL requestRemote = [RCTConvert BOOL:json[@"remote"]]; + NSSet* categories = [RNNotifications interpretNotificationCategories:catsArr]; + [self requestPermissionsWithCategoriesInternal:categories requestRemote:requestRemote]; } RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification withId:(NSString *)notificationId) @@ -433,6 +476,21 @@ + (void)didNotificationOpen:(NSDictionary *)notification userInfo:notification]; } ++ (NSSet*) interpretNotificationCategories: (NSArray*)json { + NSMutableSet* categories = nil; + if ([json count] > 0) + { + categories = [NSMutableSet new]; + for (NSDictionary* dic in json) + { + [categories addObject:[RCTConvert UNNotificationCategory:dic]]; + } + } + + // return immutable set + return [categories copy]; +} + /* * Helper methods */ diff --git a/index.ios.js b/index.ios.js index 8fe1dd9db..487acdda7 100644 --- a/index.ios.js +++ b/index.ios.js @@ -120,7 +120,13 @@ export default class NotificationsIOS { /** * Sets the notification categories */ - static requestPermissions(categories: Array) { + static requestPermissions(opts) { + // backwards compat + if (Array.isArray(opts)) { + opts = { categories: opts } + } + + const { categories, remote=true } = opts let notificationCategories = []; if (categories) { @@ -139,7 +145,10 @@ export default class NotificationsIOS { }); } - RNNotifications.requestPermissionsWithCategories(notificationCategories); + RNNotifications.requestPermissionsWithCategories({ + categories: notificationCategories, + remote + }); } /** diff --git a/package.json b/package.json index 6f133d88e..b17fc4330 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "react-native-notifications", - "version": "1.3.0", + "name": "@exodus/react-native-notifications", + "version": "1.4.2", "description": "Advanced Push Notifications (Silent, interactive notifications) for iOS & Android", "author": "Lidan Hifi ", "license": "MIT", @@ -50,11 +50,11 @@ }, "repository": { "type": "git", - "url": "https://github.com/wix/react-native-notifications.git" + "url": "https://github.com/ExodusMovement/react-native-notifications.git" }, - "homepage": "https://github.com/wix/react-native-notifications", + "homepage": "https://github.com/ExodusMovement/react-native-notifications", "bugs": { - "url": "https://github.com/wix/react-native-notifications/issues" + "url": "https://github.com/ExodusMovement/react-native-notifications/issues" }, "babel": { "presets": [