1// Copyright 2016-present 650 Industries. All rights reserved.
2
3#import <EXNotifications/EXLegacyRemoteNotificationPermissionRequester.h>
4#import <ExpoModulesCore/EXUtilities.h>
5
6@interface EXLegacyRemoteNotificationPermissionRequester ()
7
8@property (nonatomic, strong) EXPromiseResolveBlock resolve;
9@property (nonatomic, strong) EXPromiseRejectBlock reject;
10@property (nonatomic, assign) BOOL remoteNotificationsRegistrationIsPending;
11@property (nonatomic, weak) id<EXPermissionsRequester> userNotificationPermissionRequester;
12@property (nonatomic, weak) dispatch_queue_t methodQueue;
13@property (nonatomic, weak) id<EXRemoteNotificationPermissionProgressPublisher> permissionProgressPublisher;
14
15@end
16
17@implementation EXLegacyRemoteNotificationPermissionRequester
18
19+ (NSString *)permissionType
20{
21  return @"notifications";
22}
23
24- (instancetype)initWithUserNotificationPermissionRequester:(id<EXPermissionsRequester>)userNotificationPermissionRequester
25                                        permissionPublisher:(id<EXRemoteNotificationPermissionProgressPublisher>)permissionProgressPublisher
26                                            withMethodQueue:(dispatch_queue_t)methodQueue
27{
28  if (self = [super init]) {
29    _remoteNotificationsRegistrationIsPending = NO;
30    _permissionProgressPublisher = permissionProgressPublisher;
31    _userNotificationPermissionRequester = userNotificationPermissionRequester;
32    _methodQueue = methodQueue;
33  }
34  return self;
35}
36
37- (NSDictionary *)getPermissions
38{
39  __block EXPermissionStatus status;
40  [EXUtilities performSynchronouslyOnMainThread:^{
41    status = (UMSharedApplication().isRegisteredForRemoteNotifications) ?
42    EXPermissionStatusGranted :
43    EXPermissionStatusUndetermined;
44  }];
45  NSMutableDictionary *permissions = [[_userNotificationPermissionRequester getPermissions] mutableCopy];
46
47  [permissions setValuesForKeysWithDictionary:@{
48                                                @"status": @(status),
49
50                                                }];
51  return permissions;
52}
53
54- (void)requestPermissionsWithResolver:(EXPromiseResolveBlock)resolve rejecter:(EXPromiseRejectBlock)reject
55{
56  if (_resolve != nil || _reject != nil) {
57    reject(@"E_AWAIT_PROMISE", @"Another request for the same permission is already being handled.", nil);
58    return;
59  }
60
61  _resolve = resolve;
62  _reject = reject;
63
64  BOOL __block isRegisteredForRemoteNotifications = NO;
65  [EXUtilities performSynchronouslyOnMainThread:^{
66    isRegisteredForRemoteNotifications = UMSharedApplication().isRegisteredForRemoteNotifications;
67  }];
68
69  if (isRegisteredForRemoteNotifications) {
70    // resolve immediately if already registered
71    [self _maybeConsumeResolverWithCurrentPermissions];
72  } else {
73    [_permissionProgressPublisher addDelegate:self];
74     EX_WEAKIFY(self)
75    [_userNotificationPermissionRequester requestPermissionsWithResolver:^(NSDictionary *permission){
76      EX_STRONGIFY(self)
77      EXPermissionStatus localNotificationsStatus = [[permission objectForKey:@"status"] intValue];
78      // We may assume that `EXLocalNotificationRequester`'s permission request will always finish
79      // when the user responds to the dialog or has already responded in the past.
80      // However, `UIApplication.registerForRemoteNotification` results in calling
81      // `application:didRegisterForRemoteNotificationsWithDeviceToken:` or
82      // `application:didFailToRegisterForRemoteNotificationsWithError:` on the application delegate
83      // ONLY when the notifications are enabled in settings (by allowing sound, alerts or app badge).
84      // So, when the local notifications are disabled, the application delegate's callbacks will not be called instantly.
85      if (localNotificationsStatus == EXPermissionStatusDenied) {
86        [self _clearObserver];
87        [self _maybeConsumeResolverWithCurrentPermissions];
88      } else {
89        self.remoteNotificationsRegistrationIsPending = YES;
90        dispatch_async(dispatch_get_main_queue(), ^{
91          [UMSharedApplication() registerForRemoteNotifications];
92        });
93      }
94    } rejecter:^(NSString *code, NSString *message, NSError *error){
95      [self _clearObserver];
96      if (self.reject) {
97        self.reject(code, message, error);
98      }
99    }];
100  }
101}
102
103- (void)dealloc
104{
105  [self _clearObserver];
106}
107
108- (void)handleDidFinishRegisteringForRemoteNotifications
109{
110  [self _clearObserver];
111  EX_WEAKIFY(self)
112  dispatch_async(_methodQueue, ^{
113    EX_STRONGIFY(self)
114    [self _maybeConsumeResolverWithCurrentPermissions];
115  });
116}
117
118- (void)_clearObserver
119{
120  [_permissionProgressPublisher removeDelegate:self];
121  _remoteNotificationsRegistrationIsPending = NO;
122}
123
124- (void)_maybeConsumeResolverWithCurrentPermissions
125{
126  if (!_remoteNotificationsRegistrationIsPending) {
127    if (_resolve) {
128      _resolve([self getPermissions]);
129      _resolve = nil;
130      _reject = nil;
131    }
132  }
133}
134
135@end
136