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