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