Skip to content

Commit 4615c77

Browse files
committed
refactor(tabs): replace isNativeAction with actionOrigin enum
TabSelectedEvent previously carried a boolean `isNativeAction`, collapsing two distinct origins of native-driven tab selections (user tap, UIKit reshuffle on size-class transition) into the same value. Replace it with `actionOrigin: 'user' | 'js' | 'implicit'`, matching the origin set defined in RFC-1028. iOS already distinguishes all three internally — the `implicit` case is now exposed instead of being collapsed into `true`. Android emits only `'user'` and `'js'`; no `'implicit'` code path exists there today. While here, consolidate the Obj-C origin model: the existing `RNSTabsNavigationStateUpdateSource` enum is renamed to `RNSTabsActionOrigin` (cases `User`, `ProgrammaticJs`, `Implicit`) so we have a single representation across internal state transitions and the event payload. The private method `progressNavigationState:withSource:` becomes `…withOrigin:`. No JS/RN consumers (incl. the react-navigation submodule) read the old field today, so this is a contract change with no behavior fallout.
1 parent 8c915dc commit 4615c77

17 files changed

Lines changed: 101 additions & 46 deletions
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.swmansion.rnscreens.gamma.tabs.container
2+
3+
import com.swmansion.rnscreens.gamma.tabs.container.TabsActionOrigin.JS
4+
import com.swmansion.rnscreens.gamma.tabs.container.TabsActionOrigin.USER
5+
6+
/**
7+
* Origin (actor) that requested a tab transition. Mirrors the public `actionOrigin` event field.
8+
*
9+
* - [USER] — direct native UI interaction (tab bar tap).
10+
* - [JS] — JS-initiated request delivered via the `navStateRequest` prop.
11+
*
12+
* The `implicit` origin defined on the public TS API is iOS-only at the moment;
13+
* Android does not currently produce it.
14+
*/
15+
enum class TabsActionOrigin {
16+
USER,
17+
JS,
18+
;
19+
20+
override fun toString(): String =
21+
when (this) {
22+
USER -> "user"
23+
JS -> "js"
24+
}
25+
}

android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsContainer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ internal class TabsContainer(
377377
navState,
378378
isRepeated = isRepeated,
379379
hasTriggeredSpecialEffect = hasTriggeredSpecialEffect,
380-
isNativeAction = !isInExternalOperationContext,
380+
actionOrigin = if (isInExternalOperationContext) TabsActionOrigin.JS else TabsActionOrigin.USER,
381381
)
382382
}
383383

