From 6c99242ec3500231ff1e991712ce81123319f479 Mon Sep 17 00:00:00 2001 From: yogevbd Date: Wed, 31 Jul 2019 16:09:27 +0300 Subject: [PATCH] Splits requestPermissionsWithCategories to two functions --- RNNotifications/RCTConvert+RNNotifications.m | 8 -- RNNotifications/RNBridgeModule.m | 8 +- RNNotifications/RNCommandsHandler.h | 4 +- RNNotifications/RNCommandsHandler.m | 8 +- RNNotifications/RNNotificationCenter.h | 4 +- RNNotifications/RNNotificationCenter.m | 23 ++-- .../RNCommandsHandlerIntegrationTest.m | 26 ++-- lib/src/Notifications.ts | 78 +++++++++++- lib/src/adapters/NativeCommandsSender.ts | 62 +++++++++- lib/src/commands/Commands.test.ts | 115 +++++++++++++++++- lib/src/commands/Commands.ts | 42 ++++++- lib/src/interfaces/Notification.ts | 25 ++++ 12 files changed, 356 insertions(+), 47 deletions(-) diff --git a/RNNotifications/RCTConvert+RNNotifications.m b/RNNotifications/RCTConvert+RNNotifications.m index 3c5f7c9..92609ec 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 04e357c..804d3cf 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 b8bf9b4..a55b795 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 4daf611..e0e3061 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 b9f461c..df33f6c 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 0741825..92ef14c 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 39220dc..e2760b4 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 6400ee8..68b00e0 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 1400153..1b9b916 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 11995b6..f1da265 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 cdb545f..882a5e9 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 e24c46b..a564909 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 +} -- 2.26.2