expo-router 5.2.0-canary-20250729-d8899ae → 5.2.0-canary-20250811-5c940c0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/_ctx.android.js +1 -1
  2. package/_ctx.ios.js +1 -1
  3. package/_ctx.web.js +1 -1
  4. package/assets/modal.module.css +12 -3
  5. package/build/ExpoRoot.d.ts.map +1 -1
  6. package/build/ExpoRoot.js +12 -2
  7. package/build/ExpoRoot.js.map +1 -1
  8. package/build/Route.d.ts +9 -0
  9. package/build/Route.d.ts.map +1 -1
  10. package/build/Route.js.map +1 -1
  11. package/build/constants.d.ts +2 -0
  12. package/build/constants.d.ts.map +1 -1
  13. package/build/constants.js +3 -1
  14. package/build/constants.js.map +1 -1
  15. package/build/doctor/index.d.ts +1 -1
  16. package/build/doctor/index.d.ts.map +1 -1
  17. package/build/doctor/index.js +4 -1
  18. package/build/doctor/index.js.map +1 -1
  19. package/build/exports.d.ts +2 -2
  20. package/build/exports.d.ts.map +1 -1
  21. package/build/exports.js +6 -6
  22. package/build/exports.js.map +1 -1
  23. package/build/fork/native-stack/createNativeStackNavigator.d.ts +2 -2
  24. package/build/fork/native-stack/createNativeStackNavigator.d.ts.map +1 -1
  25. package/build/fork/native-stack/createNativeStackNavigator.js +82 -2
  26. package/build/fork/native-stack/createNativeStackNavigator.js.map +1 -1
  27. package/build/fork/useLinking.js +2 -2
  28. package/build/fork/useLinking.js.map +1 -1
  29. package/build/getLinkingConfig.d.ts +29 -2
  30. package/build/getLinkingConfig.d.ts.map +1 -1
  31. package/build/getLinkingConfig.js +35 -4
  32. package/build/getLinkingConfig.js.map +1 -1
  33. package/build/getRoutesCore.d.ts +1 -0
  34. package/build/getRoutesCore.d.ts.map +1 -1
  35. package/build/getRoutesCore.js +60 -0
  36. package/build/getRoutesCore.js.map +1 -1
  37. package/build/getServerManifest.d.ts +20 -1
  38. package/build/getServerManifest.d.ts.map +1 -1
  39. package/build/getServerManifest.js +8 -1
  40. package/build/getServerManifest.js.map +1 -1
  41. package/build/global-state/routeInfo.d.ts.map +1 -1
  42. package/build/global-state/routeInfo.js +12 -1
  43. package/build/global-state/routeInfo.js.map +1 -1
  44. package/build/global-state/router-store.d.ts.map +1 -1
  45. package/build/global-state/router-store.js +4 -0
  46. package/build/global-state/router-store.js.map +1 -1
  47. package/build/global-state/routing.d.ts +29 -1
  48. package/build/global-state/routing.d.ts.map +1 -1
  49. package/build/global-state/routing.js +78 -42
  50. package/build/global-state/routing.js.map +1 -1
  51. package/build/global-state/utils.d.ts +4 -0
  52. package/build/global-state/utils.d.ts.map +1 -0
  53. package/build/global-state/utils.js +29 -0
  54. package/build/global-state/utils.js.map +1 -0
  55. package/build/hooks.d.ts +1 -1
  56. package/build/hooks.d.ts.map +1 -1
  57. package/build/hooks.js +9 -4
  58. package/build/hooks.js.map +1 -1
  59. package/build/layouts/DrawerClient.d.ts +2 -2
  60. package/build/layouts/StackClient.d.ts +2 -2
  61. package/build/layouts/StackClient.d.ts.map +1 -1
  62. package/build/layouts/StackClient.js +11 -6
  63. package/build/layouts/StackClient.js.map +1 -1
  64. package/build/layouts/TabsClient.d.ts +3 -3
  65. package/build/layouts/withLayoutContext.d.ts.map +1 -1
  66. package/build/layouts/withLayoutContext.js +13 -0
  67. package/build/layouts/withLayoutContext.js.map +1 -1
  68. package/build/link/ExpoLink.d.ts.map +1 -1
  69. package/build/link/ExpoLink.js +3 -2
  70. package/build/link/ExpoLink.js.map +1 -1
  71. package/build/link/InternalLinkPreviewContext.d.ts +6 -0
  72. package/build/link/InternalLinkPreviewContext.d.ts.map +1 -0
  73. package/build/link/InternalLinkPreviewContext.js +6 -0
  74. package/build/link/InternalLinkPreviewContext.js.map +1 -0
  75. package/build/link/Link.d.ts +2 -67
  76. package/build/link/Link.d.ts.map +1 -1
  77. package/build/link/Link.js +5 -70
  78. package/build/link/Link.js.map +1 -1
  79. package/build/link/LinkWithPreview.d.ts +1 -46
  80. package/build/link/LinkWithPreview.d.ts.map +1 -1
  81. package/build/link/LinkWithPreview.js +20 -113
  82. package/build/link/LinkWithPreview.js.map +1 -1
  83. package/build/link/elements.d.ts +166 -0
  84. package/build/link/elements.d.ts.map +1 -0
  85. package/build/link/elements.js +172 -0
  86. package/build/link/elements.js.map +1 -0
  87. package/build/link/preview/HrefPreview.d.ts +1 -1
  88. package/build/link/preview/HrefPreview.d.ts.map +1 -1
  89. package/build/link/preview/HrefPreview.js +43 -7
  90. package/build/link/preview/HrefPreview.js.map +1 -1
  91. package/build/link/preview/LinkPreviewContext.d.ts +3 -2
  92. package/build/link/preview/LinkPreviewContext.d.ts.map +1 -1
  93. package/build/link/preview/LinkPreviewContext.js +3 -2
  94. package/build/link/preview/LinkPreviewContext.js.map +1 -1
  95. package/build/link/preview/native.d.ts +14 -6
  96. package/build/link/preview/native.d.ts.map +1 -1
  97. package/build/link/preview/native.js.map +1 -1
  98. package/build/link/preview/useNextScreenId.d.ts +8 -1
  99. package/build/link/preview/useNextScreenId.d.ts.map +1 -1
  100. package/build/link/preview/useNextScreenId.js +37 -32
  101. package/build/link/preview/useNextScreenId.js.map +1 -1
  102. package/build/link/preview/utils.d.ts +12 -0
  103. package/build/link/preview/utils.d.ts.map +1 -0
  104. package/build/link/preview/utils.js +66 -0
  105. package/build/link/preview/utils.js.map +1 -0
  106. package/build/modal/Modal.js +1 -1
  107. package/build/modal/Modal.js.map +1 -1
  108. package/build/modal/ModalsRenderer.js +1 -1
  109. package/build/modal/ModalsRenderer.js.map +1 -1
  110. package/build/modal/web/ModalStackRouteDrawer.web.d.ts.map +1 -1
  111. package/build/modal/web/ModalStackRouteDrawer.web.js +0 -1
  112. package/build/modal/web/ModalStackRouteDrawer.web.js.map +1 -1
  113. package/build/native-tabs/NativeBottomTabs/NativeBottomTabsNavigator.d.ts +25 -0
  114. package/build/native-tabs/NativeBottomTabs/NativeBottomTabsNavigator.d.ts.map +1 -0
  115. package/build/native-tabs/NativeBottomTabs/NativeBottomTabsNavigator.js +27 -0
  116. package/build/native-tabs/NativeBottomTabs/NativeBottomTabsNavigator.js.map +1 -0
  117. package/build/native-tabs/NativeBottomTabs/NativeBottomTabsRouter.d.ts +3 -0
  118. package/build/native-tabs/NativeBottomTabs/NativeBottomTabsRouter.d.ts.map +1 -0
  119. package/build/native-tabs/NativeBottomTabs/NativeBottomTabsRouter.js +65 -0
  120. package/build/native-tabs/NativeBottomTabs/NativeBottomTabsRouter.js.map +1 -0
  121. package/build/native-tabs/NativeBottomTabs/NativeTabs.d.ts +7 -0
  122. package/build/native-tabs/NativeBottomTabs/NativeTabs.d.ts.map +1 -0
  123. package/build/native-tabs/NativeBottomTabs/NativeTabs.js +9 -0
  124. package/build/native-tabs/NativeBottomTabs/NativeTabs.js.map +1 -0
  125. package/build/native-tabs/NativeBottomTabs/NativeTabsView.d.ts +103 -0
  126. package/build/native-tabs/NativeBottomTabs/NativeTabsView.d.ts.map +1 -0
  127. package/build/native-tabs/NativeBottomTabs/NativeTabsView.js +69 -0
  128. package/build/native-tabs/NativeBottomTabs/NativeTabsView.js.map +1 -0
  129. package/build/native-tabs/NativeBottomTabs/TabOptions.d.ts +40 -0
  130. package/build/native-tabs/NativeBottomTabs/TabOptions.d.ts.map +1 -0
  131. package/build/native-tabs/NativeBottomTabs/TabOptions.js +103 -0
  132. package/build/native-tabs/NativeBottomTabs/TabOptions.js.map +1 -0
  133. package/build/native-tabs/NativeBottomTabs/utils.d.ts +7 -0
  134. package/build/native-tabs/NativeBottomTabs/utils.d.ts.map +1 -0
  135. package/build/native-tabs/NativeBottomTabs/utils.js +21 -0
  136. package/build/native-tabs/NativeBottomTabs/utils.js.map +1 -0
  137. package/build/native-tabs/common/elements.d.ts +59 -0
  138. package/build/native-tabs/common/elements.d.ts.map +1 -0
  139. package/build/native-tabs/common/elements.js +15 -0
  140. package/build/native-tabs/common/elements.js.map +1 -0
  141. package/build/native-tabs/index.d.ts +3 -0
  142. package/build/native-tabs/index.d.ts.map +1 -0
  143. package/build/native-tabs/index.js +10 -0
  144. package/build/native-tabs/index.js.map +1 -0
  145. package/build/routes-manifest.d.ts +42 -0
  146. package/build/routes-manifest.d.ts.map +1 -1
  147. package/build/routes-manifest.js.map +1 -1
  148. package/build/testing-library/mock-config.d.ts +18 -0
  149. package/build/testing-library/mock-config.d.ts.map +1 -1
  150. package/build/testing-library/mock-config.js +4 -1
  151. package/build/testing-library/mock-config.js.map +1 -1
  152. package/build/ui/common.d.ts.map +1 -1
  153. package/build/ui/common.js +7 -6
  154. package/build/ui/common.js.map +1 -1
  155. package/build/useNavigation.d.ts.map +1 -1
  156. package/build/useNavigation.js +8 -5
  157. package/build/useNavigation.js.map +1 -1
  158. package/build/views/NoSSR.d.ts +5 -0
  159. package/build/views/NoSSR.d.ts.map +1 -0
  160. package/build/views/NoSSR.js +22 -0
  161. package/build/views/NoSSR.js.map +1 -0
  162. package/build/views/Screen.d.ts.map +1 -1
  163. package/build/views/Screen.js +4 -1
  164. package/build/views/Screen.js.map +1 -1
  165. package/build/views/Sitemap.d.ts.map +1 -1
  166. package/build/views/Sitemap.js +75 -2
  167. package/build/views/Sitemap.js.map +1 -1
  168. package/build/views/Unmatched.d.ts.map +1 -1
  169. package/build/views/Unmatched.js +14 -4
  170. package/build/views/Unmatched.js.map +1 -1
  171. package/ios/ExpoHead.podspec +10 -1
  172. package/ios/LinkPreview/LinkPreviewNativeActionView.swift +159 -21
  173. package/ios/LinkPreview/LinkPreviewNativeModule.swift +37 -5
  174. package/ios/LinkPreview/LinkPreviewNativeNavigation.h +37 -11
  175. package/ios/LinkPreview/LinkPreviewNativeNavigation.mm +110 -87
  176. package/ios/LinkPreview/LinkPreviewNativeNavigation.swift +136 -0
  177. package/ios/LinkPreview/LinkPreviewNativeView.swift +70 -70
  178. package/package.json +38 -9
  179. package/plugin/build/index.d.ts +2 -0
  180. package/plugin/options.json +5 -0
  181. package/plugin/src/index.ts +2 -0
  182. package/server.d.ts +2 -1
