From 6476026b865ab9f24575b230983da5851603642b Mon Sep 17 00:00:00 2001 From: Lidan Hifi Date: Sat, 23 Apr 2016 17:30:25 +0300 Subject: [PATCH] added local notifications support --- README.md | 2 +- RNNotifications/RNNotifications.m | 41 ++++++++++++++++++++++++++----- example/index.ios.js | 11 +++++++++ index.ios.js | 19 +++++++++++++- test/index.ios.spec.js | 30 +++++++++++++++++++--- 5 files changed, 92 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 8267de4..2d58689 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,4 @@ Handle push notifications for your app, including remote and local notifications **Work in progress, please notice that this library is not production-ready yet!** ## TODO -- Better support of local notifications. +- Android support. diff --git a/RNNotifications/RNNotifications.m b/RNNotifications/RNNotifications.m index 7f16b12..072b26d 100644 --- a/RNNotifications/RNNotifications.m +++ b/RNNotifications/RNNotifications.m @@ -48,7 +48,7 @@ RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{ { NSDictionary *details = [self NSDictionary:json]; - UIMutableUserNotificationAction* action =[[UIMutableUserNotificationAction alloc] init]; + UIMutableUserNotificationAction* action =[UIMutableUserNotificationAction new]; action.activationMode = [RCTConvert UIUserNotificationActivationMode:details[@"activationMode"]]; action.behavior = [RCTConvert UIUserNotificationActionBehavior:details[@"behavior"]]; action.authenticationRequired = [RCTConvert BOOL:details[@"authenticationRequired"]]; @@ -65,11 +65,11 @@ RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{ { NSDictionary *details = [self NSDictionary:json]; - UIMutableUserNotificationCategory* category = [[UIMutableUserNotificationCategory alloc] init]; + UIMutableUserNotificationCategory* category = [UIMutableUserNotificationCategory new]; category.identifier = details[@"identifier"]; // category actions - NSMutableArray* actions = [[NSMutableArray alloc] init]; + NSMutableArray* actions = [NSMutableArray new]; for (NSDictionary* actionJson in [RCTConvert NSArray:details[@"actions"]]) { [actions addObject:[RCTConvert UIMutableUserNotificationAction:actionJson]]; } @@ -80,6 +80,24 @@ RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{ } @end +@implementation RCTConvert (UILocalNotification) ++ (UILocalNotification *)UILocalNotification:(id)json +{ + NSDictionary *details = [self NSDictionary:json]; + + UILocalNotification* notification = [UILocalNotification new]; + notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]]; + notification.alertBody = [RCTConvert NSString:details[@"alertBody"]]; + notification.alertTitle = [RCTConvert NSString:details[@"alertTitle"]]; + notification.alertAction = [RCTConvert NSString:details[@"alertAction"]]; + notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName; + notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{}; + notification.category = [RCTConvert NSString:details[@"category"]]; + + return notification; +} +@end + @implementation RNNotifications RCT_EXPORT_MODULE() @@ -163,7 +181,9 @@ RCT_EXPORT_MODULE() { UIApplicationState state = [UIApplication sharedApplication].applicationState; - if (state == UIApplicationStateInactive) { + if (state == UIApplicationStateActive) { + [self didReceiveNotificationOnForegroundState:notification.userInfo]; + } else if (state == UIApplicationStateInactive) { NSString* notificationId = [notification.userInfo objectForKey:@"notificationId"]; if (notificationId) { [self clearNotificationFromNotificationsCenter:notificationId]; @@ -243,7 +263,7 @@ RCT_EXPORT_MODULE() && alert) { // trigger new client push notification - UILocalNotification* note = [[UILocalNotification alloc] init]; + UILocalNotification* note = [UILocalNotification new]; note.alertTitle = [alert objectForKey:@"title"]; note.alertBody = [alert objectForKey:@"body"]; note.userInfo = notification; @@ -393,7 +413,7 @@ RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json) NSMutableSet* categories = nil; if ([json count] > 0) { - categories = [[NSMutableSet alloc] init]; + categories = [NSMutableSet new]; for (NSDictionary* categoryJson in json) { [categories addObject:[RCTConvert UIMutableUserNotificationCategory:categoryJson]]; } @@ -445,4 +465,13 @@ RCT_EXPORT_METHOD(consumeBackgroundQueue) [RNNotificationsBridgeQueue sharedInstance].jsIsReady = YES; } +RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification) +{ + if ([notification objectForKey:@"fireDate"] != nil) { + [RCTSharedApplication() scheduleLocalNotification:[RCTConvert UILocalNotification:notification]]; + } else { + [RCTSharedApplication() presentLocalNotificationNow:[RCTConvert UILocalNotification:notification]]; + } +} + @end diff --git a/example/index.ios.js b/example/index.ios.js index 9abdb82..c36a503 100644 --- a/example/index.ios.js +++ b/example/index.ios.js @@ -58,6 +58,8 @@ class NotificationsExampleApp extends Component { NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); NotificationsIOS.addEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this)); NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened.bind(this)); + + } onPushRegistered(deviceToken) { @@ -75,6 +77,15 @@ class NotificationsExampleApp extends Component { onNotificationReceivedBackground(notification) { NotificationsIOS.log("Notification Received Background: " + JSON.stringify(notification)); + NotificationsIOS.localNotification({ + alertBody: "Received background notificiation!", + alertTitle: "Local Notification Title", + alertAction: "Click here to open", + soundName: "chime.aiff", + category: "SOME_CATEGORY", + userInfo: notification.getData() + }); + // NotificationsIOS.backgroundTimeRemaining(time => NotificationsIOS.log("remaining background time: " + time)); } diff --git a/index.ios.js b/index.ios.js index d9c8377..7f6d2f2 100644 --- a/index.ios.js +++ b/index.ios.js @@ -161,7 +161,24 @@ export default class NotificationsIOS { NativeRNNotifications.consumeBackgroundQueue(); } - static log(message) { + static log(message: string) { NativeRNNotifications.log(message); } + + /** + * Presenting local notification + * + * notification is an object containing: + * + * - `alertBody` : The message displayed in the notification alert. + * - `alertTitle` : The message title displayed in the notification. + * - `alertAction` : The "action" displayed beneath an actionable notification. Defaults to "view"; + * - `soundName` : The sound played when the notification is fired (optional). + * - `category` : The category of this notification, required for actionable notifications (optional). + * - `userInfo` : An optional object containing additional notification data. + * - `fireDate` : The date and time when the system should deliver the notification. if not specified, the notification will be dispatched immediately. + */ + static localNotification(notification: Object) { + NativeRNNotifications.localNotification(notification); + } } diff --git a/test/index.ios.spec.js b/test/index.ios.spec.js index 9b1cb54..db6654d 100644 --- a/test/index.ios.spec.js +++ b/test/index.ios.spec.js @@ -23,7 +23,8 @@ describe("NotificationsIOS", () => { nativeAbandonPermissions, nativeRegisterPushKit, nativeBackgroundTimeRemaining, - nativeConsumeBackgroundQueue; + nativeConsumeBackgroundQueue, + nativeLocalNotification; let NotificationsIOS, NotificationAction, NotificationCategory; let someHandler = () => {}; /*eslint-enable indent*/ @@ -38,6 +39,7 @@ describe("NotificationsIOS", () => { nativeRegisterPushKit = sinon.spy(); nativeBackgroundTimeRemaining = sinon.spy(); nativeConsumeBackgroundQueue = sinon.spy(); + nativeLocalNotification = sinon.spy(); let libUnderTest = proxyquire("../index.ios", { "react-native": { @@ -47,7 +49,8 @@ describe("NotificationsIOS", () => { abandonPermissions: nativeAbandonPermissions, registerPushKit: nativeRegisterPushKit, backgroundTimeRemaining: nativeBackgroundTimeRemaining, - consumeBackgroundQueue: nativeConsumeBackgroundQueue + consumeBackgroundQueue: nativeConsumeBackgroundQueue, + localNotification: nativeLocalNotification } }, NativeAppEventEmitter: { @@ -83,6 +86,7 @@ describe("NotificationsIOS", () => { nativeRegisterPushKit.reset(); nativeBackgroundTimeRemaining.reset(); nativeConsumeBackgroundQueue.reset(); + nativeLocalNotification.reset(); }); after(() => { @@ -95,6 +99,7 @@ describe("NotificationsIOS", () => { nativeRegisterPushKit = null; nativeBackgroundTimeRemaining = null; nativeConsumeBackgroundQueue = null; + nativeLocalNotification = null; NotificationsIOS = null; NotificationAction = null; @@ -216,11 +221,30 @@ describe("NotificationsIOS", () => { }); }); - describe("Get background remaining time", () => { + describe("Consume background queue which holds background notificiations and actions until js thread is ready", () => { it("should call native consume background queue method", () => { NotificationsIOS.consumeBackgroundQueue(); expect(nativeConsumeBackgroundQueue).to.have.been.called; }); }); + + describe("Get background remaining time", () => { + it("should call native consume background queue method", () => { + let someLocalNotification = { + alertBody: "some body", + alertTitle: "some title", + alertAction: "some action", + soundName: "sound", + category: "SOME_CATEGORY", + userInfo: { + "key": "value" + } + }; + + NotificationsIOS.localNotification(someLocalNotification); + + expect(nativeLocalNotification).to.have.been.calledWith(someLocalNotification); + }); + }); }); -- 2.26.2