react-native-screens 4.25.0-beta.1 → 4.25.0-beta.2
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/swmansion/rnscreens/gamma/stack/header/StackHeaderAppBarLayout.kt +6 -14
- package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/StackHeaderAppBarLayoutBehavior.kt +29 -0
- package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/StackHeaderCoordinator.kt +56 -0
- package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/config/StackHeaderConfig.kt +11 -0
- package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/config/StackHeaderConfigProviding.kt +5 -0
- package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/config/StackHeaderConfigViewManager.kt +35 -0
- package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/subview/StackHeaderSubview.kt +3 -7
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsActionOrigin.kt +26 -0
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsContainer.kt +227 -151
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsNavigationState.kt +60 -0
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/{TabsContainerDelegate.kt → TabsNavigationStateObserver.kt} +19 -14
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsNavigationStateObserverRegistry.kt +88 -0
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/TabsHost.kt +40 -24
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/TabsHostEventEmitter.kt +11 -9
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/TabsHostViewManager.kt +19 -7
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/event/TabsHostTabSelectedEvent.kt +4 -3
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/event/TabsHostTabSelectionPreventedEvent.kt +3 -3
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/event/TabsHostTabSelectionRejectedEvent.kt +11 -10
- package/ios/conversion/RNSConversions-Tabs.mm +19 -0
- package/ios/conversion/RNSConversions.h +3 -0
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryHelper.mm +34 -5
- package/ios/tabs/host/RNSTabBarController.h +152 -99
- package/ios/tabs/host/RNSTabBarController.mm +137 -113
- package/ios/tabs/host/RNSTabsHostComponentView.h +7 -8
- package/ios/tabs/host/RNSTabsHostComponentView.mm +37 -33
- package/ios/tabs/host/RNSTabsHostEventEmitter.h +4 -4
- package/ios/tabs/host/RNSTabsHostEventEmitter.mm +5 -3
- package/ios/tabs/host/RNSTabsNavigationState.h +142 -27
- package/ios/tabs/host/RNSTabsNavigationState.mm +35 -2
- package/ios/tabs/host/RNSTabsNavigationStateObserverRegistry.h +62 -0
- package/ios/tabs/host/RNSTabsNavigationStateObserverRegistry.mm +104 -0
- package/lib/commonjs/components/gamma/stack/header/StackHeaderConfig.android.js +46 -1
- package/lib/commonjs/components/gamma/stack/header/StackHeaderConfig.android.js.map +1 -1
- package/lib/commonjs/components/safe-area/SafeAreaView.web.js +2 -3
- package/lib/commonjs/components/safe-area/SafeAreaView.web.js.map +1 -1
- package/lib/commonjs/components/tabs/host/TabsHost.android.js +2 -2
- package/lib/commonjs/components/tabs/host/TabsHost.android.js.map +1 -1
- package/lib/commonjs/components/tabs/host/TabsHost.ios.js +2 -2
- package/lib/commonjs/components/tabs/host/TabsHost.ios.js.map +1 -1
- package/lib/commonjs/fabric/gamma/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
- package/lib/commonjs/flags.js +1 -0
- package/lib/commonjs/flags.js.map +1 -1
- package/lib/module/components/gamma/stack/header/StackHeaderConfig.android.js +46 -1
- package/lib/module/components/gamma/stack/header/StackHeaderConfig.android.js.map +1 -1
- package/lib/module/components/safe-area/SafeAreaView.web.js +1 -1
- package/lib/module/components/safe-area/SafeAreaView.web.js.map +1 -1
- package/lib/module/components/tabs/host/TabsHost.android.js +2 -2
- package/lib/module/components/tabs/host/TabsHost.android.js.map +1 -1
- package/lib/module/components/tabs/host/TabsHost.ios.js +2 -2
- package/lib/module/components/tabs/host/TabsHost.ios.js.map +1 -1
- package/lib/module/fabric/gamma/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
- package/lib/module/flags.js +1 -0
- package/lib/module/flags.js.map +1 -1
- package/lib/typescript/components/gamma/split/SplitHost.types.d.ts +1 -1
- package/lib/typescript/components/gamma/split/SplitHost.types.d.ts.map +1 -1
- package/lib/typescript/components/gamma/stack/header/StackHeaderConfig.android.d.ts.map +1 -1
- package/lib/typescript/components/gamma/stack/header/StackHeaderConfig.android.types.d.ts +183 -8
- package/lib/typescript/components/gamma/stack/header/StackHeaderConfig.android.types.d.ts.map +1 -1
- package/lib/typescript/components/gamma/stack/header/StackHeaderConfig.types.d.ts +37 -0
- package/lib/typescript/components/gamma/stack/header/StackHeaderConfig.types.d.ts.map +1 -1
- package/lib/typescript/components/gamma/stack/header/android/StackHeaderSubview.android.types.d.ts +1 -1
- package/lib/typescript/components/gamma/stack/header/android/StackHeaderSubview.android.types.d.ts.map +1 -1
- package/lib/typescript/components/gamma/stack/host/StackHost.types.d.ts +1 -1
- package/lib/typescript/components/gamma/stack/host/StackHost.types.d.ts.map +1 -1
- package/lib/typescript/components/safe-area/SafeAreaView.web.d.ts +1 -1
- package/lib/typescript/components/safe-area/SafeAreaView.web.d.ts.map +1 -1
- package/lib/typescript/components/tabs/host/TabsHost.types.d.ts +27 -17
- package/lib/typescript/components/tabs/host/TabsHost.types.d.ts.map +1 -1
- package/lib/typescript/components/tabs/index.d.ts +1 -1
- package/lib/typescript/components/tabs/index.d.ts.map +1 -1
- package/lib/typescript/fabric/gamma/stack/StackHeaderConfigAndroidNativeComponent.d.ts +5 -0
- package/lib/typescript/fabric/gamma/stack/StackHeaderConfigAndroidNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/tabs/TabsHostAndroidNativeComponent.d.ts +4 -4
- package/lib/typescript/fabric/tabs/TabsHostAndroidNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts +4 -4
- package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts.map +1 -1
- package/lib/typescript/flags.d.ts +1 -0
- package/lib/typescript/flags.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/gamma/split/SplitHost.types.ts +1 -1
- package/src/components/gamma/stack/header/StackHeaderConfig.android.tsx +72 -2
- package/src/components/gamma/stack/header/StackHeaderConfig.android.types.ts +183 -8
- package/src/components/gamma/stack/header/StackHeaderConfig.types.ts +37 -0
- package/src/components/gamma/stack/header/android/StackHeaderSubview.android.types.ts +1 -1
- package/src/components/gamma/stack/host/StackHost.types.ts +1 -1
- package/src/components/safe-area/SafeAreaView.web.tsx +1 -1
- package/src/components/tabs/host/TabsHost.android.tsx +2 -2
- package/src/components/tabs/host/TabsHost.ios.tsx +2 -2
- package/src/components/tabs/host/TabsHost.types.ts +27 -17
- package/src/components/tabs/index.ts +1 -1
- package/src/fabric/gamma/stack/StackHeaderConfigAndroidNativeComponent.ts +6 -0
- package/src/fabric/tabs/TabsHostAndroidNativeComponent.ts +4 -4
- package/src/fabric/tabs/TabsHostIOSNativeComponent.ts +4 -4
- package/src/flags.ts +1 -0
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsContainerOps.kt +0 -7
- package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsNavState.kt +0 -43
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
#import <React/RCTLog.h>
|
|
4
4
|
#import <objc/message.h>
|
|
5
5
|
#import <objc/runtime.h>
|
|
6
|
+
#import <limits>
|
|
6
7
|
#import "NSString+RNSUtility.h"
|
|
7
8
|
#import "RNSLog.h"
|
|
8
9
|
#import "RNSScreenWindowTraits.h"
|
|
9
10
|
#import "RNSTabsHostComponentView.h"
|
|
11
|
+
#import "RNSTabsNavigationStateObserverRegistry.h"
|
|
10
12
|
|
|
11
13
|
#define RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE !TARGET_OS_TV && !TARGET_OS_VISION
|
|
12
14
|
|
|
@@ -34,16 +36,17 @@
|
|
|
34
36
|
* (reached via UIKit's `tabBarController` property on the parent chain)
|
|
35
37
|
* to check whether the push should be prevented (e.g. due to `preventNativeSelection`).
|
|
36
38
|
*/
|
|
37
|
-
static void
|
|
38
|
-
|
|
39
|
+
static void rns_pushViewController(__unsafe_unretained id self,
|
|
40
|
+
SEL _cmd,
|
|
41
|
+
UIViewController *viewController,
|
|
42
|
+
BOOL animated)
|
|
39
43
|
{
|
|
40
44
|
UITabBarController *rawTabBarController = static_cast<UIViewController *>(self).tabBarController;
|
|
41
45
|
|
|
42
|
-
RCTAssert(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
rawTabBarController.class);
|
|
46
|
+
RCTAssert([rawTabBarController isKindOfClass:RNSTabBarController.class],
|
|
47
|
+
@"[RNScreens] Expected tabBarController to be of class %@, got: %@",
|
|
48
|
+
RNSTabBarController.class,
|
|
49
|
+
rawTabBarController.class);
|
|
47
50
|
RNSTabBarController *tabBarController = static_cast<RNSTabBarController *>(rawTabBarController);
|
|
48
51
|
|
|
49
52
|
if ([tabBarController moreNavigationController:self shouldPushViewController:viewController]) {
|
|
@@ -73,12 +76,14 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
73
76
|
/// This property is nullable until first container update. Later it MUST NOT be nil.
|
|
74
77
|
RNSTabsNavigationState *_Nullable _lastUINavigationState;
|
|
75
78
|
|
|
76
|
-
|
|
79
|
+
RNSTabsNavigationStateUpdateRequest *_Nullable _pendingStateUpdate;
|
|
77
80
|
|
|
78
81
|
/// When YES, the controller is inside an explicit selection-changing code path (container update,
|
|
79
82
|
/// delegate handling). Setter overrides skip reconciliation while this flag is set.
|
|
80
83
|
BOOL _isHandlingExplicitSelectionUpdate;
|
|
81
84
|
|
|
85
|
+
RNSTabsNavigationStateObserverRegistry *_observerRegistry;
|
|
86
|
+
|
|
82
87
|
#if !RCT_NEW_ARCH_ENABLED
|
|
83
88
|
BOOL _isControllerFlushBlockScheduled;
|
|
84
89
|
#endif // !RCT_NEW_ARCH_ENABLED
|
|
@@ -91,8 +96,9 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
91
96
|
_tabBarAppearanceCoordinator = [RNSTabBarAppearanceCoordinator new];
|
|
92
97
|
_tabsHostComponentView = nil;
|
|
93
98
|
_navigationState = nil;
|
|
94
|
-
|
|
99
|
+
_pendingStateUpdate = nil;
|
|
95
100
|
_shouldProgressStateOnMoreNavigationControllerPush = NO;
|
|
101
|
+
_observerRegistry = [RNSTabsNavigationStateObserverRegistry new];
|
|
96
102
|
|
|
97
103
|
// Delegate field retains weakly, no risk of cycle.
|
|
98
104
|
self.delegate = self;
|
|
@@ -104,6 +110,41 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
104
110
|
return self;
|
|
105
111
|
}
|
|
106
112
|
|
|
113
|
+
#pragma mark - Public API
|
|
114
|
+
|
|
115
|
+
- (void)submitSelectionOfTabsScreenWithKey:(nonnull NSString *)screenKey
|
|
116
|
+
{
|
|
117
|
+
RCTAssert(screenKey != nil, @"[RNScreens] Requested screenKey MUST NOT be nil");
|
|
118
|
+
int baseProvenance = _navigationState != nil ? _navigationState.provenance : std::numeric_limits<int>::min();
|
|
119
|
+
RNSTabsNavigationStateUpdateRequest *request =
|
|
120
|
+
[RNSTabsNavigationStateUpdateRequest requestWithSelectedScreenKey:screenKey
|
|
121
|
+
baseProvenance:baseProvenance
|
|
122
|
+
actionOrigin:RNSTabsActionOriginProgrammaticNative];
|
|
123
|
+
[self setPendingNavigationStateUpdate:request];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
- (void)flushPendingUpdates
|
|
127
|
+
{
|
|
128
|
+
[self performContainerUpdate];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
- (BOOL)addNavigationStateObserver:(id<RNSTabsNavigationStateObserver>)observer
|
|
132
|
+
{
|
|
133
|
+
return [_observerRegistry addObserver:observer];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
- (BOOL)removeNavigationStateObserver:(id<RNSTabsNavigationStateObserver>)observer
|
|
137
|
+
{
|
|
138
|
+
return [_observerRegistry removeObserver:observer];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
- (void)tearDown
|
|
142
|
+
{
|
|
143
|
+
[_observerRegistry clear];
|
|
144
|
+
_pendingStateUpdate = nil;
|
|
145
|
+
_tabsHostComponentView = nil;
|
|
146
|
+
}
|
|
147
|
+
|
|
107
148
|
- (instancetype)initWithTabsHostComponentView:(nullable RNSTabsHostComponentView *)tabsHostComponentView
|
|
108
149
|
{
|
|
109
150
|
if (self = [self init]) {
|
|
@@ -146,9 +187,9 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
146
187
|
|
|
147
188
|
#pragma mark - Signals
|
|
148
189
|
|
|
149
|
-
- (void)setPendingNavigationStateUpdate:(nullable
|
|
190
|
+
- (void)setPendingNavigationStateUpdate:(nullable RNSTabsNavigationStateUpdateRequest *)stateUpdate
|
|
150
191
|
{
|
|
151
|
-
|
|
192
|
+
_pendingStateUpdate = stateUpdate;
|
|
152
193
|
}
|
|
153
194
|
|
|
154
195
|
- (void)childViewControllersHaveChangedTo:(NSArray<RNSTabsScreenViewController *> *)reactChildControllers
|
|
@@ -232,11 +273,10 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
232
273
|
|
|
233
274
|
UIViewController *currSelectedViewController = self.selectedViewController;
|
|
234
275
|
|
|
235
|
-
RCTAssert(
|
|
236
|
-
|
|
237
|
-
@"[RNScreens] The screenKey MUST NOT be null if the view controller is not null");
|
|
276
|
+
RCTAssert(![NSString rnscreens_isBlankOrNull:screenKey],
|
|
277
|
+
@"[RNScreens] The screenKey MUST NOT be null if the view controller is not null");
|
|
238
278
|
|
|
239
|
-
[self progressNavigationState:screenKey
|
|
279
|
+
[self progressNavigationState:screenKey withOrigin:RNSTabsActionOriginProgrammaticJs];
|
|
240
280
|
|
|
241
281
|
if (currSelectedViewController == nextSelectedViewController) {
|
|
242
282
|
return YES;
|
|
@@ -254,14 +294,13 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
254
294
|
*/
|
|
255
295
|
- (void)updateNavigationStateOnModelUpdate
|
|
256
296
|
{
|
|
257
|
-
[self progressNavigationState:[self screenKeyForSelectedViewController]
|
|
258
|
-
withSource:RNSTabsNavigationStateUpdateSourceUser];
|
|
297
|
+
[self progressNavigationState:[self screenKeyForSelectedViewController] withOrigin:RNSTabsActionOriginUser];
|
|
259
298
|
}
|
|
260
299
|
|
|
261
300
|
- (void)userDidRepeatViewControllerSelection:(nonnull UIViewController *)viewController
|
|
262
301
|
{
|
|
263
|
-
RCTAssert(
|
|
264
|
-
|
|
302
|
+
RCTAssert(self.selectedViewController == viewController,
|
|
303
|
+
@"[RNScreens] Expected UIKit to update selectedViewController");
|
|
265
304
|
|
|
266
305
|
if ([self isSelectedViewControllerTheMoreNavigationController]) {
|
|
267
306
|
// We don't want to run neither state update nor side effects.
|
|
@@ -277,15 +316,15 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
277
316
|
[[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
|
|
278
317
|
isRepeated:YES
|
|
279
318
|
hasTriggeredSpecialEffect:repeatedSelectionHandledBySpecialEffect
|
|
280
|
-
|
|
281
|
-
[
|
|
319
|
+
actionOrigin:RNSTabsActionOriginUser];
|
|
320
|
+
[_observerRegistry emitDidUpdateStateTo:_navigationState withContext:updateContext sender:self];
|
|
282
321
|
}
|
|
283
322
|
|
|
284
323
|
- (void)userDidSelectViewController:(nonnull UIViewController *)viewController
|
|
285
324
|
{
|
|
286
325
|
// At this moment the `UITabBarController` model is already updated.
|
|
287
|
-
RCTAssert(
|
|
288
|
-
|
|
326
|
+
RCTAssert(self.selectedViewController == viewController,
|
|
327
|
+
@"[RNScreens] Expected UIKit to update selectedViewController");
|
|
289
328
|
|
|
290
329
|
if ([self isSelectedViewControllerTheMoreNavigationController]) {
|
|
291
330
|
[self disableNavigationBarInMoreNavigationController];
|
|
@@ -293,20 +332,20 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
293
332
|
|
|
294
333
|
// We don't want to progress state in case a user selected the more navigation controller.
|
|
295
334
|
// Instead, we emit a dedicated event so JS knows the More tab was tapped.
|
|
296
|
-
[
|
|
335
|
+
[_observerRegistry emitDidSelectMoreTabWithCurrentState:_navigationState sender:self];
|
|
297
336
|
} else {
|
|
298
337
|
[self updateNavigationStateOnModelUpdate];
|
|
299
338
|
auto *updateContext = [[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
|
|
300
339
|
isRepeated:NO
|
|
301
340
|
hasTriggeredSpecialEffect:NO
|
|
302
|
-
|
|
303
|
-
[
|
|
341
|
+
actionOrigin:RNSTabsActionOriginUser];
|
|
342
|
+
[_observerRegistry emitDidUpdateStateTo:_navigationState withContext:updateContext sender:self];
|
|
304
343
|
}
|
|
305
344
|
}
|
|
306
345
|
|
|
307
346
|
- (void)onDidPreventUserFromSelectingViewControllerWithKey:(nonnull NSString *)screenKey
|
|
308
347
|
{
|
|
309
|
-
[
|
|
348
|
+
[_observerRegistry emitPreventedSelectionOf:screenKey currentState:_navigationState sender:self];
|
|
310
349
|
}
|
|
311
350
|
|
|
312
351
|
- (BOOL)shouldPreventNativeTabSelection:(nonnull UIViewController *)nextViewController
|
|
@@ -331,11 +370,10 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
331
370
|
RCTAssert(tabBarController == self, @"[RNScreens] Unexpected type of controller: %@", tabBarController.class);
|
|
332
371
|
|
|
333
372
|
// Can be UINavigationController in case of MoreNavigationController
|
|
334
|
-
RCTAssert(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
viewController.class);
|
|
373
|
+
RCTAssert([viewController isKindOfClass:RNSTabsScreenViewController.class] ||
|
|
374
|
+
[viewController isKindOfClass:UINavigationController.class],
|
|
375
|
+
@"[RNScreens] Unexpected type of controller: %@",
|
|
376
|
+
viewController.class);
|
|
339
377
|
|
|
340
378
|
// TODO: handle enforcing orientation with natively-driven tabs
|
|
341
379
|
|
|
@@ -384,11 +422,10 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
384
422
|
RCTAssert(self == tabBarController, @"[RNScreens] Unexpected type of controller: %@", tabBarController.class);
|
|
385
423
|
|
|
386
424
|
// Can be UINavigationController in case of MoreNavigationController
|
|
387
|
-
RCTAssert(
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
viewController.class);
|
|
425
|
+
RCTAssert([viewController isKindOfClass:RNSTabsScreenViewController.class] ||
|
|
426
|
+
[viewController isKindOfClass:UINavigationController.class],
|
|
427
|
+
@"[RNScreens] Unexpected type of controller: %@",
|
|
428
|
+
viewController.class);
|
|
392
429
|
|
|
393
430
|
[self userDidSelectViewController:viewController];
|
|
394
431
|
_isHandlingExplicitSelectionUpdate = NO;
|
|
@@ -401,10 +438,9 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
401
438
|
animated:(BOOL)animated
|
|
402
439
|
{
|
|
403
440
|
#if RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE
|
|
404
|
-
RCTAssert(
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
navigationController);
|
|
441
|
+
RCTAssert(self.moreNavigationController == navigationController,
|
|
442
|
+
@"[RNScreens] Unexpected view controller called delegate method: %@",
|
|
443
|
+
navigationController);
|
|
408
444
|
|
|
409
445
|
// The root view controller is of different type.
|
|
410
446
|
if ([viewController isKindOfClass:RNSTabsScreenViewController.class] &&
|
|
@@ -439,60 +475,58 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
439
475
|
|
|
440
476
|
- (void)updateSelectedViewControllerIfNeeded
|
|
441
477
|
{
|
|
442
|
-
if (
|
|
478
|
+
if (_pendingStateUpdate != nil) {
|
|
443
479
|
[self updateSelectedViewController];
|
|
444
480
|
}
|
|
445
481
|
}
|
|
446
482
|
|
|
447
483
|
- (void)updateSelectedViewController
|
|
448
484
|
{
|
|
449
|
-
if (
|
|
485
|
+
if (_pendingStateUpdate == nil || self.viewControllers.count == 0) {
|
|
450
486
|
return;
|
|
451
487
|
}
|
|
452
488
|
|
|
453
489
|
RNSLog(@"TabBarCtrl updateSelectedViewController");
|
|
454
490
|
[self updateSelectedViewControllerInner];
|
|
455
|
-
|
|
491
|
+
_pendingStateUpdate = nil;
|
|
456
492
|
}
|
|
457
493
|
|
|
458
494
|
/**
|
|
459
495
|
* NEVER call this method directly. Call the proper function `updateSelectedViewController`
|
|
460
496
|
*
|
|
461
|
-
* The logic is extracted to an inner method to correctly manage
|
|
497
|
+
* The logic is extracted to an inner method to correctly manage `_pendingStateUpdate` cleanup.
|
|
462
498
|
*/
|
|
463
499
|
- (void)updateSelectedViewControllerInner
|
|
464
500
|
{
|
|
465
501
|
UIViewController *_Nonnull currSelectedViewController = self.selectedViewController;
|
|
466
502
|
|
|
467
|
-
NSString *_Nonnull nextSelectedViewControllerKey =
|
|
503
|
+
NSString *_Nonnull nextSelectedViewControllerKey = _pendingStateUpdate.selectedScreenKey;
|
|
468
504
|
UIViewController *nextSelectedViewController = [self findChildViewControllerForKey:nextSelectedViewControllerKey];
|
|
469
505
|
|
|
470
|
-
RCTAssert(
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
currentState:_navigationState
|
|
485
|
-
withReason:RNSTabsNavigationStateRejectionReasonStale];
|
|
506
|
+
RCTAssert(nextSelectedViewController != nil,
|
|
507
|
+
@"[RNScreens] Failed to determine next selected view controller for key: %@",
|
|
508
|
+
nextSelectedViewControllerKey);
|
|
509
|
+
|
|
510
|
+
RCTAssert([nextSelectedViewController isKindOfClass:RNSTabsScreenViewController.class],
|
|
511
|
+
@"[RNScreens] nextSelectedViewController MUST be %@, got: %@",
|
|
512
|
+
RNSTabsScreenViewController.class,
|
|
513
|
+
nextSelectedViewController.class);
|
|
514
|
+
|
|
515
|
+
if (self.rejectStaleNavigationStateUpdates && [self isNavigationStateUpdateStale:_pendingStateUpdate]) {
|
|
516
|
+
[_observerRegistry emitRejectedStateUpdate:_pendingStateUpdate
|
|
517
|
+
currentState:_navigationState
|
|
518
|
+
withReason:RNSTabsNavigationStateRejectionReasonStale
|
|
519
|
+
sender:self];
|
|
486
520
|
return;
|
|
487
521
|
}
|
|
488
522
|
|
|
489
523
|
if (currSelectedViewController == nextSelectedViewController && _navigationState != nil) {
|
|
490
524
|
// Nothing to do, we don't allow for programmatic repeat selection, unless
|
|
491
525
|
// we're during first render.
|
|
492
|
-
[
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
526
|
+
[_observerRegistry emitRejectedStateUpdate:_pendingStateUpdate
|
|
527
|
+
currentState:_navigationState
|
|
528
|
+
withReason:RNSTabsNavigationStateRejectionReasonRepeated
|
|
529
|
+
sender:self];
|
|
496
530
|
return;
|
|
497
531
|
}
|
|
498
532
|
|
|
@@ -517,8 +551,8 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
517
551
|
[[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
|
|
518
552
|
isRepeated:NO
|
|
519
553
|
hasTriggeredSpecialEffect:NO
|
|
520
|
-
|
|
521
|
-
[
|
|
554
|
+
actionOrigin:_pendingStateUpdate.actionOrigin];
|
|
555
|
+
[_observerRegistry emitDidUpdateStateTo:_navigationState withContext:context sender:self];
|
|
522
556
|
}
|
|
523
557
|
}
|
|
524
558
|
|
|
@@ -562,10 +596,9 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
562
596
|
return nil;
|
|
563
597
|
}
|
|
564
598
|
for (UIViewController *viewController in self.viewControllers) {
|
|
565
|
-
RCTAssert(
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
viewController.class);
|
|
599
|
+
RCTAssert([viewController isKindOfClass:RNSTabsScreenViewController.class],
|
|
600
|
+
@"[RNScreens] Unexpected type of controller: %@",
|
|
601
|
+
viewController.class);
|
|
569
602
|
auto *screenViewController = static_cast<RNSTabsScreenViewController *>(viewController);
|
|
570
603
|
if ([screenViewController.getScreenKeyOrNull isEqualToString:screenKey]) {
|
|
571
604
|
return screenViewController;
|
|
@@ -574,8 +607,7 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
574
607
|
return nil;
|
|
575
608
|
}
|
|
576
609
|
|
|
577
|
-
- (void)progressNavigationState:(nonnull NSString *)newSelectedScreenKey
|
|
578
|
-
withSource:(RNSTabsNavigationStateUpdateSource)updateSource
|
|
610
|
+
- (void)progressNavigationState:(nonnull NSString *)newSelectedScreenKey withOrigin:(RNSTabsActionOrigin)origin
|
|
579
611
|
{
|
|
580
612
|
RCTAssert(newSelectedScreenKey != nil, @"[RNScreens] newSelectedScreenKey MUST NOT be nil");
|
|
581
613
|
|
|
@@ -587,7 +619,7 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
587
619
|
_navigationState = [RNSTabsNavigationState stateWithSelectedScreenKey:newSelectedScreenKey
|
|
588
620
|
provenance:_navigationState.provenance + 1];
|
|
589
621
|
|
|
590
|
-
if (
|
|
622
|
+
if (origin != RNSTabsActionOriginProgrammaticJs) {
|
|
591
623
|
_lastUINavigationState = [_navigationState cloneState];
|
|
592
624
|
}
|
|
593
625
|
}
|
|
@@ -598,20 +630,18 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
598
630
|
*/
|
|
599
631
|
- (RNSTabsScreenViewController *)selectedScreenViewController
|
|
600
632
|
{
|
|
601
|
-
RCTAssert(
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
self.selectedViewController.class);
|
|
633
|
+
RCTAssert([self.selectedViewController isKindOfClass:RNSTabsScreenViewController.class],
|
|
634
|
+
@"[RNScreens] Unexpected type of selectedViewController: %@",
|
|
635
|
+
self.selectedViewController.class);
|
|
605
636
|
return static_cast<RNSTabsScreenViewController *>(self.selectedViewController);
|
|
606
637
|
}
|
|
607
638
|
|
|
608
639
|
- (nonnull NSString *)screenKeyForViewController:(nonnull UIViewController *)viewController
|
|
609
640
|
{
|
|
610
|
-
RCTAssert(
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
viewController.class);
|
|
641
|
+
RCTAssert([viewController isKindOfClass:RNSTabsScreenViewController.class],
|
|
642
|
+
@"[RNScreens] Expected selected view controller to be of class %@, got: %@",
|
|
643
|
+
RNSTabsScreenViewController.class,
|
|
644
|
+
viewController.class);
|
|
615
645
|
|
|
616
646
|
auto *screenKey = static_cast<RNSTabsScreenViewController *>(viewController).getScreenKeyOrNull;
|
|
617
647
|
RCTAssert(screenKey != nil, @"[RNScreens] screenKey MUST NOT be nil");
|
|
@@ -650,10 +680,9 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
650
680
|
}
|
|
651
681
|
|
|
652
682
|
if (![self.selectedViewController isKindOfClass:RNSTabsScreenViewController.class]) {
|
|
653
|
-
RCTAssert(
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
self.selectedViewController.class);
|
|
683
|
+
RCTAssert(NO,
|
|
684
|
+
@"[RNScreens] Unexpected controller type during state reconciliation: %@",
|
|
685
|
+
self.selectedViewController.class);
|
|
657
686
|
return;
|
|
658
687
|
}
|
|
659
688
|
|
|
@@ -662,25 +691,24 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
662
691
|
return;
|
|
663
692
|
}
|
|
664
693
|
|
|
665
|
-
RNSLog(
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
[self progressNavigationState:selectedScreenKey withSource:RNSTabsNavigationStateUpdateSourceImplicit];
|
|
694
|
+
RNSLog(@"TabBarCtrl reconcileNavigationStateWithUIKitState: %@ -> %@",
|
|
695
|
+
_navigationState.selectedScreenKey,
|
|
696
|
+
selectedScreenKey);
|
|
697
|
+
[self progressNavigationState:selectedScreenKey withOrigin:RNSTabsActionOriginImplicit];
|
|
670
698
|
|
|
671
699
|
auto *context = [[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
|
|
672
700
|
isRepeated:NO
|
|
673
701
|
hasTriggeredSpecialEffect:NO
|
|
674
|
-
|
|
675
|
-
[
|
|
702
|
+
actionOrigin:RNSTabsActionOriginImplicit];
|
|
703
|
+
[_observerRegistry emitDidUpdateStateTo:_navigationState withContext:context sender:self];
|
|
676
704
|
}
|
|
677
705
|
|
|
678
706
|
/**
|
|
679
707
|
* This function assumes that the source of the state is NOT user. In current model, user update is never stale.
|
|
680
708
|
*/
|
|
681
|
-
- (BOOL)isNavigationStateUpdateStale:(nullable
|
|
709
|
+
- (BOOL)isNavigationStateUpdateStale:(nullable RNSTabsNavigationStateUpdateRequest *)stateUpdate
|
|
682
710
|
{
|
|
683
|
-
if (
|
|
711
|
+
if (stateUpdate == nil) {
|
|
684
712
|
return YES;
|
|
685
713
|
}
|
|
686
714
|
|
|
@@ -688,7 +716,7 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
688
716
|
return NO;
|
|
689
717
|
}
|
|
690
718
|
|
|
691
|
-
return
|
|
719
|
+
return stateUpdate.baseProvenance < _lastUINavigationState.provenance;
|
|
692
720
|
}
|
|
693
721
|
|
|
694
722
|
#pragma mark-- More Navigation Controller
|
|
@@ -754,10 +782,9 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
754
782
|
// We quietly assume here, that the root view controller is the `UIMoreListViewController`.
|
|
755
783
|
if (shouldRespectSelectionPrevention) {
|
|
756
784
|
UIViewController *topViewController = self.moreNavigationController.topViewController;
|
|
757
|
-
RCTAssert(
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
topViewController.class);
|
|
785
|
+
RCTAssert([topViewController isKindOfClass:RNSTabsScreenViewController.class],
|
|
786
|
+
@"[RNScreens] Unexpected type of view controller on moreNavigationControllerStack: %@",
|
|
787
|
+
topViewController.class);
|
|
761
788
|
RNSTabsScreenViewController *screenController = static_cast<RNSTabsScreenViewController *>(topViewController);
|
|
762
789
|
if (screenController.isPreventNativeSelectionEnabled) {
|
|
763
790
|
return [self popToRootMoreNavigationController:self.moreNavigationController animated:shouldAnimate];
|
|
@@ -785,9 +812,8 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
785
812
|
}
|
|
786
813
|
|
|
787
814
|
auto *poppedViewControllers = [moreNavigationController popToRootViewControllerAnimated:animated];
|
|
788
|
-
RCTAssert(
|
|
789
|
-
|
|
790
|
-
@"[RNScreens] Expected exactly one view controller to be popped");
|
|
815
|
+
RCTAssert(poppedViewControllers != nil && poppedViewControllers.count == 1,
|
|
816
|
+
@"[RNScreens] Expected exactly one view controller to be popped");
|
|
791
817
|
return [poppedViewControllers firstObject];
|
|
792
818
|
}
|
|
793
819
|
|
|
@@ -842,11 +868,10 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
842
868
|
RCTAssert(dynamicSubclass != nil, @"[RNScreens] Failed to allocate dynamic subclass of %s", currentClassName);
|
|
843
869
|
|
|
844
870
|
Method pushMethod = class_getInstanceMethod(currentClass, @selector(pushViewController:animated:));
|
|
845
|
-
class_addMethod(
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
method_getTypeEncoding(pushMethod));
|
|
871
|
+
class_addMethod(dynamicSubclass,
|
|
872
|
+
@selector(pushViewController:animated:),
|
|
873
|
+
(IMP)rns_pushViewController,
|
|
874
|
+
method_getTypeEncoding(pushMethod));
|
|
850
875
|
|
|
851
876
|
objc_registerClassPair(dynamicSubclass);
|
|
852
877
|
}
|
|
@@ -982,9 +1007,8 @@ rns_pushViewController(__unsafe_unretained id self, SEL _cmd, UIViewController *
|
|
|
982
1007
|
}
|
|
983
1008
|
#endif // RNS_IPHONE_OS_VERSION_AVAILABLE(17_0)
|
|
984
1009
|
|
|
985
|
-
RCTAssert(
|
|
986
|
-
|
|
987
|
-
@"[RNScreens] Expected non-null parent view controller for layout direction update.");
|
|
1010
|
+
RCTAssert(self.parentViewController != nil,
|
|
1011
|
+
@"[RNScreens] Expected non-null parent view controller for layout direction update.");
|
|
988
1012
|
[self.parentViewController
|
|
989
1013
|
setOverrideTraitCollection:[UITraitCollection
|
|
990
1014
|
traitCollectionWithLayoutDirection:self.tabsHostComponentView.layoutDirection]
|
|
@@ -26,14 +26,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
26
26
|
* 2. provider of React state & props for the tab bar controller
|
|
27
27
|
* 3. two way communication channel with React (commands & events)
|
|
28
28
|
*/
|
|
29
|
-
@interface RNSTabsHostComponentView : RNSReactBaseView <
|
|
30
|
-
|
|
31
|
-
RNSTabBarControllerDelegate
|
|
29
|
+
@interface RNSTabsHostComponentView : RNSReactBaseView <RNSScreenContainerDelegate,
|
|
30
|
+
RNSTabsNavigationStateObserver
|
|
32
31
|
#if !RCT_NEW_ARCH_ENABLED
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
,
|
|
33
|
+
RCTInvalidating
|
|
35
34
|
#endif
|
|
36
|
-
|
|
35
|
+
>
|
|
37
36
|
|
|
38
37
|
#if !RCT_NEW_ARCH_ENABLED
|
|
39
38
|
- (instancetype)initWithFrame:(CGRect)frame reactImageLoader:(RCTImageLoader *)imageLoader;
|
|
@@ -48,9 +47,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
48
47
|
@interface RNSTabsHostComponentView ()
|
|
49
48
|
|
|
50
49
|
/**
|
|
51
|
-
* Last navigation state requested by JS. Will be nonnull after first prop update.
|
|
50
|
+
* Last navigation state update requested by JS. Will be nonnull after first prop update.
|
|
52
51
|
*/
|
|
53
|
-
@property (nonatomic, strong, readonly, nullable)
|
|
52
|
+
@property (nonatomic, strong, readonly, nullable) RNSTabsNavigationStateUpdateRequest *navStateRequest;
|
|
54
53
|
|
|
55
54
|
@property (nonatomic, readonly) BOOL rejectStaleNavStateUpdates;
|
|
56
55
|
|