@@ -1,24 +1,50 @@
1
1
  // Copyright 2015-present 650 Industries. All rights reserved.
2
2
 
3
- #import <RNScreens/RNSScreenStack.h>
3
+ #import <RNScreens/RNSDismissibleModalProtocol.h>
4
+ #import <RNScreens/RNSTabBarController.h>
4
5
 
5
- @interface LinkPreviewNativeNavigation: NSObject
6
+ @interface LinkPreviewNativeNavigationObjC : NSObject
6
7
 
7
8
  /*
8
- * Updates the preloaded view with the given screenId and UIResponder.
9
- * This function will go through the responder's view hierarchy to find the screen view with the given screenId and activity state 0.
10
- */
11
- - (void)updatePreloadedView:(NSString *)screenId withUiResponder:(UIResponder *)responder;
9
+ * Pushes the previously preloaded view.
10
+ * This function will set the activity state of the preloaded screen view to 2
11
+ */
12
+ + (void)pushPreloadedView:(nonnull UIView *)view
13
+ ontoStackView:(nonnull UIView *)rawStackView;
12
14
 
13
15
  /*
14
- * Pushes the previously preloaded view.
15
- * This function will set the activity state of the preloaded screen view to 2
16
- */
17
- - (void)pushPreloadedView;
16
+ * Helper function to check if the view is a RNSScreenStackView. Can be used in
17
+ * Swift
18
+ */
19
+ + (BOOL)isRNSScreenStackView:(UIView *)view;
20
+ /*
21
+ * Helper function to get all screen IDs from a RNSScreenStackView.
22
+ */
23
+ + (nonnull NSArray<NSString *> *)getStackViewScreenIds:(UIView *)view;
24
+ /*
25
+ * Helper function to get all screen views from a RNSScreenStackView.
26
+ */
27
+ + (nonnull NSArray<UIView *> *)getScreenViews:(UIView *)view;
28
+ /*
29
+ * Helper function to get the screen ID of a RNSScreenView.
30
+ */
31
+ + (nonnull NSString *)getScreenId:(UIView *)view;
32
+
33
+ + (nonnull NSString *)getTabKey:(UIView *)view;
34
+
35
+ + (BOOL)isRNSBottomTabsScreenComponentView:(UIView *)view;
36
+
37
+ + (BOOL)isRNSTabBarController:(UIView *)view;
38
+
39
+ + (nullable RNSTabBarController *)getBottomTabControllerFromView:(UIView *)view;
40
+
41
+ + (BOOL)isRNSBottomTabsHostComponentView:(UIView *)view;
42
+
43
+ + (nullable UIView *)getTab:(UITabBarController *)controller
44
+ withKey:(NSString *)key;
18
45
 
