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