diff --git a/RNNotifications/RCTConvert+RNNotifications.m b/RNNotifications/RCTConvert+RNNotifications.m index 3c5f7c9d42bf09a0b31bfbaceb7d8dbe795a556c..92609ec98b9cf4eb08371fa89fa349d7dbe9d1bb 100644 --- a/RNNotifications/RCTConvert+RNNotifications.m +++ b/RNNotifications/RCTConvert+RNNotifications.m @@ -1,13 +1,5 @@ #import "RCTConvert+RNNotifications.h" - -@implementation RCTConvert (UIUserNotificationActivationMode) -RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{ - @"foreground": @(UIUserNotificationActivationModeForeground), - @"background": @(UIUserNotificationActivationModeBackground) - }), UIUserNotificationActivationModeForeground, integerValue) -@end - @implementation RCTConvert (UNNotificationActionOptions) + (UNNotificationActionOptions)UNUserNotificationActionOptions:(id)json { diff --git a/RNNotifications/RNBridgeModule.m b/RNNotifications/RNBridgeModule.m index 04e357cdc5c12522ef57244e65088db8b4a96e5e..804d3cf1cc5d130f917fdc5dea0aa255872a77c8 100644 --- a/RNNotifications/RNBridgeModule.m +++ b/RNNotifications/RNBridgeModule.m @@ -32,8 +32,12 @@ RCT_EXPORT_MODULE(); #pragma mark - JS interface -RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json) { - [_commandsHandler requestPermissionsWithCategories:json]; +RCT_EXPORT_METHOD(requestPermissions) { + [_commandsHandler requestPermissions]; +} + +RCT_EXPORT_METHOD(setCategories:(NSArray *)categories) { + [_commandsHandler setCategories:categories]; } RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { diff --git a/RNNotifications/RNCommandsHandler.h b/RNNotifications/RNCommandsHandler.h index b8bf9b4165cdc8b50ab796cf9aad6fdc15156e6d..a55b795da2070e6e91b322cf09f6e1e1ef780114 100644 --- a/RNNotifications/RNCommandsHandler.h +++ b/RNNotifications/RNCommandsHandler.h @@ -5,7 +5,9 @@ - (instancetype)init; -- (void)requestPermissionsWithCategories:(NSArray *)json; +- (void)requestPermissions; + +- (void)setCategories:(NSArray *)categories; - (void)getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject; diff --git a/RNNotifications/RNCommandsHandler.m b/RNNotifications/RNCommandsHandler.m index 4daf611b36f032e60e91f3d56bc81b2f08704dce..e0e3061c6a95870208e195bd5ad2d21fe37b98e5 100644 --- a/RNNotifications/RNCommandsHandler.m +++ b/RNNotifications/RNCommandsHandler.m @@ -13,8 +13,12 @@ return self; } -- (void)requestPermissionsWithCategories:(NSArray *)json { - [_notificationCenter requestPermissionsWithCategories:json]; +- (void)requestPermissions { + [_notificationCenter requestPermissions]; +} + +- (void)setCategories:(NSArray *)categories { + [_notificationCenter setCategories:categories]; } - (void)getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { diff --git a/RNNotifications/RNNotificationCenter.h b/RNNotifications/RNNotificationCenter.h index b9f461caeff5a1ddc0a2c61eaee40e18659c2eda..df33f6cd150675d54702c9f9bb28fadbdc817d46 100644 --- a/RNNotifications/RNNotificationCenter.h +++ b/RNNotifications/RNNotificationCenter.h @@ -10,7 +10,9 @@ typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError - (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve; -- (void)requestPermissionsWithCategories:(NSArray *)json; +- (void)requestPermissions; + +- (void)setCategories:(NSArray *)json; - (void)checkPermissions:(RCTPromiseResolveBlock)resolve; diff --git a/RNNotifications/RNNotificationCenter.m b/RNNotifications/RNNotificationCenter.m index 0741825ad5ebc0ba30132ad5e40127b05736f350..92ef14cf8b8e745101a7eee3a1e6031f522b7ec6 100644 --- a/RNNotifications/RNNotificationCenter.m +++ b/RNNotifications/RNNotificationCenter.m @@ -3,16 +3,7 @@ @implementation RNNotificationCenter -- (void)requestPermissionsWithCategories:(NSArray *)json { - NSMutableSet* categories = nil; - - if ([json count] > 0) { - categories = [NSMutableSet new]; - for (NSDictionary* categoryJson in json) { - [categories addObject:[RCTConvert UNMutableUserNotificationCategory:categoryJson]]; - } - } - [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categories]; +- (void)requestPermissions { UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert); [UNUserNotificationCenter.currentNotificationCenter requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) { if (!error && granted) { @@ -27,6 +18,18 @@ }]; } +- (void)setCategories:(NSArray *)json { + NSMutableSet* categories = nil; + + if ([json count] > 0) { + categories = [NSMutableSet new]; + for (NSDictionary* categoryJson in json) { + [categories addObject:[RCTConvert UNMutableUserNotificationCategory:categoryJson]]; + } + } + [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categories]; +} + - (void)sendLocalNotification:(NSDictionary *)notification withId:(NSString *)notificationId { UNNotificationRequest* localNotification = [RCTConvert UNNotificationRequest:notification withId:notificationId]; [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:localNotification withCompletionHandler:nil]; diff --git a/RNNotifications/RNNotificationsTests/Integration/RNCommandsHandlerIntegrationTest.m b/RNNotifications/RNNotificationsTests/Integration/RNCommandsHandlerIntegrationTest.m index 39220dca6eb1f3aa9725c2157ff3e1e6d9137bc1..e2760b412b5d5c38cf62948e84d6d8e5307308c2 100644 --- a/RNNotifications/RNNotificationsTests/Integration/RNCommandsHandlerIntegrationTest.m +++ b/RNNotifications/RNNotificationsTests/Integration/RNCommandsHandlerIntegrationTest.m @@ -25,33 +25,43 @@ _notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; } -- (void)testRequestPermissionsWithCategories_userAuthorizedPermissions { - NSArray* json = @[@{@"identifier": @"identifier"}]; +- (void)testRequestPermissions_userAuthorizedPermissions { UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert); UNNotificationSettings* settings = [UNNotificationSettings new]; [settings setValue:@(UNAuthorizationStatusAuthorized) forKey:@"authorizationStatus"]; - [[_notificationCenter expect] setNotificationCategories:[OCMArg any]]; [[_notificationCenter expect] requestAuthorizationWithOptions:authOptions completionHandler:[OCMArg invokeBlockWithArgs:@(YES), [NSNull null], nil]]; [[_notificationCenter expect] getNotificationSettingsWithCompletionHandler:[OCMArg invokeBlockWithArgs:settings, nil]]; [[(id)[UIApplication sharedApplication] expect] registerForRemoteNotifications]; - [_uut requestPermissionsWithCategories:json]; + [_uut requestPermissions]; [_notificationCenter verify]; } -- (void)testRequestPermissionsWithCategories_userDeniedPermissions { - NSArray* json = @[@{@"identifier": @"identifier"}]; +- (void)testRequestPermissions_userDeniedPermissions { UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert); UNNotificationSettings* settings = [UNNotificationSettings new]; [settings setValue:@(UNAuthorizationStatusDenied) forKey:@"authorizationStatus"]; - [[_notificationCenter expect] setNotificationCategories:[OCMArg any]]; [[_notificationCenter expect] requestAuthorizationWithOptions:authOptions completionHandler:[OCMArg invokeBlockWithArgs:@(YES), [NSNull null], nil]]; [[_notificationCenter expect] getNotificationSettingsWithCompletionHandler:[OCMArg invokeBlockWithArgs:settings, nil]]; [[(id)[UIApplication sharedApplication] reject] registerForRemoteNotifications]; - [_uut requestPermissionsWithCategories:json]; + [_uut requestPermissions]; + [_notificationCenter verify]; +} + +- (void)testSetCategories_shouldSetCategories { + NSArray* json = @[@{@"identifier": @"categoryId", @"actions": @[@{@"identifier" : @"actionId", @"activationMode": @"foreground"}]}]; + [[_notificationCenter expect] setNotificationCategories:[OCMArg checkWithBlock:^BOOL(NSMutableSet* categories) { + UNNotificationCategory* category = categories.allObjects.firstObject; + UNNotificationAction* action = category.actions.firstObject; + return ([category.identifier isEqualToString:@"categoryId"] && + [action.identifier isEqualToString:@"actionId"] && + action.options == UNNotificationActionOptionForeground); + }]]; + + [_uut setCategories:json]; [_notificationCenter verify]; } diff --git a/lib/src/Notifications.ts b/lib/src/Notifications.ts index 6400ee85206fc1bfb8487c609c52ae940a4ea6ae..68b00e0ca2d98a85cee87efdf9c17cfe86941d8a 100644 --- a/lib/src/Notifications.ts +++ b/lib/src/Notifications.ts @@ -2,7 +2,7 @@ import { NativeCommandsSender } from './adapters/NativeCommandsSender'; import { NativeEventsReceiver } from './adapters/NativeEventsReceiver'; import { Commands } from './commands/Commands'; import { EventsRegistry } from './events/EventsRegistry'; -import { Notification } from './interfaces/Notification'; +import { Notification, NotificationCategory } from './interfaces/Notification'; export class NotificationsRoot { private readonly nativeEventsReceiver: NativeEventsReceiver; @@ -22,14 +22,21 @@ export class NotificationsRoot { /** * Request permissions to send remote notifications - iOS only */ - public requestPermissions(): Promise { + public requestPermissions() { return this.commands.requestPermissions(); } + /** + * registerPushKit + */ + public registerPushKit() { + return this.commands.registerPushKit(); + } + /** * Reset the app to a new layout */ - public localNotification(notification: Notification): Promise { + public localNotification(notification: Notification) { return this.commands.sendLocalNotification(notification); } @@ -40,6 +47,71 @@ export class NotificationsRoot { return this.commands.getInitialNotification(); } + /** + * setCategories + */ + public setCategories(categories: [NotificationCategory?]) { + this.commands.setCategories(categories); + } + + /** + * getBadgesCount + */ + public getBadgeCount(): Promise { + return this.commands.getBadgeCount(); + } + + /** + * setBadgeCount + * @param count number of the new badge count + */ + public setBadgeCount(count: number) { + return this.commands.setBadgeCount(count); + } + + /** + * cancelLocalNotification + */ + public cancelLocalNotification(notificationId: string) { + return this.commands.cancelLocalNotification(notificationId); + } + + /** + * cancelAllLocalNotifications + */ + public cancelAllLocalNotifications() { + this.commands.cancelAllLocalNotifications(); + } + + /** + * isRegisteredForRemoteNotifications + */ + public isRegisteredForRemoteNotifications(): Promise { + this.commands.isRegisteredForRemoteNotifications(); + } + + /** + * checkPermissions + */ + public checkPermissions() { + return this.commands.checkPermissions(); + } + + /** + * removeAllDeliveredNotifications + */ + public removeAllDeliveredNotifications() { + return this.commands.removeAllDeliveredNotifications(); + } + + /** + * removeDeliveredNotifications + * @param identifiers Array of notification identifiers + */ + public removeDeliveredNotifications(identifiers: Array) { + return this.commands.removeDeliveredNotifications(identifiers); + } + /** * Obtain the events registry instance */ diff --git a/lib/src/adapters/NativeCommandsSender.ts b/lib/src/adapters/NativeCommandsSender.ts index 14001530a21cc67a03647b695714d9b07a08ca61..1b9b9169336245c961f965311e92300aadf40654 100644 --- a/lib/src/adapters/NativeCommandsSender.ts +++ b/lib/src/adapters/NativeCommandsSender.ts @@ -1,11 +1,21 @@ import { NativeModules } from 'react-native'; -import { Notification } from '../interfaces/Notification'; +import { Notification, NotificationCategory, NotificationPermissions } from '../interfaces/Notification'; interface NativeCommandsModule { - getInitialNotification(): Promise; - localNotification(notification: Notification, id: string): Promise; - requestPermissionsWithCategories(categories: any): Promise; - abandonPermissions(): Promise; + getInitialNotification(): Promise; + localNotification(notification: Notification, id: string): void; + requestPermissions(): void; + abandonPermissions(): void; + registerPushKit(): void; + getBadgeCount(): Promise; + setBadgeCount(count: number): void; + cancelLocalNotification(notificationId: string): void; + cancelAllLocalNotifications(): void; + isRegisteredForRemoteNotifications(): Promise; + checkPermissions(): Promise; + removeDeliveredNotifications(identifiers: Array): void; + removeAllDeliveredNotifications(): void; + setCategories(categories: [NotificationCategory?]): void; } export class NativeCommandsSender { @@ -23,10 +33,50 @@ export class NativeCommandsSender { } requestPermissions() { - return this.nativeCommandsModule.requestPermissionsWithCategories([]); + return this.nativeCommandsModule.requestPermissions(); } abandonPermissions() { return this.nativeCommandsModule.abandonPermissions(); } + + registerPushKit() { + return this.nativeCommandsModule.registerPushKit(); + } + + setCategories(categories: [NotificationCategory?]) { + this.nativeCommandsModule.setCategories(categories); + } + + getBadgeCount(): Promise { + return this.nativeCommandsModule.getBadgeCount(); + } + + setBadgeCount(count: number) { + this.nativeCommandsModule.setBadgeCount(count); + } + + cancelLocalNotification(notificationId: string) { + this.nativeCommandsModule.cancelLocalNotification(notificationId); + } + + cancelAllLocalNotifications() { + this.nativeCommandsModule.cancelAllLocalNotifications(); + } + + isRegisteredForRemoteNotifications(): Promise { + return this.nativeCommandsModule.isRegisteredForRemoteNotifications(); + } + + checkPermissions() { + return this.nativeCommandsModule.checkPermissions(); + } + + removeAllDeliveredNotifications() { + return this.nativeCommandsModule.removeAllDeliveredNotifications(); + } + + removeDeliveredNotifications(identifiers: Array) { + return this.nativeCommandsModule.removeDeliveredNotifications(identifiers); + } } diff --git a/lib/src/commands/Commands.test.ts b/lib/src/commands/Commands.test.ts index 11995b6cf8f1651d4d4ae3d1becfda00ed62ab59..f1da2659955fd3bc6866e29088d35b48162813ca 100644 --- a/lib/src/commands/Commands.test.ts +++ b/lib/src/commands/Commands.test.ts @@ -3,7 +3,7 @@ import { mock, verify, instance, deepEqual, when, anything, anyString } from 'ts import { Commands } from './Commands'; import { NativeCommandsSender } from '../adapters/NativeCommandsSender'; -import { Notification } from '../interfaces/Notification'; +import { Notification, NotificationCategory, NotificationPermissions } from '../interfaces/Notification'; describe('Commands', () => { let uut: Commands; @@ -18,7 +18,7 @@ describe('Commands', () => { }); describe('getInitialNotification', () => { - it('sends getInitialNotification to native', () => { + it('sends to native', () => { uut.getInitialNotification(); verify(mockedNativeCommandsSender.getInitialNotification()).called(); }); @@ -33,24 +33,129 @@ describe('Commands', () => { }); describe('requestPermissions', () => { - it('sends requestPermissions to native', () => { + it('sends to native', () => { uut.requestPermissions(); verify(mockedNativeCommandsSender.requestPermissions()).called(); }); }); + describe('registerPushKit', () => { + it('sends to native', () => { + uut.registerPushKit(); + verify(mockedNativeCommandsSender.registerPushKit()).called(); + }); + }); + + describe('setCategories', () => { + it('sends to native', () => { + const emptyCategoriesArray: [NotificationCategory?] = []; + uut.setCategories(emptyCategoriesArray); + verify(mockedNativeCommandsSender.setCategories(emptyCategoriesArray)).called(); + }); + + it('sends to native with categories', () => { + const category: NotificationCategory = {identifier: 'id', actions: []}; + const categoriesArray: [NotificationCategory] = [category]; + uut.setCategories(categoriesArray); + verify(mockedNativeCommandsSender.setCategories(categoriesArray)).called(); + }); + }); + describe('abandonPermissions', () => { - it('sends abandonPermissions to native', () => { + it('sends to native', () => { uut.abandonPermissions(); verify(mockedNativeCommandsSender.abandonPermissions()).called(); }); }); describe('sendLocalNotification', () => { - it('sends sendLocalNotification to native', () => { + it('sends to native', () => { const notification: Notification = {data: {}, alert: 'alert'}; uut.sendLocalNotification(notification); verify(mockedNativeCommandsSender.sendLocalNotification(notification, 'id')).called(); }); }); + + describe('getBadgeCount', () => { + it('sends to native', () => { + uut.getBadgeCount(); + verify(mockedNativeCommandsSender.getBadgeCount()).called(); + }); + }); + + describe('setBadgeCount', () => { + it('sends to native', () => { + uut.setBadgeCount(10); + verify(mockedNativeCommandsSender.setBadgeCount(10)).called(); + }); + }); + + describe('cancelLocalNotification', () => { + it('sends to native', () => { + uut.cancelLocalNotification("notificationId"); + verify(mockedNativeCommandsSender.cancelLocalNotification("notificationId")).called(); + }); + }); + + describe('cancelAllLocalNotifications', () => { + it('sends to native', () => { + uut.cancelAllLocalNotifications(); + verify(mockedNativeCommandsSender.cancelAllLocalNotifications()).called(); + }); + }); + + describe('isRegisteredForRemoteNotifications', () => { + it('sends to native', () => { + uut.isRegisteredForRemoteNotifications(); + verify(mockedNativeCommandsSender.isRegisteredForRemoteNotifications()).called(); + }); + + it('return positive response from native', async () => { + when(mockedNativeCommandsSender.isRegisteredForRemoteNotifications()).thenResolve( + true + ); + const isRegistered = await uut.isRegisteredForRemoteNotifications(); + verify(mockedNativeCommandsSender.isRegisteredForRemoteNotifications()).called(); + expect(isRegistered).toEqual(true); + }); + + it('return negative response from native', async () => { + when(mockedNativeCommandsSender.isRegisteredForRemoteNotifications()).thenResolve( + false + ); + const isRegistered = await uut.isRegisteredForRemoteNotifications(); + expect(isRegistered).toEqual(false); + }); + }); + + describe('checkPermissions', () => { + it('sends to native', () => { + uut.checkPermissions(); + verify(mockedNativeCommandsSender.checkPermissions()).called(); + }); + + it('return negative response from native', async () => { + const expectedPermissions: NotificationPermissions = {badge: false, alert: true, sound: false}; + when(mockedNativeCommandsSender.checkPermissions()).thenResolve( + expectedPermissions + ); + const permissions = await uut.checkPermissions(); + expect(permissions).toEqual(expectedPermissions); + }); + }); + + describe('removeAllDeliveredNotifications', () => { + it('sends to native', () => { + uut.removeAllDeliveredNotifications(); + verify(mockedNativeCommandsSender.removeAllDeliveredNotifications()).called(); + }); + }); + + describe('removeDeliveredNotifications', async () => { + it('sends to native', () => { + const identifiers: Array = ["id1", "id2"]; + uut.removeDeliveredNotifications(identifiers); + verify(mockedNativeCommandsSender.removeDeliveredNotifications(identifiers)).called(); + }); + }); }); diff --git a/lib/src/commands/Commands.ts b/lib/src/commands/Commands.ts index cdb545fbda478a0993090b5bef35d19761df5419..882a5e986998488446d1932a787a83aa09e2bd7f 100644 --- a/lib/src/commands/Commands.ts +++ b/lib/src/commands/Commands.ts @@ -1,6 +1,6 @@ import * as _ from 'lodash'; import { NativeCommandsSender } from '../adapters/NativeCommandsSender'; -import { Notification } from '../interfaces/Notification'; +import { Notification, NotificationCategory, NotificationPermissions } from '../interfaces/Notification'; export class Commands { constructor( @@ -27,4 +27,44 @@ export class Commands { const result = this.nativeCommandsSender.abandonPermissions(); return result; } + + public registerPushKit() { + this.nativeCommandsSender.registerPushKit(); + } + + public setCategories(categories: [NotificationCategory?]) { + this.nativeCommandsSender.setCategories(categories); + } + + public getBadgeCount(): Promise { + return this.nativeCommandsSender.getBadgeCount(); + } + + public setBadgeCount(count: number) { + this.nativeCommandsSender.setBadgeCount(count); + } + + public cancelLocalNotification(notificationId: string) { + this.nativeCommandsSender.cancelLocalNotification(notificationId); + } + + public cancelAllLocalNotifications() { + this.nativeCommandsSender.cancelAllLocalNotifications(); + } + + public isRegisteredForRemoteNotifications(): Promise { + return this.nativeCommandsSender.isRegisteredForRemoteNotifications(); + } + + public checkPermissions(): Promise { + return this.nativeCommandsSender.checkPermissions(); + } + + public removeAllDeliveredNotifications() { + this.nativeCommandsSender.removeAllDeliveredNotifications(); + } + + public removeDeliveredNotifications(identifiers: Array) { + return this.nativeCommandsSender.removeDeliveredNotifications(identifiers); + } } diff --git a/lib/src/interfaces/Notification.ts b/lib/src/interfaces/Notification.ts index e24c46b1d6ca6352246469894bf3a21a8d1852ff..a564909092d14483061a167b2593a7be2f9febe9 100644 --- a/lib/src/interfaces/Notification.ts +++ b/lib/src/interfaces/Notification.ts @@ -6,3 +6,28 @@ export interface Notification { type?: string; thread?: string; } + +export interface NotificationPermissions { + badge: boolean; + alert: boolean; + sound: boolean; +} + +export interface NotificationCategory { + identifier: string + actions: [NotificationAction?]; +} + + +export interface NotificationTextInput { + buttonTitle: string; + placeholder: string; +} + +export interface NotificationAction { + identifier: string; + activationMode: 'foreground' | 'authenticationRequired' | 'destructive'; + title: string; + authenticationRequired: boolean; + textInput: NotificationTextInput +}