19
46
  @end
20
47
 
21
48
  @protocol LinkPreviewModalDismissible <RNSDismissibleModalProtocol>
22
49
  @required
23
50
  @end
24
-
@@ -6,12 +6,116 @@
6
6
  #import <RNScreens/RNSScreen.h>
7
7
  #import <RNScreens/RNSScreenStack.h>
8
8
 
9
- @implementation LinkPreviewNativeNavigation {
10
- RNSScreenView *preloadedScreenView;
11
- RNSScreenStackView *stackView;
9
+ @implementation LinkPreviewNativeNavigationObjC {
12
10
  }
13
11
 
14
- - (void)pushPreloadedView {
12
+ + (BOOL)isRNSScreenStackView:(UIView *)view {
13
+ if (view != nil) {
14
+ return [view isKindOfClass:[RNSScreenStackView class]];
15
+ }
16
+
17
+ return NO;
18
+ }
19
+
20
+ + (BOOL)isRNSBottomTabsScreenComponentView:(UIView *)view {
21
+ if (view != nil) {
22
+ return [view isKindOfClass:[RNSBottomTabsScreenComponentView class]];
23
+ }
24
+
25
+ return NO;
26
+ }
27
+
28
+ + (BOOL)isRNSBottomTabsHostComponentView:(UIView *)view {
29
+ if (view != nil) {
30
+ return [view isKindOfClass:[RNSBottomTabsHostComponentView class]];
31
+ }
32
+
33
+ return NO;
34
+ }
35
+
36
+ + (nullable UITabBarController *)getBottomTabControllerFromView:(UIView *)view {
37
+ if ([view isKindOfClass:[RNSBottomTabsScreenComponentView class]]) {
38
+ RNSBottomTabsScreenComponentView *bottomTabsView =
39
+ (RNSBottomTabsScreenComponentView *)view;
40
+ UIViewController *reactVC = [bottomTabsView reactViewController];
41
+ if (reactVC != nil &&
42
+ [reactVC.tabBarController isKindOfClass:[RNSTabBarController class]]) {
43
+ RNSTabBarController *tabBarController =
44
+ (RNSTabBarController *)reactVC.tabBarController;
45
+ return tabBarController;
46
+ }
47
+ }
48
+ if ([view isKindOfClass:[RNSBottomTabsHostComponentView class]]) {
49
+ RNSBottomTabsHostComponentView *bottomTabsView =
50
+ (RNSBottomTabsHostComponentView *)view;
51
+ return [bottomTabsView controller];
52
+ }
53
+ return nil;
54
+ }
55
+
56
+ + (nullable UIView *)getTab:(UITabBarController *)controller
57
+ withKey:(NSString *)key {
58
+ if (controller != nil) {
59
+ for (UIViewController *subcontroller in controller.viewControllers) {
60
+ if ([subcontroller.view
61
+ isKindOfClass:[RNSBottomTabsScreenComponentView class]]) {
62
+ if (((RNSBottomTabsScreenComponentView *)subcontroller.view).tabKey ==
63
+ key) {
64
+ return subcontroller.view;
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ return nil;
71
+ }
72
+
73
+ + (nonnull NSArray<NSString *> *)getStackViewScreenIds:(UIView *)view {
74
+ if (view != nil && [view isKindOfClass:[RNSScreenStackView class]]) {
75
+ RNSScreenStackView *stackView = (RNSScreenStackView *)view;
76
+ return stackView.screenIds;
77
+ }
78
+ return @[];
79
+ }
80
+
81
+ + (nonnull NSArray<UIView *> *)getScreenViews:(UIView *)view {
82
+ if (view != nil && [view isKindOfClass:[RNSScreenStackView class]]) {
83
+ RNSScreenStackView *stackView = (RNSScreenStackView *)view;
84
+ return stackView.reactSubviews;
85
+ }
86
+ return @[];
87
+ }
88
+
89
+ + (nonnull NSString *)getScreenId:(UIView *)view {
90
+ if (view != nil && [view isKindOfClass:[RNSScreenView class]]) {
91
+ RNSScreenView *screenView = (RNSScreenView *)view;
92
+ return screenView.screenId;
93
+ }
94
+ return nil;
95
+ }
96
+
97
+ + (nonnull NSString *)getTabKey:(UIView *)view {
98
+ if (view != nil &&
99
+ [view isKindOfClass:[RNSBottomTabsScreenComponentView class]]) {
100
+ RNSBottomTabsScreenComponentView *tabScreenView =
101
+ (RNSBottomTabsScreenComponentView *)view;
102
+ return tabScreenView.tabKey;
103
+ }
104
+ return @"";
105
+ }
106
+
107
+ + (void)pushPreloadedView:(nonnull UIView *)view
108
+ ontoStackView:(nonnull UIView *)rawStackView {
109
+ if (![LinkPreviewNativeNavigationObjC isRNSScreenStackView:rawStackView]) {
110
+ NSLog(@"ExpoRouter: The provided stack view is not a RNSScreenStackView.");
111
+ return;
112
+ }
113
+ if (![view isKindOfClass:[RNSScreenView class]]) {
114
+ NSLog(@"ExpoRouter: The provided view is not a RNSScreenView.");
115
+ return;
116
+ }
117
+ RNSScreenStackView *stackView = (RNSScreenStackView *)rawStackView;
118
+ RNSScreenView *preloadedScreenView = (RNSScreenView *)view;
15
119
  if (preloadedScreenView != nil && stackView != nil) {
16
120
  // Instead of pushing the preloaded screen view, we set its activity state
17
121
  // React native screens will then handle the rest.
@@ -41,7 +145,8 @@
41
145
  [firstChild isKindOfClass:[RNSScreenView class]]) {
42
146
  RNSScreenView *screenContentView = (RNSScreenView *)firstChild;
43
147
  // Same as above, we let React Native Screens handle the transition.
44
- // We need to set the activity of inner screen as well, because its react value is the same as the preloaded screen - 0.
148
+ // We need to set the activity of inner screen as well, because its
149
+ // react value is the same as the preloaded screen - 0.
45
150
  // https://github.com/software-mansion/react-native-screens/blob/8b82e081e8fdfa6e0864821134bda9e87a745b00/src/components/ScreenStackItem.tsx#L151
46
151
  [screenContentView setActivityState:2];
47
152
  [innerScreenStack markChildUpdated];
@@ -56,86 +161,4 @@
56
161
  }
57
162
  }
58
163
 
59
- - (void)updatePreloadedView:(nullable NSString *)screenId
60
- withUiResponder:(nonnull UIResponder *)responder {
61
- if (screenId != nil && [screenId length] > 0) {
62
- if ([self setPreloadedScreenViewWithScreenId:screenId
63
- withUiResponder:responder]) {
64
- NSLog(@"ExpoRouter: Preloaded screen view updated.");
65
- } else {
66
- NSLog(@"ExpoRouter: No native screen view found with screenId: %@",
67
- screenId);
68
- }
69
- } else {
70
- preloadedScreenView = nil;
71
- }
72
- }
73
-
74
- - (nonnull NSArray<RNSScreenStackView *> *)
75
- findAllScreenStackViewsInResponderChain:(nonnull UIResponder *)responder {
76
- NSMutableArray<RNSScreenStackView *> *stackViews = [NSMutableArray array];
77
-
78
- while (responder) {
79
- responder = [responder nextResponder];
80
- if ([responder isKindOfClass:[RNSScreenStackView class]]) {
81
- [stackViews addObject:(RNSScreenStackView *)responder];
82
- }
83
- }
84
-
85
- return stackViews;
86
- }
87
-
88
- - (nonnull NSArray<RNSScreenView *> *)extractScreenViewsFromSubviews:
89
- (nonnull NSArray<UIView *> *)subviews {
90
- NSMutableArray<RNSScreenView *> *screenViews = [NSMutableArray array];
91
-
92
- for (UIView *subview in subviews) {
93
- if ([subview isKindOfClass:[RNSScreenView class]]) {
94
- [screenViews addObject:(RNSScreenView *)subview];
95
- }
96
- }
97
- return screenViews;
98
- }
99
-
100
- - (BOOL)setPreloadedScreenViewWithScreenId:(nonnull NSString *)screenId
101
- withUiResponder:(nonnull UIResponder *)responder {
102
- NSArray<RNSScreenStackView *> *stacks =
103
- [self findAllScreenStackViewsInResponderChain:responder];
104
-
105
- for (RNSScreenStackView *stack in stacks) {
106
- if ([stack.screenIds containsObject:screenId] &&
107
- [self setPreloadedScreenViewWithScreenId:screenId
108
- withStackView:stack]) {
109
- return YES;
110
- }
111
- }
112
- return NO;
113
- }
114
-
115
- - (BOOL)setPreloadedScreenViewWithScreenId:(nonnull NSString *)screenId
116
- withStackView:(nonnull RNSScreenStackView *)stack {
117
- NSArray<RNSScreenView *> *screenSubviews =
118
- [self extractScreenViewsFromSubviews:stack.reactSubviews];
119
- RNSScreenView *screenView = [self findPreloadedScreenView:screenSubviews
120
- withScreenId:screenId];
121
- if (screenView != nil) {
122
- preloadedScreenView = screenView;
123
- stackView = stack;
124
- return YES;
125
- }
126
- return NO;
127
- }
128
-
129
- - (nullable RNSScreenView *)
130
- findPreloadedScreenView:(nonnull NSArray<RNSScreenView *> *)screenViews
131
- withScreenId:(nonnull NSString *)screenId {
132
- for (RNSScreenView *screenView in screenViews) {
133
- if (screenView.activityState == 0 &&
134
- [screenView.screenId isEqualToString:screenId]) {
135
- return screenView;
136
- }
137
- }
138
- return nil;
139
- }
140
-
141
164
  @end
@@ -0,0 +1,136 @@
1
+ import UIKit
2
+
3
+ struct TabChangeCommand {
4
+ weak var tabBarController: UITabBarController?
5
+ let tabIndex: Int
6
+ }
7
+
8
+ internal class LinkPreviewNativeNavigation {
9
+ private var preloadedView: UIView?
10
+ private var preloadedStackView: UIView?
11
+ private var tabChangeCommands: [TabChangeCommand] = []
12
+
13
+ init() {}
14
+
15
+ func pushPreloadedView() {
16
+ self.tabChangeCommands.forEach { command in
17
+ command.tabBarController?.selectedIndex = command.tabIndex
18
+ }
19
+ guard let preloadedView,
20
+ let preloadedStackView
21
+ else {
22
+ return
23
+ }
24
+ LinkPreviewNativeNavigationObjC.pushPreloadedView(
25
+ preloadedView, ontoStackView: preloadedStackView)
26
+ }
27
+
28
+ func updatePreloadedView(screenId: String?, tabPath: TabPathPayload?, responder: UIView) {
29
+ self.tabChangeCommands = []
30
+ let oldTabKeys = tabPath?.path.map { $0.oldTabKey } ?? []
31
+ let stackOrTabView = findStackViewWithScreenIdOrTabBarController(
32
+ screenId: screenId, tabKeys: oldTabKeys, responder: responder)
33
+ if let stackOrTabView = stackOrTabView {
34
+ if LinkPreviewNativeNavigationObjC.isRNSBottomTabsScreenComponentView(stackOrTabView) {
35
+ let tabView = stackOrTabView
36
+ let newTabKeys = tabPath?.path.map { $0.newTabKey } ?? []
37
+ let stackView = findStackViewWithScreenIdInSubViews(
38
+ screenId: screenId, tabKeys: newTabKeys, rootView: tabView)
39
+ if let stackView = stackView {
40
+ let screenViews = LinkPreviewNativeNavigationObjC.getScreenViews(stackView)
41
+ if let screenView = screenViews.first(where: {
42
+ LinkPreviewNativeNavigationObjC.getScreenId($0) == screenId
43
+ }) {
44
+ preloadedView = screenView
45
+ preloadedStackView = stackView
46
+ print("LinkPreviewNativeNavigation: Preloaded view for screenId \(screenId).")
47
+ }
48
+ }
49
+ } else if LinkPreviewNativeNavigationObjC.isRNSScreenStackView(stackOrTabView) {
50
+ let stackView = stackOrTabView
51
+ let screenViews = LinkPreviewNativeNavigationObjC.getScreenViews(stackView)
52
+ if let screenView = screenViews.first(where: {
53
+ LinkPreviewNativeNavigationObjC.getScreenId($0) == screenId
54
+ }) {
55
+ preloadedView = screenView
56
+ preloadedStackView = stackView
57
+ print("LinkPreviewNativeNavigation: Preloaded view for screenId \(screenId).")
58
+ }
59
+ }
60
+ } else {
61
+ print("LinkPreviewNativeNavigation: No stack view found for screenId \(screenId).")
62
+ }
63
+ }
64
+
65
+ private func findStackViewWithScreenIdInSubViews(
66
+ screenId: String?, tabKeys: [String], rootView: UIView
67
+ ) -> UIView? {
68
+ if LinkPreviewNativeNavigationObjC.isRNSScreenStackView(rootView),
69
+ let _screenId = screenId {
70
+ let screenIds = LinkPreviewNativeNavigationObjC.getStackViewScreenIds(rootView)
71
+ if screenIds.contains(_screenId) {
72
+ return rootView
73
+ }
74
+ } else if LinkPreviewNativeNavigationObjC.isRNSBottomTabsScreenComponentView(rootView)
75
+ || LinkPreviewNativeNavigationObjC.isRNSBottomTabsHostComponentView(rootView) {
76
+ let tabBarController = LinkPreviewNativeNavigationObjC.getBottomTabController(from: rootView)
77
+ if let tabBarController = tabBarController {
78
+ let views = tabBarController.viewControllers?.compactMap { $0.view } ?? []
79
+ let enumeratedViews = views.enumerated()
80
+ if let (tabIndex, tabView) =
81
+ enumeratedViews
82
+ .first(where: { _, view in
83
+ LinkPreviewNativeNavigationObjC.isRNSBottomTabsScreenComponentView(view)
84
+ && tabKeys.contains(LinkPreviewNativeNavigationObjC.getTabKey(view))
85
+ }) {
86
+ self.tabChangeCommands.append(
87
+ TabChangeCommand(tabBarController: tabBarController, tabIndex: tabIndex))
88
+ let test = tabBarController.viewControllers
89
+
90
+ for subview in tabView.subviews {
91
+ let result = findStackViewWithScreenIdInSubViews(
92
+ screenId: screenId, tabKeys: tabKeys, rootView: subview)
93
+ if result != nil {
94
+ return result
95
+ }
96
+ }
97
+ }
98
+ }
99
+ } else {
100
+ for subview in rootView.subviews {
101
+ let result = findStackViewWithScreenIdInSubViews(
102
+ screenId: screenId, tabKeys: tabKeys, rootView: subview)
103
+ if result != nil {
104
+ return result
105
+ }
106
+ }
107
+ }
108
+
109
+ return nil
110
+ }
111
+
112
+ private func findStackViewWithScreenIdOrTabBarController(
113
+ screenId: String?, tabKeys: [String], responder: UIView
114
+ ) -> UIView? {
115
+ var currentResponder: UIResponder? = responder
116
+
117
+ while let nextResponder = currentResponder?.next {
118
+ if let view = nextResponder as? UIView,
119
+ LinkPreviewNativeNavigationObjC.isRNSScreenStackView(view),
120
+ let _screenId = screenId {
121
+ let screenIds = LinkPreviewNativeNavigationObjC.getStackViewScreenIds(view)
122
+ if screenIds.contains(_screenId) {
123
+ return view
124
+ }
125
+ } else if let tabView = nextResponder as? UIView,
126
+ LinkPreviewNativeNavigationObjC.isRNSBottomTabsScreenComponentView(tabView) {
127
+ let tabKey = LinkPreviewNativeNavigationObjC.getTabKey(tabView)
128
+ if tabKeys.contains(tabKey) {
129
+ return tabView
130
+ }
131
+ }
132
+ currentResponder = nextResponder
133
+ }
134
+ return nil
135
+ }
136
+ }
@@ -1,10 +1,20 @@
1
1
  import ExpoModulesCore
2
2
 
3
- class NativeLinkPreviewView: ExpoView, UIContextMenuInteractionDelegate, LinkPreviewModalDismissible {
3
+ class NativeLinkPreviewView: ExpoView, UIContextMenuInteractionDelegate,
4
+ LinkPreviewModalDismissible, LinkPreviewMenuUpdatable {
4
5
  private var trigger: NativeLinkPreviewTrigger?
5
6
  private var preview: NativeLinkPreviewContentView?
6
7
  private var interaction: UIContextMenuInteraction?
7
- private var nextScreenId: String?
8
+ var nextScreenId: String? {
9
+ didSet {
10
+ performUpdateOfPreloadedView()
11
+ }
12
+ }
13
+ var tabPath: TabPathPayload? {
14
+ didSet {
15
+ performUpdateOfPreloadedView()
16
+ }
17
+ }
8
18
  private var actions: [LinkPreviewNativeActionView] = []
9
19
 
10
20
  private let linkPreviewNativeNavigation = LinkPreviewNativeNavigation()
@@ -15,7 +25,6 @@ class NativeLinkPreviewView: ExpoView, UIContextMenuInteractionDelegate, LinkPre
15
25
  let onDidPreviewOpen = EventDispatcher()
16
26
  let onPreviewWillClose = EventDispatcher()
17
27
  let onPreviewDidClose = EventDispatcher()
18
- let onActionSelected = EventDispatcher()
19
28
 
20
29
  required init(appContext: AppContext? = nil) {
21
30
  super.init(appContext: appContext)
@@ -30,56 +39,62 @@ class NativeLinkPreviewView: ExpoView, UIContextMenuInteractionDelegate, LinkPre
30
39
 
31
40
  // MARK: - Props
32
41
 
33
- func setNextScreenId(_ screenId: String) {
34
- self.nextScreenId = screenId
35
- linkPreviewNativeNavigation.updatePreloadedView(screenId, with: self)
42
+ func performUpdateOfPreloadedView() {
43
+ if nextScreenId == nil || tabPath?.path.isEmpty != false {
44
+ return
45
+ }
46
+ print("Perform update \(nextScreenId) \(tabPath) \(self)")
47
+ linkPreviewNativeNavigation.updatePreloadedView(
48
+ screenId: nextScreenId, tabPath: tabPath, responder: self)
36
49
  }
37
50
 
38
51
  // MARK: - Children
39
- #if RCT_NEW_ARCH_ENABLED
40
- override func mountChildComponentView(_ childComponentView: UIView, index: Int) {
41
- if let triggerView = childComponentView as? NativeLinkPreviewTrigger {
42
- trigger = triggerView
43
- if let interaction = self.interaction, self.preview != nil {
44
- trigger?.addInteraction(interaction)
52
+ #if RCT_NEW_ARCH_ENABLED
53
+ override func mountChildComponentView(_ childComponentView: UIView, index: Int) {
54
+ if let triggerView = childComponentView as? NativeLinkPreviewTrigger {
55
+ trigger = triggerView
56
+ if let interaction = self.interaction {
57
+ triggerView.addInteraction(interaction)
58
+ }
59
+ super.mountChildComponentView(childComponentView, index: index)
60
+ } else if let previewView = childComponentView as? NativeLinkPreviewContentView {
61
+ preview = previewView
62
+ if let interaction = self.interaction, let trigger = self.trigger {
63
+ trigger.addInteraction(interaction)
64
+ }
65
+ } else if let actionView = childComponentView as? LinkPreviewNativeActionView {
66
+ actionView.parentMenuUpdatable = self
67
+ actions.append(actionView)
68
+ } else {
69
+ print(
70
+ "ExpoRouter: Unknown child component view (\(childComponentView)) mounted to NativeLinkPreviewView"
71
+ )
45
72
  }
46
- super.mountChildComponentView(childComponentView, index: index)
47
- } else if let previewView = childComponentView as? NativeLinkPreviewContentView {
48
- preview = previewView
49
- if let interaction = self.interaction, let trigger = self.trigger {
50
- trigger.addInteraction(interaction)
51
- }
52
- } else if let actionView = childComponentView as? LinkPreviewNativeActionView {
53
- actions.append(actionView)
54
- } else {
55
- print(
56
- "ExpoRouter: Unknown child component view (\(childComponentView)) mounted to NativeLinkPreviewView"
57
- )
58
73
  }
59
- }
60
74
 
61
- override func unmountChildComponentView(_ child: UIView, index: Int) {
62
- if child is NativeLinkPreviewTrigger {
63
- if let interaction = self.interaction {
64
- trigger?.removeInteraction(interaction)
75
+ override func unmountChildComponentView(_ child: UIView, index: Int) {
76
+ if child is NativeLinkPreviewTrigger {
77
+ if let interaction = self.interaction {
78
+ trigger?.removeInteraction(interaction)
79
+ }
80
+ trigger = nil
81
+ super.unmountChildComponentView(child, index: index)
82
+ } else if child is NativeLinkPreviewContentView {
83
+ preview = nil
84
+ if let interaction = self.interaction {
85
+ trigger?.removeInteraction(interaction)
86
+ }
87
+ } else if let actionView = child as? LinkPreviewNativeActionView {
88
+ actions.removeAll(where: {
89
+ $0 == actionView
90
+ })
91
+ } else {
92
+ print(
93
+ "ExpoRouter: Unknown child component view (\(child)) unmounted from NativeLinkPreviewView"
94
+ )
65
95
  }
66
- trigger = nil
67
- super.unmountChildComponentView(child, index: index)
68
- } else if child is NativeLinkPreviewContentView {
69
- preview = nil
70
- if let interaction = self.interaction {
71
- trigger?.removeInteraction(interaction)
72
- }
73
- } else if let actionView = child as? LinkPreviewNativeActionView {
74
- actions.removeAll(where: {
75
- $0 == actionView
76
- })
77
- } else {
78
- print(
79
- "ExpoRouter: Unknown child component view (\(child)) unmounted from NativeLinkPreviewView")
80
96
  }
81
- }
82
- #endif
97
+ #endif
83
98
 
84
99
  // MARK: - UIContextMenuInteractionDelegate
85
100
 
@@ -154,9 +169,9 @@ class NativeLinkPreviewView: ExpoView, UIContextMenuInteractionDelegate, LinkPre
154
169
 
155
170
  // MARK: - Context Menu Helpers
156
171
 
157
- private func createPreviewViewController() -> UIViewController {
172
+ private func createPreviewViewController() -> UIViewController? {
158
173
  guard let preview = preview else {
159
- return UIViewController()
174
+ return nil
160
175
  }
161
176
 
162
177
  let vc = PreviewViewController(linkPreviewNativePreview: preview)
@@ -167,38 +182,23 @@ class NativeLinkPreviewView: ExpoView, UIContextMenuInteractionDelegate, LinkPre
167
182
  return vc
168
183
  }
169
184
 
185
+ func updateMenu() {
186
+ self.interaction?.updateVisibleMenu { _ in
187
+ self.createContextMenu()
188
+ }
189
+ }
190
+
170
191
  private func createContextMenu() -> UIMenu {
171
- if actions.count == 1, let menu = convertActionViewToUiAction(actions[0]) as? UIMenu {
192
+ if actions.count == 1, let menu = actions[0].uiAction as? UIMenu {
172
193
  return menu
173
194
  }
174
195
  return UIMenu(
175
196
  title: "",
176
197
  children: actions.map { action in
177
- self.convertActionViewToUiAction(action)
198
+ action.uiAction
178
199
  }
179
200
  )
180
201
  }
181
-
182
- private func convertActionViewToUiAction(_ action: LinkPreviewNativeActionView) -> UIMenuElement {
183
- if !action.subActions.isEmpty {
184
- let subActions = action.subActions.map { subAction in
185
- self.convertActionViewToUiAction(subAction)
186
- }
187
- return UIMenu(
188
- title: action.title,
189
- image: action.icon.flatMap { UIImage(systemName: $0) },
190
- children: subActions
191
- )
192
- }
193
- return UIAction(
194
- title: action.title,
195
- image: action.icon.flatMap { UIImage(systemName: $0) }
196
- ) { _ in
197
- self.onActionSelected([
198
- "id": action.id
199
- ])
200
- }
201
- }
202
202
  }
203
203
 
204
204
  class PreviewViewController: UIViewController {