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