android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsContainerDelegate.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ internal interface TabsContainerDelegate {
1111
* @param navState The new navigation state after the change.
1212
* @param isRepeated Whether the same tab that was already selected has been selected again.
1313
* @param hasTriggeredSpecialEffect Whether a special effect (e.g. scroll-to-top) was triggered.
14-
* @param isNativeAction Whether the change was initiated by a native user action (tap).
14+
* @param actionOrigin Origin (actor) that requested this transition.
1515
*/
1616
fun onNavStateUpdate(
1717
navState: TabsNavState,
1818
isRepeated: Boolean,
1919
hasTriggeredSpecialEffect: Boolean,
20-
isNativeAction: Boolean,
20+
actionOrigin: TabsActionOrigin,
2121
)
2222

2323
/**

android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/TabsHost.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.facebook.react.uimanager.ThemedReactContext
1212
import com.facebook.react.uimanager.UIManagerHelper
1313
import com.swmansion.rnscreens.gamma.common.colorscheme.ColorScheme
1414
import com.swmansion.rnscreens.gamma.helpers.getFabricUIManagerNotNull
15+
import com.swmansion.rnscreens.gamma.tabs.container.TabsActionOrigin
1516
import com.swmansion.rnscreens.gamma.tabs.container.TabSelectOp
1617
import com.swmansion.rnscreens.gamma.tabs.container.TabsContainer
1718
import com.swmansion.rnscreens.gamma.tabs.container.TabsContainerDelegate
@@ -159,14 +160,14 @@ class TabsHost(
159160
navState: TabsNavState,
160161
isRepeated: Boolean,
161162
hasTriggeredSpecialEffect: Boolean,
162-
isNativeAction: Boolean,
163+
actionOrigin: TabsActionOrigin,
163164
) {
164165
eventEmitter.emitOnTabSelectedEvent(
165166
navState.selectedKey,
166167
navState.provenance,
167168
isRepeated,
168169
hasTriggeredSpecialEffect,
169-
isNativeAction,
170+
actionOrigin,
170171
)
171172
}
172173

android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/TabsHostEventEmitter.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.swmansion.rnscreens.gamma.tabs.host
22

33
import com.facebook.react.bridge.ReactContext
44
import com.swmansion.rnscreens.gamma.common.event.BaseEventEmitter
5+
import com.swmansion.rnscreens.gamma.tabs.container.TabsActionOrigin
56
import com.swmansion.rnscreens.gamma.tabs.container.TabsNavState
67
import com.swmansion.rnscreens.gamma.tabs.container.TabsNavStateUpdateRejectionReason
78
import com.swmansion.rnscreens.gamma.tabs.host.event.TabsHostTabSelectedEvent
@@ -20,7 +21,7 @@ internal class TabsHostEventEmitter(
2021
provenance: Int,
2122
isRepeated: Boolean,
2223
hasTriggeredSpecialEffect: Boolean,
23-
isNativeAction: Boolean,
24+
actionOrigin: TabsActionOrigin,
2425
) {
2526
reactEventDispatcher.dispatchEvent(
2627
TabsHostTabSelectedEvent(
@@ -30,7 +31,7 @@ internal class TabsHostEventEmitter(
3031
provenance,
3132
isRepeated,
3233
hasTriggeredSpecialEffect,
33-
isNativeAction,
34+
actionOrigin,
3435
),
3536
)
3637
}

android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/event/TabsHostTabSelectedEvent.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.facebook.react.bridge.Arguments
44
import com.facebook.react.bridge.WritableMap
55
import com.facebook.react.uimanager.events.Event
66
import com.swmansion.rnscreens.gamma.common.event.NamingAwareEventType
7+
import com.swmansion.rnscreens.gamma.tabs.container.TabsActionOrigin
78

89
class TabsHostTabSelectedEvent(
910
surfaceId: Int,
@@ -12,7 +13,7 @@ class TabsHostTabSelectedEvent(
1213
val provenance: Int,
1314
val isRepeated: Boolean,
1415
val hasTriggeredSpecialEffect: Boolean,
15-
val isNativeAction: Boolean,
16+
val actionOrigin: TabsActionOrigin,
1617
) : Event<TabsHostTabSelectedEvent>(surfaceId, viewId),
1718
NamingAwareEventType {
1819
override fun getEventName() = EVENT_NAME
@@ -28,7 +29,7 @@ class TabsHostTabSelectedEvent(
2829
putInt(EK_PROVENANCE, provenance)
2930
putBoolean(EK_IS_REPEATED, isRepeated)
3031
putBoolean(EK_HAS_TRIGGERED_SPECIAL_EFFECT, hasTriggeredSpecialEffect)
31-
putBoolean(EK_IS_NATIVE_ACTION, isNativeAction)
32+
putString(EK_ACTION_ORIGIN, actionOrigin.toString())
3233
}
3334

3435
companion object : NamingAwareEventType {
@@ -39,7 +40,7 @@ class TabsHostTabSelectedEvent(
3940
private const val EK_PROVENANCE = "provenance"
4041
private const val EK_IS_REPEATED = "isRepeated"
4142
private const val EK_HAS_TRIGGERED_SPECIAL_EFFECT = "hasTriggeredSpecialEffect"
42-
private const val EK_IS_NATIVE_ACTION = "isNativeAction"
43+
private const val EK_ACTION_ORIGIN = "actionOrigin"
4344

4445
override fun getEventName() = EVENT_NAME
4546

ios/conversion/RNSConversions-Tabs.mm

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,20 @@ UITabBarControllerMode UITabBarControllerModeFromRNSTabBarControllerMode(RNSTabB
233233
}
234234
}
235235

236+
react::RNSTabsHostIOSEventEmitter::OnTabSelectedActionOrigin RNSOnTabSelectedActionOriginFromRNSTabsActionOrigin(
237+
RNSTabsActionOrigin actionOrigin)
238+
{
239+
using enum facebook::react::RNSTabsHostIOSEventEmitter::OnTabSelectedActionOrigin;
240+
switch (actionOrigin) {
241+
case RNSTabsActionOriginUser:
242+
return User;
243+
case RNSTabsActionOriginProgrammaticJs:
244+
return Js;
245+
case RNSTabsActionOriginImplicit:
246+
return Implicit;
247+
}
248+
}
249+
236250
RNSTabsIconType RNSTabsIconTypeFromIcon(react::RNSTabsScreenIOSIconType iconType)
237251
{
238252
using enum facebook::react::RNSTabsScreenIOSIconType;

ios/conversion/RNSConversions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ react::RNSTabsHostIOSEventEmitter::OnTabSelectionRejectedRejectionReason
6363
RNSOnTabSelectionRejectedRejectionReasonFromRNSTabsNavigationStateRejectionReason(
6464
RNSTabsNavigationStateRejectionReason reason);
6565

66+
react::RNSTabsHostIOSEventEmitter::OnTabSelectedActionOrigin RNSOnTabSelectedActionOriginFromRNSTabsActionOrigin(
67+
RNSTabsActionOrigin actionOrigin);
68+
6669
RNSTabsIconType RNSTabsIconTypeFromIcon(react::RNSTabsScreenIOSIconType iconType);
6770

6871
RNSTabsScreenSystemItem RNSTabsScreenSystemItemFromReactRNSTabsScreenSystemItem(

ios/tabs/host/RNSTabBarController.mm

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ - (BOOL)updateSelectedViewControllerTo:(nullable UIViewController *)nextSelected
236236
![NSString rnscreens_isBlankOrNull:screenKey],
237237
@"[RNScreens] The screenKey MUST NOT be null if the view controller is not null");
238238

239-
[self progressNavigationState:screenKey withSource:RNSTabsNavigationStateUpdateSourceExternal];
239+
[self progressNavigationState:screenKey withOrigin:RNSTabsActionOriginProgrammaticJs];
240240

241241
if (currSelectedViewController == nextSelectedViewController) {
242242
return YES;
@@ -254,8 +254,7 @@ - (BOOL)updateSelectedViewControllerTo:(nullable UIViewController *)nextSelected
254254
*/
255255
- (void)updateNavigationStateOnModelUpdate
256256
{
257-
[self progressNavigationState:[self screenKeyForSelectedViewController]
258-
withSource:RNSTabsNavigationStateUpdateSourceUser];
257+
[self progressNavigationState:[self screenKeyForSelectedViewController] withOrigin:RNSTabsActionOriginUser];
259258
}
260259

261260
- (void)userDidRepeatViewControllerSelection:(nonnull UIViewController *)viewController
@@ -277,7 +276,7 @@ - (void)userDidRepeatViewControllerSelection:(nonnull UIViewController *)viewCon
277276
[[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
278277
isRepeated:YES
279278
hasTriggeredSpecialEffect:repeatedSelectionHandledBySpecialEffect
280-
isNativeAction:YES];
279+
actionOrigin:RNSTabsActionOriginUser];
281280
[self.tabsHostComponentView tabBarController:self didUpdateStateTo:_navigationState withContext:updateContext];
282281
}
283282

