RNNotifications.m 29.3 KB
Newer Older
Lidan Hifi's avatar
Lidan Hifi committed
1 2

#import <UIKit/UIKit.h>
3
#import <PushKit/PushKit.h>
4 5 6 7 8
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>
#import "RNNotifications.h"
#import <React/RCTConvert.h>
#import <React/RCTUtils.h>
9
#import "RNNotificationsBridgeQueue.h"
10
#import <UserNotifications/UserNotifications.h>
Lidan Hifi's avatar
Lidan Hifi committed
11

12 13
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

14 15
NSString* const RNNotificationCreateAction = @"CREATE";
NSString* const RNNotificationClearAction = @"CLEAR";
Lidan Hifi's avatar
Lidan Hifi committed
16

17 18 19 20 21 22
NSString* const RNNotificationsRegistered = @"RNNotificationsRegistered";
NSString* const RNNotificationsRegistrationFailed = @"RNNotificationsRegistrationFailed";
NSString* const RNPushKitRegistered = @"RNPushKitRegistered";
NSString* const RNNotificationReceivedForeground = @"RNNotificationReceivedForeground";
NSString* const RNNotificationReceivedBackground = @"RNNotificationReceivedBackground";
NSString* const RNNotificationOpened = @"RNNotificationOpened";
23
NSString* const RNNotificationActionTriggered = @"RNNotificationActionTriggered";
Lidan Hifi's avatar
Lidan Hifi committed
24

25 26 27 28 29 30 31 32 33
/*
 * Converters for Interactive Notifications
 */
@implementation RCTConvert (UIUserNotificationActivationMode)
RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{
                                                        @"foreground": @(UIUserNotificationActivationModeForeground),
                                                        @"background": @(UIUserNotificationActivationModeBackground)
                                                        }), UIUserNotificationActivationModeForeground, integerValue)
@end
34

35 36 37 38 39 40
@implementation RCTConvert (UIUserNotificationActionContext)
RCT_ENUM_CONVERTER(UIUserNotificationActionContext, (@{
                                                       @"default": @(UIUserNotificationActionContextDefault),
                                                       @"minimal": @(UIUserNotificationActionContextMinimal)
                                                       }), UIUserNotificationActionContextDefault, integerValue)
@end
41 42

@implementation RCTConvert (UIUserNotificationActionBehavior)
43 44 45 46 47
/* iOS 9 only */
RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{
                                                        @"default": @(UIUserNotificationActionBehaviorDefault),
                                                        @"textInput": @(UIUserNotificationActionBehaviorTextInput)
                                                        }), UIUserNotificationActionBehaviorDefault, integerValue)
48 49
@end

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
@implementation RCTConvert (UIMutableUserNotificationAction)
+ (UIMutableUserNotificationAction *)UIMutableUserNotificationAction:(id)json
{
    NSDictionary<NSString *, id> *details = [self NSDictionary:json];

    UIMutableUserNotificationAction* action =[UIMutableUserNotificationAction new];
    action.activationMode = [RCTConvert UIUserNotificationActivationMode:details[@"activationMode"]];
    action.behavior = [RCTConvert UIUserNotificationActionBehavior:details[@"behavior"]];
    action.authenticationRequired = [RCTConvert BOOL:details[@"authenticationRequired"]];
    action.destructive = [RCTConvert BOOL:details[@"destructive"]];
    action.title = [RCTConvert NSString:details[@"title"]];
    action.identifier = [RCTConvert NSString:details[@"identifier"]];

    return action;
}
65
@end
66

67 68 69 70 71 72 73
@implementation RCTConvert (UIMutableUserNotificationCategory)
+ (UIMutableUserNotificationCategory *)UIMutableUserNotificationCategory:(id)json
{
    NSDictionary<NSString *, id> *details = [self NSDictionary:json];

    UIMutableUserNotificationCategory* category = [UIMutableUserNotificationCategory new];
    category.identifier = details[@"identifier"];
74

75 76 77 78 79
    // category actions
    NSMutableArray* actions = [NSMutableArray new];
    for (NSDictionary* actionJson in [RCTConvert NSArray:details[@"actions"]]) {
        [actions addObject:[RCTConvert UIMutableUserNotificationAction:actionJson]];
    }
80

81
    [category setActions:actions forContext:[RCTConvert UIUserNotificationActionContext:details[@"context"]]];
82

83 84 85 86 87 88
    return category;
}
@end

