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