1// Copyright 2015-present 650 Industries. All rights reserved. 2 3#import "EXEnvironment.h" 4#import "EXHomeModule.h" 5#import "EXSession.h" 6#import "EXUnversioned.h" 7 8#import <React/RCTEventDispatcher.h> 9 10@interface EXHomeModule () 11 12@property (nonatomic, assign) BOOL hasListeners; 13@property (nonatomic, strong) NSMutableDictionary *eventSuccessBlocks; 14@property (nonatomic, strong) NSMutableDictionary *eventFailureBlocks; 15@property (nonatomic, strong) NSArray * _Nonnull sdkVersions; 16@property (nonatomic, weak) id<EXHomeModuleDelegate> delegate; 17 18@end 19 20@implementation EXHomeModule 21 22+ (NSString *)moduleName { return @"ExponentKernel"; } 23 24- (instancetype)initWithExperienceId:(NSString *)experienceId kernelServiceDelegate:(id)kernelServiceInstance params:(NSDictionary *)params 25{ 26 if (self = [super initWithExperienceId:experienceId kernelServiceDelegate:kernelServiceInstance params:params]) { 27 _eventSuccessBlocks = [NSMutableDictionary dictionary]; 28 _eventFailureBlocks = [NSMutableDictionary dictionary]; 29 _sdkVersions = params[@"supportedSdkVersions"]; 30 _delegate = kernelServiceInstance; 31 } 32 return self; 33} 34 35+ (BOOL)requiresMainQueueSetup 36{ 37 return NO; 38} 39 40- (NSDictionary *)constantsToExport 41{ 42 return @{ @"sdkVersions": _sdkVersions }; 43} 44 45#pragma mark - RCTEventEmitter methods 46 47- (NSArray<NSString *> *)supportedEvents 48{ 49 return @[]; 50} 51 52/** 53 * Override this method to avoid the [self supportedEvents] validation 54 */ 55- (void)sendEventWithName:(NSString *)eventName body:(id)body 56{ 57 // Note that this could be a versioned bridge! 58 [self.bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit" 59 args:body ? @[eventName, body] : @[eventName]]; 60} 61 62#pragma mark - 63 64- (void)dispatchJSEvent:(NSString *)eventName body:(NSDictionary *)eventBody onSuccess:(void (^)(NSDictionary *))success onFailure:(void (^)(NSString *))failure 65{ 66 NSString *qualifiedEventName = [NSString stringWithFormat:@"ExponentKernel.%@", eventName]; 67 NSMutableDictionary *qualifiedEventBody = (eventBody) ? [eventBody mutableCopy] : [NSMutableDictionary dictionary]; 68 69 if (success && failure) { 70 NSString *eventId = [[NSUUID UUID] UUIDString]; 71 [_eventSuccessBlocks setObject:success forKey:eventId]; 72 [_eventFailureBlocks setObject:failure forKey:eventId]; 73 [qualifiedEventBody setObject:eventId forKey:@"eventId"]; 74 } 75 76 [self sendEventWithName:qualifiedEventName body:qualifiedEventBody]; 77} 78 79/** 80 * Duplicates Linking.openURL but does not validate that this is an exponent URL; 81 * in other words, we just take your word for it and never hand it off to iOS. 82 * Used by the home screen URL bar. 83 */ 84RCT_EXPORT_METHOD(openURL:(NSURL *)URL 85 resolve:(RCTPromiseResolveBlock)resolve 86 reject:(__unused RCTPromiseRejectBlock)reject) 87{ 88 if (URL) { 89 [_delegate homeModule:self didOpenUrl:URL.absoluteString]; 90 resolve(@YES); 91 } else { 92 NSError *err = [NSError errorWithDomain:EX_UNVERSIONED(@"EXKernelErrorDomain") code:-1 userInfo:@{ NSLocalizedDescriptionKey: @"Cannot open a nil url" }]; 93 reject(@"E_INVALID_URL", err.localizedDescription, err); 94 } 95} 96 97RCT_REMAP_METHOD(doesCurrentTaskEnableDevtools, 98 doesCurrentTaskEnableDevtoolsWithResolver:(RCTPromiseResolveBlock)resolve 99 reject:(RCTPromiseRejectBlock)reject) 100{ 101 if (_delegate) { 102 resolve(@([_delegate homeModuleShouldEnableDevtools:self])); 103 } else { 104 // don't reject, just disable devtools 105 resolve(@NO); 106 } 107} 108 109RCT_REMAP_METHOD(isLegacyMenuBehaviorEnabledAsync, 110 isLegacyMenuBehaviorEnabledWithResolver:(RCTPromiseResolveBlock)resolve 111 rejecter:(RCTPromiseRejectBlock)reject) 112{ 113 if (_delegate) { 114 resolve(@([_delegate homeModuleShouldEnableLegacyMenuBehavior:self])); 115 } else { 116 resolve(@(NO)); 117 } 118} 119 120RCT_EXPORT_METHOD(setIsLegacyMenuBehaviorEnabledAsync:(BOOL)isEnabled) 121{ 122 if (_delegate) { 123 [_delegate homeModule:self didSelectEnableLegacyMenuBehavior:isEnabled]; 124 } 125} 126 127RCT_REMAP_METHOD(getDevMenuItemsToShow, 128 getDevMenuItemsToShowWithResolver:(RCTPromiseResolveBlock)resolve 129 reject:(RCTPromiseRejectBlock)reject) 130{ 131 if (_delegate && [_delegate homeModuleShouldEnableDevtools:self]) { 132 resolve([_delegate devMenuItemsForHomeModule:self]); 133 } else { 134 // don't reject, just show no devtools 135 resolve(@{}); 136 } 137} 138 139RCT_EXPORT_METHOD(selectDevMenuItemWithKey:(NSString *)key) 140{ 141 if (_delegate) { 142 [_delegate homeModule:self didSelectDevMenuItemWithKey:key]; 143 } 144} 145 146RCT_EXPORT_METHOD(selectRefresh) 147{ 148 if (_delegate) { 149 [_delegate homeModuleDidSelectRefresh:self]; 150 } 151} 152 153RCT_EXPORT_METHOD(selectCloseMenu) 154{ 155 if (_delegate) { 156 [_delegate homeModuleDidSelectCloseMenu:self]; 157 } 158} 159 160RCT_EXPORT_METHOD(selectGoToHome) 161{ 162 if (_delegate) { 163 [_delegate homeModuleDidSelectGoToHome:self]; 164 } 165} 166 167RCT_EXPORT_METHOD(selectQRReader) 168{ 169 if (_delegate) { 170 [_delegate homeModuleDidSelectQRReader:self]; 171 } 172} 173 174RCT_REMAP_METHOD(getSessionAsync, 175 getSessionAsync:(RCTPromiseResolveBlock)resolve 176 rejecter:(RCTPromiseRejectBlock)reject) 177{ 178 NSDictionary *session = [[EXSession sharedInstance] session]; 179 resolve(session); 180} 181 182RCT_REMAP_METHOD(setSessionAsync, 183 setSessionAsync:(NSDictionary *)session 184 resolver:(RCTPromiseResolveBlock)resolve 185 rejecter:(RCTPromiseRejectBlock)reject) 186{ 187 NSError *error; 188 BOOL success = [[EXSession sharedInstance] saveSessionToKeychain:session error:&error]; 189 if (success) { 190 resolve(nil); 191 } else { 192 reject(@"ERR_SESSION_NOT_SAVED", @"Could not save session", error); 193 } 194} 195 196RCT_REMAP_METHOD(removeSessionAsync, 197 removeSessionAsync:(RCTPromiseResolveBlock)resolve 198 rejecter:(RCTPromiseRejectBlock)reject) 199{ 200 NSError *error; 201 BOOL success = [[EXSession sharedInstance] deleteSessionFromKeychainWithError:&error]; 202 if (success) { 203 resolve(nil); 204 } else { 205 reject(@"ERR_SESSION_NOT_REMOVED", @"Could not remove session", error); 206 } 207} 208 209RCT_EXPORT_METHOD(addDevMenu) 210{ 211 __weak typeof(self) weakSelf = self; 212 dispatch_async(dispatch_get_main_queue(), ^{ 213 if (weakSelf.delegate) { 214 [weakSelf.delegate homeModuleDidSelectHomeDiagnostics:self]; 215 } 216 }); 217} 218 219RCT_REMAP_METHOD(getIsNuxFinishedAsync, 220 getIsNuxFinishedWithResolver:(RCTPromiseResolveBlock)resolve 221 rejecter:(RCTPromiseRejectBlock)reject) 222{ 223 if (_delegate) { 224 BOOL isFinished = [_delegate homeModuleShouldFinishNux:self]; 225 resolve(@(isFinished)); 226 } else { 227 resolve(@(NO)); 228 } 229} 230 231RCT_REMAP_METHOD(setIsNuxFinishedAsync, 232 setIsNuxFinished:(BOOL)isNuxFinished) 233{ 234 if (_delegate) { 235 [_delegate homeModule:self didFinishNux:isNuxFinished]; 236 } 237} 238 239RCT_REMAP_METHOD(onEventSuccess, 240 eventId:(NSString *)eventId 241 body:(NSDictionary *)body) 242{ 243 void (^success)(NSDictionary *) = [_eventSuccessBlocks objectForKey:eventId]; 244 if (success) { 245 success(body); 246 [_eventSuccessBlocks removeObjectForKey:eventId]; 247 [_eventFailureBlocks removeObjectForKey:eventId]; 248 } 249} 250 251RCT_REMAP_METHOD(onEventFailure, 252 eventId:(NSString *)eventId 253 message:(NSString *)message) 254{ 255 void (^failure)(NSString *) = [_eventFailureBlocks objectForKey:eventId]; 256 if (failure) { 257 failure(message); 258 [_eventSuccessBlocks removeObjectForKey:eventId]; 259 [_eventFailureBlocks removeObjectForKey:eventId]; 260 } 261} 262 263@end 264