@implementation RCTConvert (UILocalNotification)
+ (UILocalNotification *)UILocalNotification:(id)json
89 90
{
    NSDictionary<NSString *, id> *details = [self NSDictionary:json];
91 92 93 94 95 96 97 98 99 100 101 102 103 104

    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;
    if ([RCTConvert BOOL:details[@"silent"]]) {
        notification.soundName = nil;
    }
    notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{};
    notification.category = [RCTConvert NSString:details[@"category"]];

    return notification;
105 106 107
}
@end

108 109 110 111
@implementation RCTConvert (UNNotificationRequest)
+ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSString*)notificationId
{
    NSDictionary<NSString *, id> *details = [self NSDictionary:json];
112

113 114 115
    UNMutableNotificationContent *content = [UNMutableNotificationContent new];
    content.body = [RCTConvert NSString:details[@"alertBody"]];
    content.title = [RCTConvert NSString:details[@"alertTitle"]];
116 117 118 119
    content.sound = [RCTConvert NSString:details[@"soundName"]]
        ? [UNNotificationSound soundNamed:[RCTConvert NSString:details[@"soundName"]]]
        : [UNNotificationSound defaultSound];
    if ([RCTConvert BOOL:details[@"silent"]]) {
120 121
        content.sound = nil;
    }
122 123
    content.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{};
    content.categoryIdentifier = [RCTConvert NSString:details[@"category"]];
124

125 126 127 128 129 130 131 132 133 134 135 136
    NSDate *triggerDate = [RCTConvert NSDate:details[@"fireDate"]];
    UNCalendarNotificationTrigger *trigger = nil;
    if (triggerDate != nil) {
        NSDateComponents *triggerDateComponents = [[NSCalendar currentCalendar]
                                                   components:NSCalendarUnitYear +
                                                   NSCalendarUnitMonth + NSCalendarUnitDay +
                                                   NSCalendarUnitHour + NSCalendarUnitMinute +
                                                   NSCalendarUnitSecond + NSCalendarUnitTimeZone
                                                   fromDate:triggerDate];
        trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDateComponents
                                                                           repeats:NO];
    }
137

138 139 140 141 142
    return [UNNotificationRequest requestWithIdentifier:notificationId
                                                content:content trigger:trigger];
}
@end

143 144
static NSDictionary *RCTFormatUNNotification(UNNotification *notification)
{
145 146
  NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary];
  UNNotificationContent *content = notification.request.content;
147

148
  formattedNotification[@"identifier"] = notification.request.identifier;
149

150 151 152 153 154 155
  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;
  }
156

157 158 159 160 161
  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));
162

163 164
  return formattedNotification;
}
165

166
@implementation RNNotifications
167

168 169
RCT_EXPORT_MODULE()

170 171 172
@synthesize bridge = _bridge;

- (void)dealloc
Lidan Hifi's avatar
Lidan Hifi committed
173
{
174
    [[NSNotificationCenter defaultCenter] removeObserver:self];
Lidan Hifi's avatar
Lidan Hifi committed
175 176
}

Artal Druk's avatar
Artal Druk committed
177 178 179
+ (BOOL)requiresMainQueueSetup {
    return YES;
}
180

Lidan Hifi's avatar
Lidan Hifi committed
181 182
- (void)setBridge:(RCTBridge *)bridge
{
183
    _bridge = bridge;
Lidan Hifi's avatar
Lidan Hifi committed
184

185 186 187 188
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationsRegistered:)
                                                 name:RNNotificationsRegistered
                                               object:nil];
189

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationsRegistrationFailed:)
                                                 name:RNNotificationsRegistrationFailed
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handlePushKitRegistered:)
                                                 name:RNPushKitRegistered
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationReceivedForeground:)
                                                 name:RNNotificationReceivedForeground
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationReceivedBackground:)
                                                 name:RNNotificationReceivedBackground
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationOpened:)
                                                 name:RNNotificationOpened
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationActionTriggered:)
                                                 name:RNNotificationActionTriggered
                                               object:nil];

    [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    UILocalNotification *localNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = localNotification ? localNotification.userInfo : nil;
223
}
224

225 226 227 228
/*
 * Public Methods
 */
+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
229
{
230 231
    if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) {
        [[UIApplication sharedApplication] registerForRemoteNotifications];
232 233
    }
}
234

