Commit 6476026b authored by Lidan Hifi's avatar Lidan Hifi

added local notifications support

parent 46a443b8
...@@ -5,4 +5,4 @@ Handle push notifications for your app, including remote and local notifications ...@@ -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!** **Work in progress, please notice that this library is not production-ready yet!**
## TODO ## TODO
- Better support of local notifications. - Android support.
...@@ -48,7 +48,7 @@ RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{ ...@@ -48,7 +48,7 @@ RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{
{ {
NSDictionary<NSString *, id> *details = [self NSDictionary:json]; NSDictionary<NSString *, id> *details = [self NSDictionary:json];
UIMutableUserNotificationAction* action =[[UIMutableUserNotificationAction alloc] init]; UIMutableUserNotificationAction* action =[UIMutableUserNotificationAction new];
action.activationMode = [RCTConvert UIUserNotificationActivationMode:details[@"activationMode"]]; action.activationMode = [RCTConvert UIUserNotificationActivationMode:details[@"activationMode"]];
action.behavior = [RCTConvert UIUserNotificationActionBehavior:details[@"behavior"]]; action.behavior = [RCTConvert UIUserNotificationActionBehavior:details[@"behavior"]];
action.authenticationRequired = [RCTConvert BOOL:details[@"authenticationRequired"]]; action.authenticationRequired = [RCTConvert BOOL:details[@"authenticationRequired"]];
...@@ -65,11 +65,11 @@ RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{ ...@@ -65,11 +65,11 @@ RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{
{ {
NSDictionary<NSString *, id> *details = [self NSDictionary:json]; NSDictionary<NSString *, id> *details = [self NSDictionary:json];
UIMutableUserNotificationCategory* category = [[UIMutableUserNotificationCategory alloc] init]; UIMutableUserNotificationCategory* category = [UIMutableUserNotificationCategory new];
category.identifier = details[@"identifier"]; category.identifier = details[@"identifier"];
// category actions // category actions
NSMutableArray* actions = [[NSMutableArray alloc] init]; NSMutableArray* actions = [NSMutableArray new];
for (NSDictionary* actionJson in [RCTConvert NSArray:details[@"actions"]]) { for (NSDictionary* actionJson in [RCTConvert NSArray:details[@"actions"]]) {
[actions addObject:[RCTConvert UIMutableUserNotificationAction:actionJson]]; [actions addObject:[RCTConvert UIMutableUserNotificationAction:actionJson]];
} }
...@@ -80,6 +80,24 @@ RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{ ...@@ -80,6 +80,24 @@ RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{
} }
@end @end
@implementation RCTConvert (UILocalNotification)
+ (UILocalNotification *)UILocalNotification:(id)json
{
NSDictionary<NSString *, id> *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 @implementation RNNotifications
RCT_EXPORT_MODULE() RCT_EXPORT_MODULE()
...@@ -163,7 +181,9 @@ RCT_EXPORT_MODULE() ...@@ -163,7 +181,9 @@ RCT_EXPORT_MODULE()
{ {
UIApplicationState state = [UIApplication sharedApplication].applicationState; 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"]; NSString* notificationId = [notification.userInfo objectForKey:@"notificationId"];
if (notificationId) { if (notificationId) {
[self clearNotificationFromNotificationsCenter:notificationId]; [self clearNotificationFromNotificationsCenter:notificationId];
...@@ -243,7 +263,7 @@ RCT_EXPORT_MODULE() ...@@ -243,7 +263,7 @@ RCT_EXPORT_MODULE()
&& alert) { && alert) {
// trigger new client push notification // trigger new client push notification
UILocalNotification* note = [[UILocalNotification alloc] init]; UILocalNotification* note = [UILocalNotification new];
note.alertTitle = [alert objectForKey:@"title"]; note.alertTitle = [alert objectForKey:@"title"];
note.alertBody = [alert objectForKey:@"body"]; note.alertBody = [alert objectForKey:@"body"];
note.userInfo = notification; note.userInfo = notification;
...@@ -393,7 +413,7 @@ RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json) ...@@ -393,7 +413,7 @@ RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json)
NSMutableSet* categories = nil; NSMutableSet* categories = nil;
if ([json count] > 0) { if ([json count] > 0) {
categories = [[NSMutableSet alloc] init]; categories = [NSMutableSet new];
for (NSDictionary* categoryJson in json) { for (NSDictionary* categoryJson in json) {
[categories addObject:[RCTConvert UIMutableUserNotificationCategory:categoryJson]]; [categories addObject:[RCTConvert UIMutableUserNotificationCategory:categoryJson]];
} }
...@@ -445,4 +465,13 @@ RCT_EXPORT_METHOD(consumeBackgroundQueue) ...@@ -445,4 +465,13 @@ RCT_EXPORT_METHOD(consumeBackgroundQueue)
[RNNotificationsBridgeQueue sharedInstance].jsIsReady = YES; [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 @end
...@@ -58,6 +58,8 @@ class NotificationsExampleApp extends Component { ...@@ -58,6 +58,8 @@ class NotificationsExampleApp extends Component {
NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this));
NotificationsIOS.addEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this)); NotificationsIOS.addEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this));
NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened.bind(this)); NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened.bind(this));
} }
onPushRegistered(deviceToken) { onPushRegistered(deviceToken) {
...@@ -75,6 +77,15 @@ class NotificationsExampleApp extends Component { ...@@ -75,6 +77,15 @@ class NotificationsExampleApp extends Component {
onNotificationReceivedBackground(notification) { onNotificationReceivedBackground(notification) {
NotificationsIOS.log("Notification Received Background: " + JSON.stringify(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)); // NotificationsIOS.backgroundTimeRemaining(time => NotificationsIOS.log("remaining background time: " + time));
} }
......
...@@ -161,7 +161,24 @@ export default class NotificationsIOS { ...@@ -161,7 +161,24 @@ export default class NotificationsIOS {
NativeRNNotifications.consumeBackgroundQueue(); NativeRNNotifications.consumeBackgroundQueue();
} }
static log(message) { static log(message: string) {
NativeRNNotifications.log(message); 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);
}
} }
...@@ -23,7 +23,8 @@ describe("NotificationsIOS", () => { ...@@ -23,7 +23,8 @@ describe("NotificationsIOS", () => {
nativeAbandonPermissions, nativeAbandonPermissions,
nativeRegisterPushKit, nativeRegisterPushKit,
nativeBackgroundTimeRemaining, nativeBackgroundTimeRemaining,
nativeConsumeBackgroundQueue; nativeConsumeBackgroundQueue,
nativeLocalNotification;
let NotificationsIOS, NotificationAction, NotificationCategory; let NotificationsIOS, NotificationAction, NotificationCategory;
let someHandler = () => {}; let someHandler = () => {};
/*eslint-enable indent*/ /*eslint-enable indent*/
...@@ -38,6 +39,7 @@ describe("NotificationsIOS", () => { ...@@ -38,6 +39,7 @@ describe("NotificationsIOS", () => {
nativeRegisterPushKit = sinon.spy(); nativeRegisterPushKit = sinon.spy();
nativeBackgroundTimeRemaining = sinon.spy(); nativeBackgroundTimeRemaining = sinon.spy();
nativeConsumeBackgroundQueue = sinon.spy(); nativeConsumeBackgroundQueue = sinon.spy();
nativeLocalNotification = sinon.spy();
let libUnderTest = proxyquire("../index.ios", { let libUnderTest = proxyquire("../index.ios", {
"react-native": { "react-native": {
...@@ -47,7 +49,8 @@ describe("NotificationsIOS", () => { ...@@ -47,7 +49,8 @@ describe("NotificationsIOS", () => {
abandonPermissions: nativeAbandonPermissions, abandonPermissions: nativeAbandonPermissions,
registerPushKit: nativeRegisterPushKit, registerPushKit: nativeRegisterPushKit,
backgroundTimeRemaining: nativeBackgroundTimeRemaining, backgroundTimeRemaining: nativeBackgroundTimeRemaining,
consumeBackgroundQueue: nativeConsumeBackgroundQueue consumeBackgroundQueue: nativeConsumeBackgroundQueue,
localNotification: nativeLocalNotification
} }
}, },
NativeAppEventEmitter: { NativeAppEventEmitter: {
...@@ -83,6 +86,7 @@ describe("NotificationsIOS", () => { ...@@ -83,6 +86,7 @@ describe("NotificationsIOS", () => {
nativeRegisterPushKit.reset(); nativeRegisterPushKit.reset();
nativeBackgroundTimeRemaining.reset(); nativeBackgroundTimeRemaining.reset();
nativeConsumeBackgroundQueue.reset(); nativeConsumeBackgroundQueue.reset();
nativeLocalNotification.reset();
}); });
after(() => { after(() => {
...@@ -95,6 +99,7 @@ describe("NotificationsIOS", () => { ...@@ -95,6 +99,7 @@ describe("NotificationsIOS", () => {
nativeRegisterPushKit = null; nativeRegisterPushKit = null;
nativeBackgroundTimeRemaining = null; nativeBackgroundTimeRemaining = null;
nativeConsumeBackgroundQueue = null; nativeConsumeBackgroundQueue = null;
nativeLocalNotification = null;
NotificationsIOS = null; NotificationsIOS = null;
NotificationAction = null; NotificationAction = null;
...@@ -216,11 +221,30 @@ describe("NotificationsIOS", () => { ...@@ -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", () => { it("should call native consume background queue method", () => {
NotificationsIOS.consumeBackgroundQueue(); NotificationsIOS.consumeBackgroundQueue();
expect(nativeConsumeBackgroundQueue).to.have.been.called; 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);
});
});
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment