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