235
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken
236
{
237 238 239 240
    NSString *tokenRepresentation = [deviceToken isKindOfClass:[NSString class]] ? deviceToken : [self deviceTokenToString:deviceToken];
    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistered
                                                        object:self
                                                      userInfo:@{@"deviceToken": tokenRepresentation}];
241
}
Lidan Hifi's avatar
Lidan Hifi committed
242

243 244 245 246
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistrationFailed
                                                        object:self
                                                      userInfo:@{@"code": [NSNumber numberWithInteger:error.code], @"domain": error.domain, @"localizedDescription": error.localizedDescription}];
247
}
Lidan Hifi's avatar
Lidan Hifi committed
248

249
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification
250
{
251
    UIApplicationState state = [UIApplication sharedApplication].applicationState;
252

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
    if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) {
        // JS thread is ready, push the notification to the bridge

        if (state == UIApplicationStateActive) {
            // Notification received foreground
            [self didReceiveNotificationOnForegroundState:notification];
        } else if (state == UIApplicationStateInactive) {
            // Notification opened
            [self didNotificationOpen:notification];
        } else {
            // Notification received background
            [self didReceiveNotificationOnBackgroundState:notification];
        }
    } else {
        // JS thread is not ready - store it in the native notifications queue
        [[RNNotificationsBridgeQueue sharedInstance] postNotification:notification];
269 270
    }
}
271

272
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification
273
{
274 275 276 277 278
    
    return @[ RNNotificationsRegistered,
              RNNotificationsRegistrationFailed,
              RNNotificationReceivedForeground,
              RNNotificationReceivedBackground,
Artal Druk's avatar
Artal Druk committed
279 280 281 282
              RNNotificationOpened,
              RNNotificationActionReceived,
              RNPushKitRegistered,
              RNNotificationActionTriggered];
283
    
284 285
}

286
-(void) checkAndSendEvent:(NSString*)name body:(id)body
287
{
288 289 290 291
    if(_hasListeners)
    {
        [self sendEventWithName:name body:body];
    }
292 293
}

294
+ (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
295
{
296
    [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:notification.userInfo completionHandler:completionHandler];
297 298
}

299
+ (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
Lidan Hifi's avatar
Lidan Hifi committed
300
{
301
    [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:userInfo completionHandler:completionHandler];
302 303
}

Lidan Hifi's avatar
Lidan Hifi committed
304 305 306
/*
 * Notification handlers
 */
307 308 309 310 311 312
+ (void)didReceiveNotificationOnForegroundState:(NSDictionary *)notification
{
    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedForeground
                                                        object:self
                                                      userInfo:notification];
}
Lidan Hifi's avatar
Lidan Hifi committed
313

314
+ (void)didReceiveNotificationOnBackgroundState:(NSDictionary *)notification
Lidan Hifi's avatar
Lidan Hifi committed
315
{
316 317 318 319
    NSDictionary* managedAps  = [notification objectForKey:@"managedAps"];
    NSDictionary* alert = [managedAps objectForKey:@"alert"];
    NSString* action = [managedAps objectForKey:@"action"];
    NSString* notificationId = [managedAps objectForKey:@"notificationId"];
320

Lidan Hifi's avatar
Lidan Hifi committed
321 322
    if (action) {
        // create or delete notification
Lidan Hifi's avatar
Lidan Hifi committed
323
        if ([action isEqualToString: RNNotificationCreateAction]
Lidan Hifi's avatar
Lidan Hifi committed
324 325 326
            && notificationId
            && alert) {
            [self dispatchLocalNotificationFromNotification:notification];
327

Lidan Hifi's avatar
Lidan Hifi committed
328
        } else if ([action isEqualToString: RNNotificationClearAction] && notificationId) {
Lidan Hifi's avatar
Lidan Hifi committed
329 330 331
            [self clearNotificationFromNotificationsCenter:notificationId];
        }
    }
332 333 334 335

    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedBackground
                                                        object:self
                                                      userInfo:notification];
Lidan Hifi's avatar
Lidan Hifi committed
336 337 338 339
}

+ (void)didNotificationOpen:(NSDictionary *)notification
{
Lidan Hifi's avatar
Lidan Hifi committed
340
    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationOpened
Lidan Hifi's avatar
Lidan Hifi committed
341 342 343 344 345 346 347
                                                        object:self
                                                      userInfo:notification];
}

/*
 * Helper methods
 */
