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 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(shouldCurrentTaskAutoReload, 109 shouldCurrentTaskAutoReloadWithResolver:(RCTPromiseResolveBlock)resolve 110 reject:(RCTPromiseRejectBlock)reject) 111{ 112 if (_delegate) { 113 resolve(@([_delegate homeModuleShouldAutoReloadCurrentTask:self])); 114 } else { 115 resolve(@NO); 116 } 117} 118 119RCT_REMAP_METHOD(isLegacyMenuBehaviorEnabledAsync, 120 isLegacyMenuBehaviorEnabledWithResolver:(RCTPromiseResolveBlock)resolve 121 rejecter:(RCTPromiseRejectBlock)reject) 122{ 123 if (_delegate) { 124 resolve(@([_delegate homeModuleShouldEnableLegacyMenuBehavior:self])); 125 } else { 126 resolve(@(NO)); 127 } 128} 129 130RCT_EXPORT_METHOD(setIsLegacyMenuBehaviorEnabledAsync:(BOOL)isEnabled) 131{ 132 if (_delegate) { 133 [_delegate homeModule:self didSelectEnableLegacyMenuBehavior:isEnabled]; 134 } 135} 136 137RCT_REMAP_METHOD(getDevMenuItemsToShow, 138 getDevMenuItemsToShowWithResolver:(RCTPromiseResolveBlock)resolve 139 reject:(RCTPromiseRejectBlock)reject) 140{ 141 if (_delegate && [_delegate homeModuleShouldEnableDevtools:self]) { 142 resolve([_delegate devMenuItemsForHomeModule:self]); 143 } else { 144 // don't reject, just show no devtools 145 resolve(@{}); 146 } 147} 148 149RCT_EXPORT_METHOD(selectDevMenuItemWithKey:(NSString *)key) 150{ 151 if (_delegate) { 152 [_delegate homeModule:self didSelectDevMenuItemWithKey:key]; 153 } 154} 155 156RCT_EXPORT_METHOD(selectRefresh) 157{ 158 if (_delegate) { 159 [_delegate homeModuleDidSelectRefresh:self]; 160 } 161} 162 163RCT_EXPORT_METHOD(selectCloseMenu) 164{ 165 if (_delegate) { 166 [_delegate homeModuleDidSelectCloseMenu:self]; 167 } 168} 169 170RCT_EXPORT_METHOD(selectGoToHome) 171{ 172 if (_delegate) { 173 [_delegate homeModuleDidSelectGoToHome:self]; 174 } 175} 176 177RCT_EXPORT_METHOD(selectQRReader) 178{ 179 if (_delegate) { 180 [_delegate homeModuleDidSelectQRReader:self]; 181 } 182} 183 184RCT_EXPORT_METHOD(addDevMenu) 185{ 186 __weak typeof(self) weakSelf = self; 187 dispatch_async(dispatch_get_main_queue(), ^{ 188 if (weakSelf.delegate) { 189 [weakSelf.delegate homeModuleDidSelectHomeDiagnostics:self]; 190 } 191 }); 192} 193 194RCT_EXPORT_METHOD(onLoaded) 195{ 196 [[NSNotificationCenter defaultCenter] postNotificationName:EX_UNVERSIONED(@"EXKernelJSIsLoadedNotification") object:self]; 197} 198 199RCT_REMAP_METHOD(onEventSuccess, 200 eventId:(NSString *)eventId 201 body:(NSDictionary *)body) 202{ 203 void (^success)(NSDictionary *) = [_eventSuccessBlocks objectForKey:eventId]; 204 if (success) { 205 success(body); 206 [_eventSuccessBlocks removeObjectForKey:eventId]; 207 [_eventFailureBlocks removeObjectForKey:eventId]; 208 } 209} 210 211RCT_REMAP_METHOD(onEventFailure, 212 eventId:(NSString *)eventId 213 message:(NSString *)message) 214{ 215 void (^failure)(NSString *) = [_eventFailureBlocks objectForKey:eventId]; 216 if (failure) { 217 failure(message); 218 [_eventSuccessBlocks removeObjectForKey:eventId]; 219 [_eventFailureBlocks removeObjectForKey:eventId]; 220 } 221} 222 223@end 224