@@ -299,7 +298,7 @@ - (void)userDidSelectViewController:(nonnull UIViewController *)viewController
299298
auto *updateContext = [[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
300299
isRepeated:NO
301300
hasTriggeredSpecialEffect:NO
302-
isNativeAction:YES];
301+
actionOrigin:RNSTabsActionOriginUser];
303302
[self.tabsHostComponentView tabBarController:self didUpdateStateTo:_navigationState withContext:updateContext];
304303
}
305304
}
@@ -517,7 +516,7 @@ - (void)updateSelectedViewControllerInner
517516
[[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
518517
isRepeated:NO
519518
hasTriggeredSpecialEffect:NO
520-
isNativeAction:NO];
519+
actionOrigin:RNSTabsActionOriginProgrammaticJs];
521520
[self.tabsHostComponentView tabBarController:self didUpdateStateTo:_navigationState withContext:context];
522521
}
523522
}
@@ -574,8 +573,7 @@ - (nullable RNSTabsScreenViewController *)findChildViewControllerForKey:(nullabl
574573
return nil;
575574
}
576575

577-
- (void)progressNavigationState:(nonnull NSString *)newSelectedScreenKey
578-
withSource:(RNSTabsNavigationStateUpdateSource)updateSource
576+
- (void)progressNavigationState:(nonnull NSString *)newSelectedScreenKey withOrigin:(RNSTabsActionOrigin)origin
579577
{
580578
RCTAssert(newSelectedScreenKey != nil, @"[RNScreens] newSelectedScreenKey MUST NOT be nil");
581579

@@ -587,7 +585,7 @@ - (void)progressNavigationState:(nonnull NSString *)newSelectedScreenKey
587585
_navigationState = [RNSTabsNavigationState stateWithSelectedScreenKey:newSelectedScreenKey
588586
provenance:_navigationState.provenance + 1];
589587

590-
if (updateSource != RNSTabsNavigationStateUpdateSourceExternal) {
588+
if (origin != RNSTabsActionOriginProgrammaticJs) {
591589
_lastUINavigationState = [_navigationState cloneState];
592590
}
593591
}
@@ -666,12 +664,12 @@ - (void)reconcileNavigationStateWithUIKitState
666664
@"TabBarCtrl reconcileNavigationStateWithUIKitState: %@ -> %@",
667665
_navigationState.selectedScreenKey,
668666
selectedScreenKey);
669-
[self progressNavigationState:selectedScreenKey withSource:RNSTabsNavigationStateUpdateSourceImplicit];
667+
[self progressNavigationState:selectedScreenKey withOrigin:RNSTabsActionOriginImplicit];
670668

671669
auto *context = [[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
672670
isRepeated:NO
673671
hasTriggeredSpecialEffect:NO
674-
isNativeAction:YES];
672+
actionOrigin:RNSTabsActionOriginImplicit];
675673
[self.tabsHostComponentView tabBarController:self didUpdateStateTo:_navigationState withContext:context];
676674
}
677675

ios/tabs/host/RNSTabsHostComponentView.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ - (void)tabBarController:(nonnull RNSTabBarController *)tabBarController
609609
.provenance = navState.provenance,
610610
.isRepeated = context.isRepeated,
611611
.hasTriggeredSpecialEffect = context.hasTriggeredSpecialEffect,
612-
.isNativeAction = context.isNativeAction}];
612+
.actionOrigin = context.actionOrigin}];
613613
}
614614

615615
- (void)tabBarController:(nonnull RNSTabBarController *)tabBarController

0 commit comments

Comments
 (0)