348
+ (void)dispatchLocalNotificationFromNotification:(NSDictionary *)notification
Lidan Hifi's avatar
Lidan Hifi committed
349
{
350 351 352 353
    NSDictionary* managedAps  = [notification objectForKey:@"managedAps"];
    NSDictionary* alert = [managedAps objectForKey:@"alert"];
    NSString* action = [managedAps objectForKey:@"action"];
    NSString* notificationId = [managedAps objectForKey:@"notificationId"];
354

Lidan Hifi's avatar
Lidan Hifi committed
355
    if ([action isEqualToString: RNNotificationCreateAction]
Lidan Hifi's avatar
Lidan Hifi committed
356 357
        && notificationId
        && alert) {
358

Lidan Hifi's avatar
Lidan Hifi committed
359
        // trigger new client push notification
360
        UILocalNotification* note = [UILocalNotification new];
Lidan Hifi's avatar
Lidan Hifi committed
361 362
        note.alertTitle = [alert objectForKey:@"title"];
        note.alertBody = [alert objectForKey:@"body"];
363
        note.userInfo = notification;
364
        note.soundName = [managedAps objectForKey:@"sound"];
365
        note.category = [managedAps objectForKey:@"category"];
366

Lidan Hifi's avatar
Lidan Hifi committed
367
        [[UIApplication sharedApplication] presentLocalNotificationNow:note];
368

Lidan Hifi's avatar
Lidan Hifi committed
369 370 371 372 373
        // Serialize it and store so we can delete it later
        NSData* data = [NSKeyedArchiver archivedDataWithRootObject:note];
        NSString* notificationKey = [self buildNotificationKeyfromNotification:notificationId];
        [[NSUserDefaults standardUserDefaults] setObject:data forKey:notificationKey];
        [[NSUserDefaults standardUserDefaults] synchronize];
374

Lidan Hifi's avatar
Lidan Hifi committed
375 376 377 378
        NSLog(@"Local notification was triggered: %@", notificationKey);
    }
}

379
+ (void)clearNotificationFromNotificationsCenter:(NSString *)notificationId
Lidan Hifi's avatar
Lidan Hifi committed
380 381 382 383 384
{
    NSString* notificationKey = [self buildNotificationKeyfromNotification:notificationId];
    NSData* data = [[NSUserDefaults standardUserDefaults] objectForKey:notificationKey];
    if (data) {
        UILocalNotification* notification = [NSKeyedUnarchiver unarchiveObjectWithData: data];
385

Lidan Hifi's avatar
Lidan Hifi committed
386 387 388
        // delete the notification
        [[UIApplication sharedApplication] cancelLocalNotification:notification];
        [[NSUserDefaults standardUserDefaults] removeObjectForKey:notificationKey];
389

390
        NSLog(@"Local notification removed: %@", notificationKey);
391

Lidan Hifi's avatar
Lidan Hifi committed
392 393 394 395
        return;
    }
}

396
+ (NSString *)buildNotificationKeyfromNotification:(NSString *)notificationId
Lidan Hifi's avatar
Lidan Hifi committed
397 398 399 400
{
    return [NSString stringWithFormat:@"%@.%@", [[NSBundle mainBundle] bundleIdentifier], notificationId];
}

401 402 403 404 405 406 407 408
+ (NSString *)deviceTokenToString:(NSData *)deviceToken
{
    NSMutableString *result = [NSMutableString string];
    NSUInteger deviceTokenLength = deviceToken.length;
    const unsigned char *bytes = deviceToken.bytes;
    for (NSUInteger i = 0; i < deviceTokenLength; i++) {
        [result appendFormat:@"%02x", bytes[i]];
    }
409

410 411 412
    return [result copy];
}

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
+ (void)requestPermissionsWithCategories:(NSMutableSet *)categories
{
    UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert);
    UIUserNotificationSettings* settings = [UIUserNotificationSettings settingsForTypes:types categories:categories];

    [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}

+ (void)emitNotificationActionForIdentifier:(NSString *)identifier responseInfo:(NSDictionary *)responseInfo userInfo:(NSDictionary *)userInfo  completionHandler:(void (^)())completionHandler
{
    NSString* completionKey = [NSString stringWithFormat:@"%@.%@", identifier, [NSString stringWithFormat:@"%d", (long)[[NSDate date] timeIntervalSince1970]]];
    NSMutableDictionary* info = [[NSMutableDictionary alloc] initWithDictionary:@{ @"identifier": identifier, @"completionKey": completionKey }];

    // add text
    NSString* text = [responseInfo objectForKey:UIUserNotificationActionResponseTypedTextKey];
    if (text != NULL) {
        info[@"text"] = text;
    }

    // add notification custom data
    if (userInfo != NULL) {
        info[@"notification"] = userInfo;
    }

    // Emit event to the queue (in order to store the completion handler). if JS thread is ready, post it also to the notification center (to the bridge).
    [[RNNotificationsBridgeQueue sharedInstance] postAction:info withCompletionKey:completionKey andCompletionHandler:completionHandler];

    if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) {
        [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered
                                                            object:self
                                                          userInfo:info];
    }
}

+ (void)registerPushKit
{
    // Create a push registry object
    PKPushRegistry* pushKitRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];

    // Set the registry delegate to app delegate
    pushKitRegistry.delegate = [[UIApplication sharedApplication] delegate];
    pushKitRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}

+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type
{
    [[NSNotificationCenter defaultCenter] postNotificationName:RNPushKitRegistered
                                                        object:self
                                                      userInfo:@{@"pushKitToken": [self deviceTokenToString:credentials.token]}];
}

- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type
{
    [RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload];
}

/*
 * Javascript events
 */
- (void)handleNotificationsRegistered:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistered" body:notification.userInfo];
}

- (void)handleNotificationsRegistrationFailed:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistrationFailed" body:notification.userInfo];
}

- (void)handlePushKitRegistered:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"pushKitRegistered" body:notification.userInfo];
}

- (void)handleNotificationReceivedForeground:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationReceivedForeground" body:notification.userInfo];
}

- (void)handleNotificationReceivedBackground:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationReceivedBackground" body:notification.userInfo];
}

- (void)handleNotificationOpened:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationOpened" body:notification.userInfo];
}

- (void)handleNotificationActionTriggered:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendAppEventWithName:@"notificationActionReceived" body:notification.userInfo];
}

Lidan Hifi's avatar
Lidan Hifi committed
507 508 509
/*
 * React Native exported methods
 */
510 511 512 513 514 515 516 517 518 519 520 521 522 523
RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json)
{
    NSMutableSet* categories = nil;

    if ([json count] > 0) {
        categories = [NSMutableSet new];
        for (NSDictionary* categoryJson in json) {
            [categories addObject:[RCTConvert UIMutableUserNotificationCategory:categoryJson]];
        }
    }

    [RNNotifications requestPermissionsWithCategories:categories];
}

