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