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)initWithExperienceId:(NSString *)experienceId kernelServiceDelegate:(id)kernelServiceInstance params:(NSDictionary *)params
26{
27  if (self = [super initWithExperienceId:experienceId kernelServiceDelegate:kernelServiceInstance params:params]) {
28    _kernelLinkingDelegate = kernelServiceInstance;
29    _initialUrl = params[@"initialUri"];
30    if (_initialUrl == [NSNull null]) {
31      _initialUrl = nil;
32    }
33  }
34  return self;
35}
36
37#pragma mark - RCTEventEmitter methods
38
39- (NSArray<NSString *> *)supportedEvents
40{
41  return @[EXLinkingEventOpenUrl];
42}
43
44- (void)startObserving
45{
46  _hasListeners = YES;
47}
48
49- (void)stopObserving
50{
51  _hasListeners = NO;
52}
53
54#pragma mark - Linking methods
55
56- (void)dispatchOpenUrlEvent:(NSURL *)url
57{
58  if (!url || !url.absoluteString) {
59    RCTFatal(RCTErrorWithMessage([NSString stringWithFormat:@"Tried to open a deep link to an invalid url: %@", url]));
60    return;
61  }
62  if (_hasListeners) {
63    [self sendEventWithName:EXLinkingEventOpenUrl body:@{@"url": url.absoluteString}];
64  }
65}
66
67RCT_EXPORT_METHOD(openURL:(NSURL *)URL
68                  resolve:(RCTPromiseResolveBlock)resolve
69                  reject:(RCTPromiseRejectBlock)reject)
70{
71  if ([_kernelLinkingDelegate linkingModule:self shouldOpenExpoUrl:URL]) {
72    [_kernelLinkingDelegate linkingModule:self didOpenUrl:URL.absoluteString];
73    resolve(@YES);
74  } else {
75    [EXUtil performSynchronouslyOnMainThread:^{
76      [RCTSharedApplication() openURL:URL options:@{} completionHandler:^(BOOL success) {
77        if (success) {
78          resolve(nil);
79        } else {
80          reject(RCTErrorUnspecified, [NSString stringWithFormat:@"Unable to open URL: %@", URL], nil);
81        }
82      }];
83    }];
84  }
85}
86
87RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL
88                  resolve:(RCTPromiseResolveBlock)resolve
89                  reject:(__unused RCTPromiseRejectBlock)reject)
90{
91  __block BOOL canOpen = [_kernelLinkingDelegate linkingModule:self shouldOpenExpoUrl:URL];
92  if (!canOpen) {
93    [EXUtil performSynchronouslyOnMainThread:^{
94      canOpen = [RCTSharedApplication() canOpenURL:URL];
95    }];
96  }
97  resolve(@(canOpen));
98}
99
100RCT_EXPORT_METHOD(openSettings:(RCTPromiseResolveBlock)resolve
101                        reject:(RCTPromiseRejectBlock)reject)
102{
103  [EXUtil performSynchronouslyOnMainThread:^{
104    NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
105    [RCTSharedApplication() openURL:url options:@{} completionHandler:^(BOOL success) {
106      if (success) {
107        resolve(nil);
108      } else {
109        reject(RCTErrorUnspecified, @"Unable to open app settings", nil);
110      }
111    }];
112  }];
113}
114
115RCT_EXPORT_METHOD(getInitialURL:(RCTPromiseResolveBlock)resolve
116                  reject:(__unused RCTPromiseRejectBlock)reject)
117{
118  resolve(RCTNullIfNil(_initialUrl.absoluteString));
119}
120
121@end
122