524 525 526
RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
{
    NSDictionary * notification = nil;
Yedidya Kennard's avatar
Yedidya Kennard committed
527
    notification = [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification ?
528 529
        [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification :
        [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification;
530
    [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = nil;
Yedidya Kennard's avatar
Yedidya Kennard committed
531
    [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = nil;
532 533 534
    resolve(notification);
}

535 536 537 538 539
RCT_EXPORT_METHOD(log:(NSString *)message)
{
    NSLog(message);
}

540
RCT_EXPORT_METHOD(completionHandler:(NSString *)completionKey)
541
{
542
    [[RNNotificationsBridgeQueue sharedInstance] completeAction:completionKey];
543 544
}

545
RCT_EXPORT_METHOD(abandonPermissions)
546
{
547
    [[UIApplication sharedApplication] unregisterForRemoteNotifications];
548 549
}

550 551 552 553
RCT_EXPORT_METHOD(registerPushKit)
{
    [RNNotifications registerPushKit];
}
554

555 556 557 558 559
RCT_EXPORT_METHOD(getBadgesCount:(RCTResponseSenderBlock)callback)
{
    NSInteger count = [UIApplication sharedApplication].applicationIconBadgeNumber;
    callback(@[ [NSNumber numberWithInteger:count] ]);
}
560

561 562 563 564
RCT_EXPORT_METHOD(setBadgesCount:(int)count)
{
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:count];
}
565

566 567 568 569 570 571
RCT_EXPORT_METHOD(backgroundTimeRemaining:(RCTResponseSenderBlock)callback)
{
    NSTimeInterval remainingTime = [UIApplication sharedApplication].backgroundTimeRemaining;
    callback(@[ [NSNumber numberWithDouble:remainingTime] ]);
}

572 573
RCT_EXPORT_METHOD(consumeBackgroundQueue)
{
574 575
    // Mark JS Thread as ready
    [RNNotificationsBridgeQueue sharedInstance].jsIsReady = YES;
576

577
    // Push actions to JS
578
    [[RNNotificationsBridgeQueue sharedInstance] consumeActionsQueue:^(NSDictionary* action) {
579 580 581
        [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered
                                                            object:self
                                                          userInfo:action];
582
    }];
583

584
    // Push background notifications to JS
585
    [[RNNotificationsBridgeQueue sharedInstance] consumeNotificationsQueue:^(NSDictionary* notification) {
586
        [RNNotifications didReceiveNotificationOnBackgroundState:notification];
587
    }];
588

589 590 591
    // Push opened local notifications
    NSDictionary* openedLocalNotification = [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification;
    if (openedLocalNotification) {
592
        [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = nil;
593
        [RNNotifications didNotificationOpen:openedLocalNotification];
594
    }
595

596 597 598
    // Push opened remote notifications
    NSDictionary* openedRemoteNotification = [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification;
    if (openedRemoteNotification) {
599
        [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = nil;
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
        [RNNotifications didNotificationOpen:openedRemoteNotification];
    }
}

RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification withId:(NSString *)notificationId)
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) {
        UNNotificationRequest* localNotification = [RCTConvert UNNotificationRequest:notification withId:notificationId];
        [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:localNotification withCompletionHandler:nil];
    } else {
        UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification];
        NSMutableArray* userInfo = localNotification.userInfo.mutableCopy;
        [userInfo setValue:notificationId forKey:@"__id"];
        localNotification.userInfo = userInfo;

        if ([notification objectForKey:@"fireDate"] != nil) {
            [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
        } else {
            [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
        }
620 621 622
    }
}

623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
RCT_EXPORT_METHOD(cancelLocalNotification:(NSString *)notificationId)
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) {
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        [center removePendingNotificationRequestsWithIdentifiers:@[notificationId]];
    } else {
        for (UILocalNotification* notification in [UIApplication sharedApplication].scheduledLocalNotifications) {
            NSDictionary* notificationInfo = notification.userInfo;

            if ([[notificationInfo objectForKey:@"__id"] isEqualToString:notificationId]) {
                [[UIApplication sharedApplication] cancelLocalNotification:notification];
            }
        }
    }
}
638 639 640 641 642 643

RCT_EXPORT_METHOD(cancelAllLocalNotifications)
{
    [RCTSharedApplication() cancelAllLocalNotifications];
}

644 645
RCT_EXPORT_METHOD(isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
{
646
    BOOL ans = [[[UIApplication sharedApplication] currentUserNotificationSettings] types] != 0;
647 648 649
    resolve(@(ans));
}

Ryan Eberhardt's avatar
Ryan Eberhardt committed
650 651 652 653 654 655 656 657 658 659
RCT_EXPORT_METHOD(checkPermissions:(RCTPromiseResolveBlock) resolve
                  reject:(RCTPromiseRejectBlock) reject) {
    UIUserNotificationSettings *currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
    resolve(@{
              @"badge": @((currentSettings.types & UIUserNotificationTypeBadge) > 0),
              @"sound": @((currentSettings.types & UIUserNotificationTypeSound) > 0),
              @"alert": @((currentSettings.types & UIUserNotificationTypeAlert) > 0),
              });
}

660 661 662 663
#if !TARGET_OS_TV

RCT_EXPORT_METHOD(removeAllDeliveredNotifications)
{
664 665 666 667
  if ([UNUserNotificationCenter class]) {
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    [center removeAllDeliveredNotifications];
  }
668 669 670 671
}

RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray<NSString *> *)identifiers)
{
672 673 674 675
  if ([UNUserNotificationCenter class]) {
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    [center removeDeliveredNotificationsWithIdentifiers:identifiers];
  }
676 677 678 679
}

RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback)
{
680 681 682 683 684 685 686 687 688 689 690
  if ([UNUserNotificationCenter class]) {
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    [center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
      NSMutableArray<NSDictionary *> *formattedNotifications = [NSMutableArray new];

      for (UNNotification *notification in notifications) {
        [formattedNotifications addObject:RCTFormatUNNotification(notification)];
      }
      callback(@[formattedNotifications]);
    }];
  }
691 692 693 694
}

#endif !TARGET_OS_TV

Lidan Hifi's avatar
Lidan Hifi committed
695
@end