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