1// Copyright 2015-present 650 Industries. All rights reserved.
2
3#import "EXLinkingManager.h"
4#import "EXScopedModuleRegistry.h"
5#import "EXUtil.h"
6
7#import <React/RCTBridge.h>
8#import <React/RCTEventDispatcher.h>
9#import <React/RCTUtils.h>
10
11NSString * const EXLinkingEventOpenUrl = @"url";
12
13@interface EXLinkingManager ()
14
15@property (nonatomic, weak) id <EXLinkingManagerScopedModuleDelegate> kernelLinkingDelegate;
16@property (nonatomic, strong) NSURL *initialUrl;
17@property (nonatomic) BOOL hasListeners;
18
19@end
20
21@implementation EXLinkingManager
22
23EX_EXPORT_SCOPED_MODULE(RCTLinkingManager, KernelLinkingManager);
24
25- (instancetype)initWithExperienceStableLegacyId:(NSString *)experienceStableLegacyId
26                                        scopeKey:(NSString *)scopeKey
27                                    easProjectId:(NSString *)easProjectId
28                           kernelServiceDelegate:(id)kernelServiceInstance
29                                          params:(NSDictionary *)params
30{
31  if (self = [super initWithExperienceStableLegacyId:experienceStableLegacyId
32                                            scopeKey:scopeKey
33                                        easProjectId:easProjectId
34                               kernelServiceDelegate:kernelServiceInstance
35                                              params:params]) {
36    _kernelLinkingDelegate = kernelServiceInstance;
37    _initialUrl = params[@"initialUri"];
38    if (_initialUrl == [NSNull null]) {
39      _initialUrl = nil;
40    }
41  }
42  return self;
43}
44
45#pragma mark - RCTEventEmitter methods
46
47- (NSArray<NSString *> *)supportedEvents
48{
49  return @[EXLinkingEventOpenUrl];
50}
51
52- (void)startObserving
53{
54  _hasListeners = YES;
55}
56
57- (void)stopObserving
58{
59  _hasListeners = NO;
60}
61
62#pragma mark - Linking methods
63
64- (void)dispatchOpenUrlEvent:(NSURL *)url
65{
66  if (!url || !url.absoluteString) {
67    RCTFatal(RCTErrorWithMessage([NSString stringWithFormat:@"Tried to open a deep link to an invalid url: %@", url]));
68    return;
69  }
70  if (_hasListeners) {
71    [self sendEventWithName:EXLinkingEventOpenUrl body:@{@"url": url.absoluteString}];
72  }
73}
74
75RCT_EXPORT_METHOD(openURL:(NSURL *)URL
76                  resolve:(RCTPromiseResolveBlock)resolve
77                  reject:(RCTPromiseRejectBlock)reject)
78{
79  if ([_kernelLinkingDelegate linkingModule:self shouldOpenExpoUrl:URL]) {
80    [_kernelLinkingDelegate linkingModule:self didOpenUrl:URL.absoluteString];
81    resolve(@YES);
82  } else {
83    [EXUtil performSynchronouslyOnMainThread:^{
84      [RCTSharedApplication() openURL:URL options:@{} completionHandler:^(BOOL success) {
85        if (success) {
86          resolve(nil);
87        } else {
88          reject(RCTErrorUnspecified, [NSString stringWithFormat:@"Unable to open URL: %@", URL], nil);
89        }
90      }];
91    }];
92  }
93}
94
95RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL
96                  resolve:(RCTPromiseResolveBlock)resolve
97                  reject:(__unused RCTPromiseRejectBlock)reject)
98{
99  __block BOOL canOpen = [_kernelLinkingDelegate linkingModule:self shouldOpenExpoUrl:URL];
100  if (!canOpen) {
101    [EXUtil performSynchronouslyOnMainThread:^{
102      canOpen = [RCTSharedApplication() canOpenURL:URL];
103    }];
104  }
105  resolve(@(canOpen));
106}
107
108RCT_EXPORT_METHOD(openSettings:(RCTPromiseResolveBlock)resolve
109                        reject:(RCTPromiseRejectBlock)reject)
110{
111  [EXUtil performSynchronouslyOnMainThread:^{
112    NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
113    [RCTSharedApplication() openURL:url options:@{} completionHandler:^(BOOL success) {
114      if (success) {
115        resolve(nil);
116      } else {
117        reject(RCTErrorUnspecified, @"Unable to open app settings", nil);
118      }
119    }];
120  }];
121}
122
123RCT_EXPORT_METHOD(getInitialURL:(RCTPromiseResolveBlock)resolve
124                  reject:(__unused RCTPromiseRejectBlock)reject)
125{
126  resolve(RCTNullIfNil(_initialUrl.absoluteString));
127}
128
129@end
130