From a15e038a6f51f49d473d91fb69ffe2fda7d76652 Mon Sep 17 00:00:00 2001 From: Gustavo Perdomo Date: Wed, 22 Mar 2017 15:49:36 -0300 Subject: [PATCH] Initial support of UNUserNotification --- README.md | 22 ++++++++++++ RNNotifications/RNNotifications.m | 59 +++++++++++++++++++++++++++++++ index.ios.js | 35 ++++++++++++++++++ notification.ios.js | 7 ++++ test/index.ios.spec.js | 45 +++++++++++++++++++++-- test/notification.ios.spec.js | 12 +++++-- 6 files changed, 175 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 60bb517..bc21c16 100644 --- a/README.md +++ b/README.md @@ -431,6 +431,28 @@ Now the server should push the notification a bit differently- background instea --- +## Remove notifications (iOS only) + +### getDeliveredNotifications + +`PushNotification.getDeliveredNotifications(callback: (notifications: [Object]) => void)` + +Provides you with a list of the app’s notifications that are still displayed in Notification Center. + +### removeDeliveredNotifications + +`PushNotification.removeDeliveredNotifications(identifiers: [string])` + +Removes the specified notifications from Notification Center. + +### removeAllDeliveredNotifications + +`PushNotification.removeAllDeliveredNotifications()` + +Removes all delivered notifications from Notification Center. + +--- + ## PushKit API (iOS only) The PushKit framework provides the classes for your iOS apps to receive background pushes from remote servers. it has better support for background notifications compared to regular push notifications with `content-available: 1`. More info in [iOS PushKit documentation](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Reference/PushKit_Framework/). diff --git a/RNNotifications/RNNotifications.m b/RNNotifications/RNNotifications.m index 8c7959d..ff231ef 100644 --- a/RNNotifications/RNNotifications.m +++ b/RNNotifications/RNNotifications.m @@ -15,6 +15,7 @@ #import "RCTUtils.h" #endif #import "RNNotificationsBridgeQueue.h" +#import NSString* const RNNotificationCreateAction = @"CREATE"; NSString* const RNNotificationClearAction = @"CLEAR"; @@ -107,6 +108,29 @@ RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{ } @end +static NSDictionary *RCTFormatUNNotification(UNNotification *notification) +{ + NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary]; + UNNotificationContent *content = notification.request.content; + + formattedNotification[@"identifier"] = notification.request.identifier; + + if (notification.date) { + NSDateFormatter *formatter = [NSDateFormatter new]; + [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; + NSString *dateString = [formatter stringFromDate:notification.date]; + formattedNotification[@"fireDate"] = dateString; + } + + formattedNotification[@"alertTitle"] = RCTNullIfNil(content.title); + formattedNotification[@"alertBody"] = RCTNullIfNil(content.body); + formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier); + formattedNotification[@"thread-id"] = RCTNullIfNil(content.threadIdentifier); + formattedNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(content.userInfo)); + + return formattedNotification; +} + @implementation RNNotifications RCT_EXPORT_MODULE() @@ -542,4 +566,39 @@ RCT_EXPORT_METHOD(cancelAllLocalNotifications) [RCTSharedApplication() cancelAllLocalNotifications]; } +#if !TARGET_OS_TV + +RCT_EXPORT_METHOD(removeAllDeliveredNotifications) +{ + if ([UNUserNotificationCenter class]) { + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + [center removeAllDeliveredNotifications]; + } +} + +RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray *)identifiers) +{ + if ([UNUserNotificationCenter class]) { + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + [center removeDeliveredNotificationsWithIdentifiers:identifiers]; + } +} + +RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback) +{ + if ([UNUserNotificationCenter class]) { + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + [center getDeliveredNotificationsWithCompletionHandler:^(NSArray * _Nonnull notifications) { + NSMutableArray *formattedNotifications = [NSMutableArray new]; + + for (UNNotification *notification in notifications) { + [formattedNotifications addObject:RCTFormatUNNotification(notification)]; + } + callback(@[formattedNotifications]); + }]; + } +} + +#endif !TARGET_OS_TV + @end diff --git a/index.ios.js b/index.ios.js index 6dd788f..566f48e 100644 --- a/index.ios.js +++ b/index.ios.js @@ -199,4 +199,39 @@ export default class NotificationsIOS { static cancelAllLocalNotifications() { NativeRNNotifications.cancelAllLocalNotifications(); } + + /** + * Remove all delivered notifications from Notification Center + */ + static removeAllDeliveredNotifications() { + return NativeRNNotifications.removeAllDeliveredNotifications(); + } + + /** + * Removes the specified notifications from Notification Center + * + * @param identifiers Array of notification identifiers + */ + static removeDeliveredNotifications(identifiers: [string]) { + return NativeRNNotifications.removeDeliveredNotifications(identifiers); + } + + /** + * Provides you with a list of the app’s notifications that are still displayed in Notification Center + * + * @param callback Function which receive an array of delivered notifications + * + * A delivered notification is an object containing: + * + * - `identifier` : The identifier of this notification. + * - `alertBody` : The message displayed in the notification alert. + * - `alertTitle` : The message title displayed in the notification. + * - `category` : The category of this notification, if has one. + * - `userInfo` : An optional object containing additional notification data. + * - `thread-id` : The thread identifier of this notification, if has one. + * - `fireDate` : The date and time when the system should deliver the notification. if not specified, the notification will be dispatched immediately. + */ + static getDeliveredNotifications(callback: (notifications: [Object]) => void) { + return NativeRNNotifications.getDeliveredNotifications(callback); + } } diff --git a/notification.ios.js b/notification.ios.js index 016d4e2..4042a96 100644 --- a/notification.ios.js +++ b/notification.ios.js @@ -5,6 +5,7 @@ export default class IOSNotification { _badge: number; _category: string; _type: string; // regular / managed + _thread: string; constructor(notification: Object) { this._data = {}; @@ -21,6 +22,7 @@ export default class IOSNotification { this._badge = notification.aps.badge; this._category = notification.managedAps.category; this._type = "managed"; + this._thread = notification.aps["thread-id"]; } else if ( notification.aps && notification.aps.alert) { @@ -30,6 +32,7 @@ export default class IOSNotification { this._badge = notification.aps.badge; this._category = notification.aps.category; this._type = "regular"; + this._thread = notification.aps["thread-id"]; } Object.keys(notification).filter(key => key !== "aps").forEach(key => { @@ -60,4 +63,8 @@ export default class IOSNotification { getType(): ?string { return this._type; } + + getThread(): ?string { + return this._thread; + } } diff --git a/test/index.ios.spec.js b/test/index.ios.spec.js index 2ba23fb..4bfc32f 100644 --- a/test/index.ios.spec.js +++ b/test/index.ios.spec.js @@ -27,10 +27,14 @@ describe("NotificationsIOS", () => { nativeConsumeBackgroundQueue, nativeLocalNotification, nativeCancelLocalNotification, - nativeCancelAllLocalNotifications; + nativeCancelAllLocalNotifications, + nativeRemoveAllDeliveredNotifications, + nativeRemoveDeliveredNotifications, + nativeGetDeliveredNotifications; let NotificationsIOS, NotificationAction, NotificationCategory; let someHandler = () => {}; let constantGuid = "some-random-uuid"; + let identifiers = ["some-random-uuid", "other-random-uuid"]; /*eslint-enable indent*/ before(() => { @@ -46,6 +50,9 @@ describe("NotificationsIOS", () => { nativeLocalNotification = sinon.spy(); nativeCancelLocalNotification = sinon.spy(); nativeCancelAllLocalNotifications = sinon.spy(); + nativeRemoveAllDeliveredNotifications = sinon.spy(); + nativeRemoveDeliveredNotifications = sinon.spy(); + nativeGetDeliveredNotifications = sinon.spy(); let libUnderTest = proxyquire("../index.ios", { "uuid": { @@ -61,7 +68,10 @@ describe("NotificationsIOS", () => { consumeBackgroundQueue: nativeConsumeBackgroundQueue, localNotification: nativeLocalNotification, cancelLocalNotification: nativeCancelLocalNotification, - cancelAllLocalNotifications: nativeCancelAllLocalNotifications + cancelAllLocalNotifications: nativeCancelAllLocalNotifications, + removeAllDeliveredNotifications: nativeRemoveAllDeliveredNotifications, + removeDeliveredNotifications: nativeRemoveDeliveredNotifications, + getDeliveredNotifications: nativeGetDeliveredNotifications } }, NativeAppEventEmitter: { @@ -100,6 +110,9 @@ describe("NotificationsIOS", () => { nativeLocalNotification.reset(); nativeCancelLocalNotification.reset(); nativeCancelAllLocalNotifications.reset(); + nativeRemoveAllDeliveredNotifications.reset(); + nativeRemoveDeliveredNotifications.reset(); + nativeGetDeliveredNotifications.reset(); }); after(() => { @@ -115,6 +128,9 @@ describe("NotificationsIOS", () => { nativeLocalNotification = null; nativeCancelLocalNotification = null; nativeCancelAllLocalNotifications = null; + nativeRemoveAllDeliveredNotifications = null; + nativeRemoveDeliveredNotifications = null; + nativeGetDeliveredNotifications = null; NotificationsIOS = null; NotificationAction = null; @@ -282,4 +298,29 @@ describe("NotificationsIOS", () => { expect(nativeCancelAllLocalNotifications).to.have.been.calledWith(); }); }); + + describe("Remove all delivered notifications", () => { + it("should call native remove all delivered notifications method", () => { + NotificationsIOS.removeAllDeliveredNotifications(); + + expect(nativeRemoveAllDeliveredNotifications).to.have.been.calledWith(); + }); + }); + + describe("Remove delivered notifications", () => { + it("should call native remove delivered notifications method", () => { + NotificationsIOS.removeDeliveredNotifications(identifiers); + + expect(nativeRemoveDeliveredNotifications).to.have.been.calledWith(identifiers); + }); + }); + + describe("Get delivered notifications", () => { + it("should call native get delivered notifications method", () => { + const callback = (notifications) => console.log(notifications); + NotificationsIOS.getDeliveredNotifications(callback); + + expect(nativeGetDeliveredNotifications).to.have.been.calledWith(callback); + }); + }); }); diff --git a/test/notification.ios.spec.js b/test/notification.ios.spec.js index 0620108..b19a901 100644 --- a/test/notification.ios.spec.js +++ b/test/notification.ios.spec.js @@ -4,7 +4,7 @@ import IOSNotification from "../notification.ios"; describe("iOS Notification Object", () => { let notification; - let someBadgeCount = 123, someSound = "someSound", someCategory = "some_notification_category"; + let someBadgeCount = 123, someSound = "someSound", someCategory = "some_notification_category", someThread = "thread-1"; describe("for a regular iOS push notification", () => { let regularNativeNotifications = [ @@ -17,7 +17,8 @@ describe("iOS Notification Object", () => { }, badge: someBadgeCount, sound: someSound, - category: someCategory + category: someCategory, + "thread-id": someThread }, key1: "value1", key2: "value2" @@ -33,7 +34,8 @@ describe("iOS Notification Object", () => { }, badge: someBadgeCount, sound: someSound, - category: someCategory + category: someCategory, + "thread-id": someThread }, key1: "value1", key2: "value2" @@ -65,6 +67,10 @@ describe("iOS Notification Object", () => { expect(notification.getCategory()).to.equal(someCategory); }); + it("should return the thread", () => { + expect(notification.getThread()).to.equal("thread-1"); + }); + it("should return the custom data", () => { expect(notification.getData()).to.deep.equal({ key1: "value1", key2: "value2" }); }); -- 2.26.2