react-native-navigation 8.8.5 → 8.8.6-snapshot.2571
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.
- package/android/src/main/java/com/reactnativenavigation/NavigationActivity.java +14 -1
- package/android/src/main/java/com/reactnativenavigation/NavigationApplication.java +3 -0
- package/android/src/main/java/com/reactnativenavigation/NavigationPackage.kt +27 -8
- package/android/src/main/java/com/reactnativenavigation/customrow/BottomTabsCustomRow.kt +262 -0
- package/android/src/main/java/com/reactnativenavigation/customrow/BottomTabsCustomRowAttacher.kt +205 -0
- package/android/src/main/java/com/reactnativenavigation/customrow/BottomTabsCustomRowConfigStore.kt +32 -0
- package/android/src/main/java/com/reactnativenavigation/customrow/BottomTabsCustomRowLayout.kt +139 -0
- package/android/src/main/java/com/reactnativenavigation/customrow/BottomTabsCustomRowModule.kt +37 -0
- package/android/src/main/java/com/reactnativenavigation/customrow/BottomTabsCustomRowOptions.kt +68 -0
- package/android/src/main/java/com/reactnativenavigation/options/BottomTabOptions.java +4 -1
- package/android/src/main/java/com/reactnativenavigation/options/NavigationBarOptions.java +19 -1
- package/android/src/main/java/com/reactnativenavigation/react/ReactView.java +13 -0
- package/android/src/main/java/com/reactnativenavigation/react/events/ComponentType.java +2 -1
- package/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt +63 -9
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabPresenter.java +28 -0
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java +77 -6
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +1 -0
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewController.java +2 -5
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java +33 -13
- package/android/src/main/java/com/reactnativenavigation/views/bottomtabs/BottomTabs.java +76 -0
- package/android/src/main/java/com/reactnativenavigation/views/bottomtabs/CustomBottomTabItemView.kt +73 -0
- package/android/src/test/java/com/reactnativenavigation/presentation/PresenterTest.java +14 -0
- package/android/src/test/java/com/reactnativenavigation/utils/SystemUiUtilsTest.kt +64 -1
- package/ios/ARCHITECTURE.md +5 -0
- package/ios/BottomTabPresenter.h +7 -0
- package/ios/BottomTabPresenter.mm +27 -0
- package/ios/RNNAppDelegate.h +16 -0
- package/ios/RNNAppDelegate.mm +73 -0
- package/ios/RNNBottomTabOptions.h +2 -0
- package/ios/RNNBottomTabOptions.mm +5 -1
- package/ios/RNNBottomTabsController.h +2 -0
- package/ios/RNNBottomTabsController.mm +209 -1
- package/ios/RNNBottomTabsCustomRow.h +57 -0
- package/ios/RNNBottomTabsCustomRow.mm +252 -0
- package/ios/RNNBottomTabsCustomRowOptions.h +42 -0
- package/ios/RNNBottomTabsCustomRowOptions.mm +37 -0
- package/ios/RNNBottomTabsOptions.h +2 -0
- package/ios/RNNBottomTabsOptions.mm +2 -0
- package/ios/RNNComponentViewCreator.h +2 -1
- package/ios/RNNCustomTabBarItemView.h +26 -0
- package/ios/RNNCustomTabBarItemView.mm +83 -0
- package/ios/RNNReactRootViewCreator.mm +1 -0
- package/ios/RNNViewControllerFactory.mm +1 -0
- package/ios/ReactNativeNavigation.xcodeproj/project.pbxproj +24 -0
- package/lib/module/ARCHITECTURE.md +30 -0
- package/lib/module/Navigation.js +34 -1
- package/lib/module/Navigation.js.map +1 -1
- package/lib/module/NavigationDelegate.js +21 -0
- package/lib/module/NavigationDelegate.js.map +1 -1
- package/lib/module/adapters/AndroidCustomRowForwarder.js +75 -0
- package/lib/module/adapters/AndroidCustomRowForwarder.js.map +1 -0
- package/lib/module/commands/Commands.js +8 -0
- package/lib/module/commands/Commands.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/interfaces/Options.js.map +1 -1
- package/lib/module/linking/DeferredLinkQueue.js +52 -0
- package/lib/module/linking/DeferredLinkQueue.js.map +1 -0
- package/lib/module/linking/DeferredLinkQueue.test.js +54 -0
- package/lib/module/linking/DeferredLinkQueue.test.js.map +1 -0
- package/lib/module/linking/LinkingHandler.js +139 -0
- package/lib/module/linking/LinkingHandler.js.map +1 -0
- package/lib/module/linking/LinkingHandler.test.js +384 -0
- package/lib/module/linking/LinkingHandler.test.js.map +1 -0
- package/lib/module/linking/ModalLayoutBuilder.js +56 -0
- package/lib/module/linking/ModalLayoutBuilder.js.map +1 -0
- package/lib/module/linking/ModalLayoutBuilder.test.js +154 -0
- package/lib/module/linking/ModalLayoutBuilder.test.js.map +1 -0
- package/lib/module/linking/RouteMatcher.js +104 -0
- package/lib/module/linking/RouteMatcher.js.map +1 -0
- package/lib/module/linking/RouteMatcher.test.js +164 -0
- package/lib/module/linking/RouteMatcher.test.js.map +1 -0
- package/lib/module/linking/URLParser.js +56 -0
- package/lib/module/linking/URLParser.js.map +1 -0
- package/lib/module/linking/URLParser.test.js +100 -0
- package/lib/module/linking/URLParser.test.js.map +1 -0
- package/lib/module/linking/types.js +4 -0
- package/lib/module/linking/types.js.map +1 -0
- package/lib/typescript/Navigation.d.ts +22 -0
- package/lib/typescript/Navigation.d.ts.map +1 -1
- package/lib/typescript/NavigationDelegate.d.ts +13 -0
- package/lib/typescript/NavigationDelegate.d.ts.map +1 -1
- package/lib/typescript/adapters/AndroidCustomRowForwarder.d.ts +23 -0
- package/lib/typescript/adapters/AndroidCustomRowForwarder.d.ts.map +1 -0
- package/lib/typescript/commands/Commands.d.ts +1 -0
- package/lib/typescript/commands/Commands.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/interfaces/Options.d.ts +90 -0
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/lib/typescript/linking/DeferredLinkQueue.d.ts +26 -0
- package/lib/typescript/linking/DeferredLinkQueue.d.ts.map +1 -0
- package/lib/typescript/linking/DeferredLinkQueue.test.d.ts +2 -0
- package/lib/typescript/linking/DeferredLinkQueue.test.d.ts.map +1 -0
- package/lib/typescript/linking/LinkingHandler.d.ts +71 -0
- package/lib/typescript/linking/LinkingHandler.d.ts.map +1 -0
- package/lib/typescript/linking/LinkingHandler.test.d.ts +2 -0
- package/lib/typescript/linking/LinkingHandler.test.d.ts.map +1 -0
- package/lib/typescript/linking/ModalLayoutBuilder.d.ts +21 -0
- package/lib/typescript/linking/ModalLayoutBuilder.d.ts.map +1 -0
- package/lib/typescript/linking/ModalLayoutBuilder.test.d.ts +2 -0
- package/lib/typescript/linking/ModalLayoutBuilder.test.d.ts.map +1 -0
- package/lib/typescript/linking/RouteMatcher.d.ts +23 -0
- package/lib/typescript/linking/RouteMatcher.d.ts.map +1 -0
- package/lib/typescript/linking/RouteMatcher.test.d.ts +2 -0
- package/lib/typescript/linking/RouteMatcher.test.d.ts.map +1 -0
- package/lib/typescript/linking/URLParser.d.ts +16 -0
- package/lib/typescript/linking/URLParser.d.ts.map +1 -0
- package/lib/typescript/linking/URLParser.test.d.ts +2 -0
- package/lib/typescript/linking/URLParser.test.d.ts.map +1 -0
- package/lib/typescript/linking/types.d.ts +107 -0
- package/lib/typescript/linking/types.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +30 -0
- package/src/Navigation.ts +36 -1
- package/src/NavigationDelegate.ts +22 -0
- package/src/adapters/AndroidCustomRowForwarder.ts +83 -0
- package/src/commands/Commands.ts +15 -0
- package/src/index.ts +1 -0
- package/src/interfaces/Options.ts +92 -0
- package/src/linking/DeferredLinkQueue.test.ts +60 -0
- package/src/linking/DeferredLinkQueue.ts +55 -0
- package/src/linking/LinkingHandler.test.ts +332 -0
- package/src/linking/LinkingHandler.ts +169 -0
- package/src/linking/ModalLayoutBuilder.test.ts +105 -0
- package/src/linking/ModalLayoutBuilder.ts +60 -0
- package/src/linking/RouteMatcher.test.ts +128 -0
- package/src/linking/RouteMatcher.ts +126 -0
- package/src/linking/URLParser.test.ts +105 -0
- package/src/linking/URLParser.ts +62 -0
- package/src/linking/types.ts +115 -0
|
@@ -13,9 +13,11 @@ import android.view.ViewGroup;
|
|
|
13
13
|
import androidx.annotation.NonNull;
|
|
14
14
|
import androidx.annotation.RestrictTo;
|
|
15
15
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
|
16
|
-
import androidx.core.
|
|
16
|
+
import androidx.core.view.ViewCompat;
|
|
17
17
|
import androidx.core.view.WindowInsetsCompat;
|
|
18
18
|
|
|
19
|
+
import android.util.Log;
|
|
20
|
+
|
|
19
21
|
import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
|
|
20
22
|
import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
|
|
21
23
|
import com.reactnativenavigation.options.BottomTabOptions;
|
|
@@ -25,6 +27,7 @@ import com.reactnativenavigation.react.CommandListener;
|
|
|
25
27
|
import com.reactnativenavigation.react.CommandListenerAdapter;
|
|
26
28
|
import com.reactnativenavigation.react.events.EventEmitter;
|
|
27
29
|
import com.reactnativenavigation.utils.ImageLoader;
|
|
30
|
+
import com.reactnativenavigation.utils.SystemUiUtils;
|
|
28
31
|
import com.reactnativenavigation.viewcontrollers.bottomtabs.attacher.BottomTabsAttacher;
|
|
29
32
|
import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry;
|
|
30
33
|
import com.reactnativenavigation.viewcontrollers.parent.ParentController;
|
|
@@ -34,7 +37,9 @@ import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;
|
|
|
34
37
|
import com.reactnativenavigation.views.bottomtabs.BottomTabs;
|
|
35
38
|
import com.reactnativenavigation.views.bottomtabs.BottomTabsContainer;
|
|
36
39
|
import com.reactnativenavigation.views.bottomtabs.BottomTabsLayout;
|
|
40
|
+
import com.reactnativenavigation.views.bottomtabs.CustomBottomTabItemView;
|
|
37
41
|
|
|
42
|
+
import java.util.ArrayList;
|
|
38
43
|
import java.util.Collection;
|
|
39
44
|
import java.util.Deque;
|
|
40
45
|
import java.util.LinkedList;
|
|
@@ -42,6 +47,8 @@ import java.util.List;
|
|
|
42
47
|
|
|
43
48
|
public class BottomTabsController extends ParentController<BottomTabsLayout> implements AHBottomNavigation.OnTabSelectedListener, TabSelector {
|
|
44
49
|
|
|
50
|
+
private static final String LOG_TAG = "BottomTabsController";
|
|
51
|
+
|
|
45
52
|
private BottomTabsContainer bottomTabsContainer;
|
|
46
53
|
private BottomTabs bottomTabs;
|
|
47
54
|
private final Deque<Integer> selectionStack;
|
|
@@ -51,6 +58,7 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
|
|
|
51
58
|
private final BottomTabsAttacher tabsAttacher;
|
|
52
59
|
private final BottomTabsPresenter presenter;
|
|
53
60
|
private final BottomTabPresenter tabPresenter;
|
|
61
|
+
private boolean useCustomItemViews;
|
|
54
62
|
|
|
55
63
|
public BottomTabsAnimator getAnimator() {
|
|
56
64
|
return presenter.getAnimator();
|
|
@@ -105,13 +113,59 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
|
|
|
105
113
|
bottomTabs.setOnTabSelectedListener(this);
|
|
106
114
|
root.addBottomTabsContainer(bottomTabsContainer);
|
|
107
115
|
|
|
116
|
+
useCustomItemViews = resolveUseCustomItemViews();
|
|
117
|
+
tabPresenter.setUseCustomItemViews(useCustomItemViews);
|
|
118
|
+
|
|
108
119
|
bottomTabs.addItems(createTabs());
|
|
120
|
+
|
|
121
|
+
if (useCustomItemViews) {
|
|
122
|
+
attachCustomItemViewsToCells();
|
|
123
|
+
}
|
|
124
|
+
|
|
109
125
|
setInitialTab(resolveCurrentOptions);
|
|
110
126
|
tabsAttacher.attach();
|
|
111
127
|
|
|
112
128
|
return root;
|
|
113
129
|
}
|
|
114
130
|
|
|
131
|
+
private boolean resolveUseCustomItemViews() {
|
|
132
|
+
if (tabs.isEmpty()) return false;
|
|
133
|
+
int withComponent = 0;
|
|
134
|
+
for (ViewController<?> tab : tabs) {
|
|
135
|
+
BottomTabOptions options = tab.resolveCurrentOptions(initialOptions).bottomTabOptions;
|
|
136
|
+
if (options.component.hasValue()) withComponent++;
|
|
137
|
+
}
|
|
138
|
+
if (withComponent == 0) return false;
|
|
139
|
+
if (withComponent != tabs.size()) {
|
|
140
|
+
Log.w(LOG_TAG,
|
|
141
|
+
"Mixed bottomTab.component usage detected (" + withComponent + " of "
|
|
142
|
+
+ tabs.size() + " tabs). All tabs must declare a component or none — "
|
|
143
|
+
+ "falling back to native rendering for all tabs.");
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private void attachCustomItemViewsToCells() {
|
|
150
|
+
List<CustomBottomTabItemView> overlays = new ArrayList<>();
|
|
151
|
+
int initialIndex = bottomTabs.getCurrentItem();
|
|
152
|
+
for (int i = 0; i < tabs.size(); i++) {
|
|
153
|
+
BottomTabOptions options = tabs.get(i).resolveCurrentOptions(initialOptions).bottomTabOptions;
|
|
154
|
+
String componentId = options.component.componentId.get(tabs.get(i).getId() + "_tab_" + i);
|
|
155
|
+
String componentName = options.component.name.get();
|
|
156
|
+
String badge = options.badge.hasValue() ? options.badge.get() : null;
|
|
157
|
+
CustomBottomTabItemView itemView = new CustomBottomTabItemView(
|
|
158
|
+
getActivity(),
|
|
159
|
+
componentId,
|
|
160
|
+
componentName,
|
|
161
|
+
i,
|
|
162
|
+
i == initialIndex,
|
|
163
|
+
badge);
|
|
164
|
+
overlays.add(itemView);
|
|
165
|
+
}
|
|
166
|
+
bottomTabs.setCustomItemViews(overlays);
|
|
167
|
+
}
|
|
168
|
+
|
|
115
169
|
private void setInitialTab(Options resolveCurrentOptions) {
|
|
116
170
|
int initialTabIndex = 0;
|
|
117
171
|
if (resolveCurrentOptions.bottomTabsOptions.currentTabId.hasValue())
|
|
@@ -120,6 +174,9 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
|
|
|
120
174
|
initialTabIndex = resolveCurrentOptions.bottomTabsOptions.currentTabIndex.get();
|
|
121
175
|
}
|
|
122
176
|
bottomTabs.setCurrentItem(initialTabIndex, false);
|
|
177
|
+
if (useCustomItemViews) {
|
|
178
|
+
bottomTabs.onCustomItemViewSelectionChanged(initialTabIndex);
|
|
179
|
+
}
|
|
123
180
|
}
|
|
124
181
|
|
|
125
182
|
@NonNull
|
|
@@ -156,6 +213,7 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
|
|
|
156
213
|
public void applyChildOptions(Options options, ViewController<?> child) {
|
|
157
214
|
super.applyChildOptions(options, child);
|
|
158
215
|
presenter.applyChildOptions(resolveCurrentOptions(), child);
|
|
216
|
+
onNavigationBarOptionsChanged(options);
|
|
159
217
|
performOnParentController(parent -> parent.applyChildOptions(
|
|
160
218
|
this.options.copy()
|
|
161
219
|
.clearBottomTabsOptions()
|
|
@@ -170,6 +228,7 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
|
|
|
170
228
|
super.mergeChildOptions(options, child);
|
|
171
229
|
presenter.mergeChildOptions(options, child);
|
|
172
230
|
tabPresenter.mergeChildOptions(options, child);
|
|
231
|
+
onNavigationBarOptionsChanged(options);
|
|
173
232
|
performOnParentController(parent -> parent.mergeChildOptions(options.copy().clearBottomTabsOptions(), child));
|
|
174
233
|
}
|
|
175
234
|
|
|
@@ -291,6 +350,9 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
|
|
|
291
350
|
ViewController<?> previouslyVisible = getCurrentChild();
|
|
292
351
|
bottomTabs.setCurrentItem(newIndex, false);
|
|
293
352
|
getCurrentChild().onSelected(previouslyVisible);
|
|
353
|
+
if (useCustomItemViews) {
|
|
354
|
+
bottomTabs.onCustomItemViewSelectionChanged(newIndex);
|
|
355
|
+
}
|
|
294
356
|
}
|
|
295
357
|
|
|
296
358
|
private void saveTabSelection(int newIndex, boolean enableSelectionHistory) {
|
|
@@ -316,14 +378,23 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
|
|
|
316
378
|
|
|
317
379
|
@Override
|
|
318
380
|
protected WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
int bottomInset = (imeInsets.bottom > 0) ? 0 : sysInsets.bottom;
|
|
323
|
-
view.setPaddingRelative(0, 0, 0, bottomInset);
|
|
381
|
+
boolean drawBehindNavBar = resolveCurrentOptions().navigationBar.isDrawBehindAndVisible();
|
|
382
|
+
view.setPaddingRelative(0, 0, 0, SystemUiUtils.getBottomTabsSystemBarPadding(insets, drawBehindNavBar));
|
|
324
383
|
return insets;
|
|
325
384
|
}
|
|
326
385
|
|
|
386
|
+
private void onNavigationBarOptionsChanged(Options options) {
|
|
387
|
+
if (!options.navigationBar.hasAnyValue()) return;
|
|
388
|
+
refreshNavigationBarInsets();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private void refreshNavigationBarInsets() {
|
|
392
|
+
if (getView() != null) {
|
|
393
|
+
applyBottomInset();
|
|
394
|
+
ViewCompat.requestApplyInsets(getView());
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
327
398
|
@RestrictTo(RestrictTo.Scope.TESTS)
|
|
328
399
|
public BottomTabs getBottomTabs() {
|
|
329
400
|
return bottomTabs;
|
|
@@ -393,6 +393,7 @@ class BottomTabsPresenter(
|
|
|
393
393
|
private fun syncNavigationBarColor(options: Options, tabsColor: Int) {
|
|
394
394
|
val resolved = options.copy().withDefaultOptions(defaultOptions)
|
|
395
395
|
if (resolved.navigationBar.backgroundColor.hasValue()) return
|
|
396
|
+
if (resolved.navigationBar.shouldDrawBehind()) return
|
|
396
397
|
val window = (bottomTabsContainer.context as? Activity)?.window ?: return
|
|
397
398
|
SystemUiUtils.setNavigationBarBackgroundColor(window, tabsColor, isColorLight(tabsColor))
|
|
398
399
|
}
|
|
@@ -173,11 +173,8 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
|
|
|
173
173
|
insets.getInsets(WindowInsetsCompat.Type.navigationBars()).top -
|
|
174
174
|
systemBarsInsets.top;
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
int
|
|
178
|
-
int systemWindowInsetBottom = SystemUiUtils.isEdgeToEdgeActive()
|
|
179
|
-
? Math.max(imeBottom, navBarBottom)
|
|
180
|
-
: imeBottom;
|
|
176
|
+
boolean drawBehindNavBar = resolveCurrentOptions(presenter.defaultOptions).navigationBar.isDrawBehindAndVisible();
|
|
177
|
+
int systemWindowInsetBottom = SystemUiUtils.getContentBottomSystemBarInset(insets, drawBehindNavBar);
|
|
181
178
|
|
|
182
179
|
WindowInsetsCompat finalInsets = new WindowInsetsCompat.Builder()
|
|
183
180
|
.setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.ime(),
|
|
@@ -11,6 +11,8 @@ import android.view.View;
|
|
|
11
11
|
import android.view.ViewGroup;
|
|
12
12
|
import android.view.ViewGroup.MarginLayoutParams;
|
|
13
13
|
|
|
14
|
+
import androidx.core.view.ViewCompat;
|
|
15
|
+
|
|
14
16
|
import com.reactnativenavigation.options.NavigationBarOptions;
|
|
15
17
|
import com.reactnativenavigation.options.Options;
|
|
16
18
|
import com.reactnativenavigation.options.OrientationOptions;
|
|
@@ -114,16 +116,16 @@ public class Presenter {
|
|
|
114
116
|
|
|
115
117
|
private void applyNavigationBarOptions(NavigationBarOptions options) {
|
|
116
118
|
applyNavigationBarVisibility(options);
|
|
117
|
-
|
|
119
|
+
applyNavigationBarBackground(options);
|
|
120
|
+
refreshNavigationBarInsets();
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
private void mergeNavigationBarOptions(NavigationBarOptions options) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (options.isVisible.hasValue()) applyNavigationBarOptions(options);
|
|
124
|
+
if (options.isVisible.hasValue()) {
|
|
125
|
+
applyNavigationBarVisibility(options);
|
|
126
|
+
}
|
|
127
|
+
applyNavigationBarBackground(options);
|
|
128
|
+
refreshNavigationBarInsets();
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
private void applyNavigationBarVisibility(NavigationBarOptions options) {
|
|
@@ -136,20 +138,38 @@ public class Presenter {
|
|
|
136
138
|
}
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
private void
|
|
141
|
+
private void applyNavigationBarBackground(NavigationBarOptions navigationBar) {
|
|
140
142
|
if (activity == null) return;
|
|
141
143
|
int defaultColor = SystemUiUtils.getDefaultNavBarColor();
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
int color;
|
|
145
|
+
boolean hideOverlay;
|
|
146
|
+
if (navigationBar.isDrawBehindAndVisible()) {
|
|
147
|
+
if (navigationBar.backgroundColor.canApplyValue()) {
|
|
148
|
+
color = navigationBar.backgroundColor.get(defaultColor);
|
|
149
|
+
hideOverlay = Color.alpha(color) == 0;
|
|
150
|
+
} else {
|
|
151
|
+
color = Color.TRANSPARENT;
|
|
152
|
+
hideOverlay = true;
|
|
153
|
+
}
|
|
145
154
|
} else {
|
|
146
|
-
|
|
155
|
+
hideOverlay = false;
|
|
156
|
+
color = navigationBar.backgroundColor.canApplyValue()
|
|
157
|
+
? navigationBar.backgroundColor.get(defaultColor)
|
|
158
|
+
: defaultColor;
|
|
147
159
|
}
|
|
160
|
+
SystemUiUtils.setNavigationBarBackgroundColor(
|
|
161
|
+
activity.getWindow(), color, isColorLight(color), hideOverlay);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private void refreshNavigationBarInsets() {
|
|
165
|
+
if (activity == null) return;
|
|
166
|
+
ViewCompat.requestApplyInsets(activity.getWindow().getDecorView());
|
|
148
167
|
}
|
|
149
168
|
|
|
150
169
|
public void onConfigurationChanged(ViewController controller, Options options) {
|
|
151
170
|
Options withDefault = options.withDefaultOptions(defaultOptions);
|
|
152
|
-
|
|
171
|
+
applyNavigationBarBackground(withDefault.navigationBar);
|
|
172
|
+
refreshNavigationBarInsets();
|
|
153
173
|
StatusBarPresenter.instance.onConfigurationChanged(withDefault.statusBar);
|
|
154
174
|
applyBackgroundColor(controller, withDefault);
|
|
155
175
|
}
|
|
@@ -8,6 +8,8 @@ import android.content.Context;
|
|
|
8
8
|
import android.graphics.Color;
|
|
9
9
|
import android.graphics.drawable.Drawable;
|
|
10
10
|
import android.view.View;
|
|
11
|
+
import android.view.ViewGroup;
|
|
12
|
+
import android.widget.FrameLayout;
|
|
11
13
|
import android.widget.LinearLayout;
|
|
12
14
|
|
|
13
15
|
import androidx.annotation.IntRange;
|
|
@@ -25,6 +27,19 @@ public class BottomTabs extends AHBottomNavigation {
|
|
|
25
27
|
private boolean itemsCreationEnabled = true;
|
|
26
28
|
private boolean shouldCreateItems = true;
|
|
27
29
|
private List<Runnable> onItemCreationEnabled = new ArrayList<>();
|
|
30
|
+
private final List<CustomBottomTabItemView> customItemViews = new ArrayList<>();
|
|
31
|
+
private boolean externalCustomItemViewHost = false;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* When enabled, this view stops re-parenting custom React tab item views
|
|
35
|
+
* into its native cells on every layout pass — the caller assumes full
|
|
36
|
+
* ownership of where those item views live in the view tree (used by
|
|
37
|
+
* the customRow floating-row implementation). Existing behavior is
|
|
38
|
+
* unchanged when this remains {@code false} (the default).
|
|
39
|
+
*/
|
|
40
|
+
public void setExternalCustomItemViewHost(boolean enabled) {
|
|
41
|
+
this.externalCustomItemViewHost = enabled;
|
|
42
|
+
}
|
|
28
43
|
|
|
29
44
|
public BottomTabs(Context context) {
|
|
30
45
|
super(context);
|
|
@@ -131,6 +146,67 @@ public class BottomTabs extends AHBottomNavigation {
|
|
|
131
146
|
if (tabsContainer != null) tabsContainer.setLayoutDirection(direction.get());
|
|
132
147
|
}
|
|
133
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Replace the visual content of every tab cell with the provided custom
|
|
151
|
+
* views. The custom view is attached as a child of the AHBottomNavigation
|
|
152
|
+
* cell view so taps continue to be handled by the native cell. Pass an
|
|
153
|
+
* empty list to remove all overlays.
|
|
154
|
+
*/
|
|
155
|
+
public void setCustomItemViews(List<CustomBottomTabItemView> customViews) {
|
|
156
|
+
clearCustomItemViews();
|
|
157
|
+
if (customViews == null || customViews.isEmpty()) return;
|
|
158
|
+
|
|
159
|
+
customItemViews.addAll(customViews);
|
|
160
|
+
attachCustomItemViews();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public void onCustomItemViewSelectionChanged(int selectedIndex) {
|
|
164
|
+
for (int i = 0; i < customItemViews.size(); i++) {
|
|
165
|
+
customItemViews.get(i).setItemSelected(i == selectedIndex);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public CustomBottomTabItemView getCustomItemView(int index) {
|
|
170
|
+
if (index < 0 || index >= customItemViews.size()) return null;
|
|
171
|
+
return customItemViews.get(index);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public boolean hasCustomItemViews() {
|
|
175
|
+
return !customItemViews.isEmpty();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private void clearCustomItemViews() {
|
|
179
|
+
for (CustomBottomTabItemView view : customItemViews) {
|
|
180
|
+
ViewGroup parent = (ViewGroup) view.getParent();
|
|
181
|
+
if (parent != null) parent.removeView(view);
|
|
182
|
+
}
|
|
183
|
+
customItemViews.clear();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private void attachCustomItemViews() {
|
|
187
|
+
if (externalCustomItemViewHost) return;
|
|
188
|
+
for (int i = 0; i < customItemViews.size(); i++) {
|
|
189
|
+
View cell = getViewAtPosition(i);
|
|
190
|
+
if (!(cell instanceof ViewGroup)) continue;
|
|
191
|
+
CustomBottomTabItemView itemView = customItemViews.get(i);
|
|
192
|
+
ViewGroup parent = (ViewGroup) itemView.getParent();
|
|
193
|
+
if (parent != null && parent != cell) parent.removeView(itemView);
|
|
194
|
+
if (itemView.getParent() == null) {
|
|
195
|
+
((ViewGroup) cell).addView(itemView, new FrameLayout.LayoutParams(
|
|
196
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
197
|
+
FrameLayout.LayoutParams.MATCH_PARENT));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
@Override
|
|
203
|
+
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
|
204
|
+
super.onLayout(changed, l, t, r, b);
|
|
205
|
+
if (changed && !customItemViews.isEmpty()) {
|
|
206
|
+
attachCustomItemViews();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
134
210
|
private boolean hasItemsAndIsMeasured(int w, int h, int oldw, int oldh) {
|
|
135
211
|
return w != 0 && h != 0 && (w != oldw || h != oldh) && getItemsCount() > 0;
|
|
136
212
|
}
|
package/android/src/main/java/com/reactnativenavigation/views/bottomtabs/CustomBottomTabItemView.kt
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package com.reactnativenavigation.views.bottomtabs
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.os.Bundle
|
|
6
|
+
import android.view.MotionEvent
|
|
7
|
+
import android.widget.FrameLayout
|
|
8
|
+
import com.reactnativenavigation.react.ReactView
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Hosts a [ReactView] that renders a user-supplied React component as a
|
|
12
|
+
* bottom tab item. The view sits on top of the native AHBottomNavigation tab
|
|
13
|
+
* cell and forwards touches through to the underlying cell so native
|
|
14
|
+
* selection, ripple and `selectTabOnPress: false` keep working.
|
|
15
|
+
*
|
|
16
|
+
* The hosted component receives the following props at creation:
|
|
17
|
+
* `componentId`, `tabIndex`, `selected`, `badge`. Selection updates are
|
|
18
|
+
* pushed via [setSelected]; badge updates via [setBadge].
|
|
19
|
+
*/
|
|
20
|
+
@SuppressLint("ViewConstructor")
|
|
21
|
+
class CustomBottomTabItemView(
|
|
22
|
+
context: Context,
|
|
23
|
+
val componentId: String,
|
|
24
|
+
val componentName: String,
|
|
25
|
+
val tabIndex: Int,
|
|
26
|
+
initialSelected: Boolean,
|
|
27
|
+
initialBadge: String?
|
|
28
|
+
) : FrameLayout(context) {
|
|
29
|
+
|
|
30
|
+
val reactView: ReactView = ReactView(context, componentId, componentName)
|
|
31
|
+
private var isCurrentlySelected: Boolean = initialSelected
|
|
32
|
+
private var badge: String? = initialBadge
|
|
33
|
+
|
|
34
|
+
init {
|
|
35
|
+
addView(reactView)
|
|
36
|
+
reactView.isClickable = false
|
|
37
|
+
reactView.isFocusable = false
|
|
38
|
+
isClickable = false
|
|
39
|
+
isFocusable = false
|
|
40
|
+
pushProps()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Touches must always reach the underlying AHBottomNavigation cell so
|
|
45
|
+
* that native selection, ripple, accessibility focus and
|
|
46
|
+
* `selectTabOnPress: false` keep working. Returning false here makes
|
|
47
|
+
* this view completely transparent to touch input and prevents any
|
|
48
|
+
* `Touchable*` rendered inside the React tree from swallowing taps.
|
|
49
|
+
*/
|
|
50
|
+
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean = false
|
|
51
|
+
|
|
52
|
+
fun setItemSelected(selected: Boolean) {
|
|
53
|
+
if (this.isCurrentlySelected == selected) return
|
|
54
|
+
this.isCurrentlySelected = selected
|
|
55
|
+
pushProps()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fun setBadge(badge: String?) {
|
|
59
|
+
if (this.badge == badge) return
|
|
60
|
+
this.badge = badge
|
|
61
|
+
pushProps()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private fun pushProps() {
|
|
65
|
+
val bundle = Bundle().apply {
|
|
66
|
+
putString("componentId", componentId)
|
|
67
|
+
putInt("tabIndex", tabIndex)
|
|
68
|
+
putBoolean("selected", isCurrentlySelected)
|
|
69
|
+
if (badge != null) putString("badge", badge)
|
|
70
|
+
}
|
|
71
|
+
reactView.setProps(bundle)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -96,4 +96,18 @@ public class PresenterTest extends BaseTest {
|
|
|
96
96
|
verify(parentView).setPadding(2,1,4,3);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
@Test
|
|
100
|
+
public void applyNavigationBarDrawBehind_usesTransparentOverlay() {
|
|
101
|
+
mockSystemUiUtils(0, 0, (mockedStatic) -> {
|
|
102
|
+
ViewGroup spy = spy(new FrameLayout(activity));
|
|
103
|
+
Mockito.when(controller.getView()).thenReturn(spy);
|
|
104
|
+
Mockito.when(controller.resolveCurrentOptions()).thenReturn(Options.EMPTY);
|
|
105
|
+
Options options = new Options();
|
|
106
|
+
options.navigationBar.drawBehind = new Bool(true);
|
|
107
|
+
uut.applyOptions(controller, options);
|
|
108
|
+
mockedStatic.verify(() -> SystemUiUtils.setNavigationBarBackgroundColor(
|
|
109
|
+
any(), eq(android.graphics.Color.TRANSPARENT), eq(false), eq(true)), times(1));
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
99
113
|
}
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
package com.reactnativenavigation.utils
|
|
2
2
|
|
|
3
3
|
import android.graphics.Color
|
|
4
|
+
import android.graphics.drawable.ColorDrawable
|
|
5
|
+
import android.view.View
|
|
4
6
|
import android.view.Window
|
|
7
|
+
import android.widget.FrameLayout
|
|
8
|
+
import androidx.appcompat.app.AppCompatActivity
|
|
5
9
|
import com.reactnativenavigation.BaseRobolectricTest
|
|
6
10
|
import com.reactnativenavigation.utils.SystemUiUtils.STATUS_BAR_HEIGHT_TRANSLUCENCY
|
|
11
|
+
import org.assertj.core.api.Java6Assertions.assertThat
|
|
12
|
+
import org.junit.After
|
|
7
13
|
import org.junit.Test
|
|
8
14
|
import org.mockito.Mockito
|
|
9
15
|
import org.mockito.kotlin.verify
|
|
16
|
+
import org.robolectric.Robolectric
|
|
10
17
|
import kotlin.math.ceil
|
|
11
18
|
|
|
12
19
|
class SystemUiUtilsTest : BaseRobolectricTest() {
|
|
13
20
|
|
|
21
|
+
@After
|
|
22
|
+
fun afterEach() {
|
|
23
|
+
SystemUiUtils.tearDown()
|
|
24
|
+
}
|
|
25
|
+
|
|
14
26
|
@Test
|
|
15
27
|
fun `setStatusBarColor - should change color considering alpha`() {
|
|
16
28
|
val window = Mockito.mock(Window::class.java)
|
|
@@ -24,4 +36,55 @@ class SystemUiUtilsTest : BaseRobolectricTest() {
|
|
|
24
36
|
|
|
25
37
|
verify(window).statusBarColor = Color.argb(ceil(STATUS_BAR_HEIGHT_TRANSLUCENCY*255).toInt(), 22, 255, 255)
|
|
26
38
|
}
|
|
27
|
-
|
|
39
|
+
|
|
40
|
+
@Test
|
|
41
|
+
fun `setupSystemBarBackgrounds - initializes navigation bar background from resolved color`() {
|
|
42
|
+
val activity = Robolectric.setupActivity(AppCompatActivity::class.java)
|
|
43
|
+
val contentLayout = FrameLayout(activity)
|
|
44
|
+
val initialColor = Color.RED
|
|
45
|
+
SystemUiUtils.setNavigationBarBackgroundColor(activity.window, initialColor, false)
|
|
46
|
+
|
|
47
|
+
SystemUiUtils.setupSystemBarBackgrounds(activity, contentLayout)
|
|
48
|
+
|
|
49
|
+
assertThat(getBackgroundColor(getNavigationBarBackground(contentLayout))).isEqualTo(initialColor)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Test
|
|
53
|
+
fun `setNavigationBarBackgroundColor - updates view and window color when edge-to-edge is inactive`() {
|
|
54
|
+
val activity = Robolectric.setupActivity(AppCompatActivity::class.java)
|
|
55
|
+
val contentLayout = FrameLayout(activity)
|
|
56
|
+
@Suppress("DEPRECATION")
|
|
57
|
+
activity.window.navigationBarColor = Color.BLACK
|
|
58
|
+
SystemUiUtils.setupSystemBarBackgrounds(activity, contentLayout)
|
|
59
|
+
|
|
60
|
+
SystemUiUtils.setNavigationBarBackgroundColor(activity.window, Color.WHITE, true)
|
|
61
|
+
|
|
62
|
+
assertThat(getBackgroundColor(getNavigationBarBackground(contentLayout))).isEqualTo(Color.WHITE)
|
|
63
|
+
assertThat(activity.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
|
|
64
|
+
.isEqualTo(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@Test
|
|
68
|
+
fun `setNavigationBarBackgroundColor - updates view and icon appearance when edge-to-edge is active`() {
|
|
69
|
+
val activity = Robolectric.setupActivity(AppCompatActivity::class.java)
|
|
70
|
+
val contentLayout = FrameLayout(activity)
|
|
71
|
+
val initialColor = Color.RED
|
|
72
|
+
SystemUiUtils.setNavigationBarBackgroundColor(activity.window, initialColor, false)
|
|
73
|
+
SystemUiUtils.setupSystemBarBackgrounds(activity, contentLayout)
|
|
74
|
+
SystemUiUtils.activateEdgeToEdge()
|
|
75
|
+
|
|
76
|
+
SystemUiUtils.setNavigationBarBackgroundColor(activity.window, Color.WHITE, true)
|
|
77
|
+
|
|
78
|
+
assertThat(getBackgroundColor(getNavigationBarBackground(contentLayout))).isEqualTo(Color.WHITE)
|
|
79
|
+
assertThat(activity.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
|
|
80
|
+
.isEqualTo(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private fun getNavigationBarBackground(contentLayout: FrameLayout): View {
|
|
84
|
+
return contentLayout.getChildAt(contentLayout.childCount - 1)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private fun getBackgroundColor(view: View): Int {
|
|
88
|
+
return (view.background as ColorDrawable).color
|
|
89
|
+
}
|
|
90
|
+
}
|
package/ios/ARCHITECTURE.md
CHANGED
|
@@ -28,6 +28,11 @@ Base class that user's AppDelegate must extend. Handles React Native and navigat
|
|
|
28
28
|
- Creates `RCTRootViewFactory` and `ReactHost`
|
|
29
29
|
- Calls `[ReactNativeNavigation bootstrapWithHost:]` to initialize navigation
|
|
30
30
|
- Handles RN version differences (0.77, 0.78, 0.79+) via compile-time macros
|
|
31
|
+
- **Deep linking plumbing**:
|
|
32
|
+
- Implements `application:openURL:options:` and `application:continueUserActivity:restorationHandler:`; both call `-dispatchDeepLinkURL:`.
|
|
33
|
+
- `-dispatchDeepLinkURL:` posts `RCTOpenURLNotification` directly if the React runtime is ready, otherwise enqueues the URL.
|
|
34
|
+
- Observes `RCTContentDidAppearNotification` (the Fabric/bridgeless signal) to flush the queue. This solves the cold-start race where URLs (push notifications, OS link launches) arrive before `RCTLinkingManager` is listening.
|
|
35
|
+
- Subclasses can call `-dispatchDeepLinkURL:` manually from notification delegates or any other URL source; the queueing behavior is reused automatically.
|
|
31
36
|
|
|
32
37
|
### ReactNativeNavigation Bootstrap
|
|
33
38
|
**File**: `ReactNativeNavigation.h/mm`
|
package/ios/BottomTabPresenter.h
CHANGED
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
@property(nonatomic, strong, readonly) RNNTabBarItemCreator *tabCreator;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* When YES, tabs whose options declare `bottomTab.component` skip native
|
|
9
|
+
* icon/text/sfSymbol/role application. The accompanying
|
|
10
|
+
* `RNNCustomTabBarItemView` is responsible for visual rendering of the tab.
|
|
11
|
+
*/
|
|
12
|
+
@property(nonatomic, assign) BOOL useCustomItemViews;
|
|
13
|
+
|
|
7
14
|
- (instancetype)initWithDefaultOptions:(RNNNavigationOptions *)defaultOptions
|
|
8
15
|
tabCreator:(RNNTabBarItemCreator *)tabCreator;
|
|
9
16
|
|
|
@@ -36,10 +36,37 @@
|
|
|
36
36
|
|
|
37
37
|
- (void)createTabBarItem:(UIViewController *)child
|
|
38
38
|
bottomTabOptions:(RNNBottomTabOptions *)bottomTabOptions {
|
|
39
|
+
if (_useCustomItemViews && bottomTabOptions.component.name.hasValue) {
|
|
40
|
+
UITabBarItem *blankItem = [self createBlankTabBarItem:child
|
|
41
|
+
bottomTabOptions:bottomTabOptions];
|
|
42
|
+
if (blankItem != child.tabBarItem) {
|
|
43
|
+
child.tabBarItem = blankItem;
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
UITabBarItem *updatedItem = [_tabCreator createTabBarItem:bottomTabOptions mergeItem:child.tabBarItem];
|
|
40
49
|
if (updatedItem != child.tabBarItem) {
|
|
41
50
|
child.tabBarItem = updatedItem;
|
|
42
51
|
}
|
|
43
52
|
}
|
|
44
53
|
|
|
54
|
+
// Builds a truly blank `UITabBarItem` (nil image, nil title). When custom
|
|
55
|
+
// item views are active, `RNNBottomTabsController` hides the native tab bar
|
|
56
|
+
// visuals and renders the custom row on top. The bar item still needs to
|
|
57
|
+
// exist so that `UITabBarController` reserves the right number of slots and
|
|
58
|
+
// the bottom safe-area inset.
|
|
59
|
+
- (UITabBarItem *)createBlankTabBarItem:(UIViewController *)child
|
|
60
|
+
bottomTabOptions:(RNNBottomTabOptions *)bottomTabOptions {
|
|
61
|
+
UITabBarItem *item = child.tabBarItem ?: [UITabBarItem new];
|
|
62
|
+
item.image = nil;
|
|
63
|
+
item.selectedImage = nil;
|
|
64
|
+
item.title = nil;
|
|
65
|
+
item.tag = bottomTabOptions.tag;
|
|
66
|
+
item.accessibilityIdentifier = [bottomTabOptions.testID withDefault:nil];
|
|
67
|
+
item.accessibilityLabel = [bottomTabOptions.accessibilityLabel withDefault:nil];
|
|
68
|
+
item.imageInsets = UIEdgeInsetsZero;
|
|
69
|
+
return item;
|
|
70
|
+
}
|
|
71
|
+
|
|
45
72
|
@end
|
package/ios/RNNAppDelegate.h
CHANGED
|
@@ -56,4 +56,20 @@
|
|
|
56
56
|
@property(nonatomic) BOOL bridgelessEnabled;
|
|
57
57
|
#endif
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Dispatch a deep link URL through React Native's Linking module so JS
|
|
61
|
+
* subscribers (including RNN's built-in deep linking framework) receive it.
|
|
62
|
+
*
|
|
63
|
+
* Safe to call before the JS bridge is ready: URLs that arrive early
|
|
64
|
+
* (e.g. cold-start notification taps) are queued natively and flushed
|
|
65
|
+
* automatically once Fabric/React content first appears.
|
|
66
|
+
*
|
|
67
|
+
* Custom-scheme and universal-link openings dispatched by the OS are
|
|
68
|
+
* forwarded through this method automatically; call it manually only
|
|
69
|
+
* when your app receives a deep link from a source RNN can't intercept
|
|
70
|
+
* (e.g. a custom `UNUserNotificationCenterDelegate`, a third-party push
|
|
71
|
+
* SDK callback, etc.).
|
|
72
|
+
*/
|
|
73
|
+
- (void)dispatchDeepLinkURL:(NSURL *)url;
|
|
74
|
+
|
|
59
75
|
@end
|