feat(tabs): add 'programmatic-native' actionOrigin variant#3951
feat(tabs): add 'programmatic-native' actionOrigin variant#3951
Conversation
fefd025 to
0d7d7fb
Compare
Adds a fourth value to the public `TabSelectedEvent.actionOrigin` union: `'user' | 'programmatic-js' | 'programmatic-native' | 'implicit'`. The new origin is reserved for downstream libraries that integrate directly against `TabsContainer` (Android) or `RNSTabBarController` (iOS) — this library itself does not produce it. Plumbed end-to-end: - Public TS contract + both Android/iOS codegen specs. - iOS: `RNSTabsActionOriginProgrammaticNative` enum case and the matching arm in the conversion helper that maps the Obj-C enum to the codegen C++ scoped enum. - Android: `TabsActionOrigin.PROGRAMMATIC_NATIVE` + `toString()` mapping. Android visibility widening to make the new origin reachable from outside the library (iOS already exposes the equivalent surface): - `TabsContainer` class is now public; constructor stays `internal` because it takes the still-`internal` `TabsContainerDelegate`. Downstream is expected to obtain the container reference via its own integration path (TabsHost.container stays `private`). - `setContainerOperation(TabsContainerOp)` is replaced with a public `setPendingNavigationStateUpdate(TabsNavStateUpdateRequest)` that takes the request directly and wraps in `TabSelectOp` internally. Naming mirrors `RNSTabBarController.setPendingNavigationStateUpdate:`. - `performContainerUpdateIfNeeded()` is now public — the flush trigger downstream calls after setting a pending update. - Internal sealed-class machinery (`TabsContainerOp`, `TabSelectOp`) stays internal. Also reformats iOS doc comments in `RNSTabsNavigationState.h` to per-case style and the `/**\n * ...\n */` form consistently.
f09f976 to
5bc649e
Compare
There was a problem hiding this comment.
Pull request overview
Extends the public TabSelectedEvent.actionOrigin API to include a new 'programmatic-native' variant, enabling downstream native integrations to attribute tab changes initiated directly by native container/controller APIs.
Changes:
- Extend the JS/TS and codegen event contract to include
'programmatic-native'foractionOrigin. - iOS: add
RNSTabsActionOriginProgrammaticNativeand map it to the codegen event enum inRNSConversions-Tabs.mm; reformat doc comments inRNSTabsNavigationState.h. - Android: add
TabsActionOrigin.PROGRAMMATIC_NATIVE, and widenTabsContainervisibility plus expose public methods to set/flush pending navigation state updates.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/fabric/tabs/TabsHostIOSNativeComponent.ts | Extends codegen event union with 'programmatic-native'. |
| src/fabric/tabs/TabsHostAndroidNativeComponent.ts | Extends codegen event union with 'programmatic-native'. |
| src/components/tabs/host/TabsHost.types.ts | Extends public TS actionOrigin union and docs. |
| ios/tabs/host/RNSTabsNavigationState.h | Adds iOS enum case and reformats doc comments. |
| ios/conversion/RNSConversions-Tabs.mm | Maps Obj-C action origin enum to codegen event enum, adding native-programmatic arm. |
| android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/TabsHost.kt | Updates host to use new TabsContainer API for pending updates. |
| android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsContainer.kt | Makes container public and exposes pending-update + flush entrypoints. |
| android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsActionOrigin.kt | Adds PROGRAMMATIC_NATIVE and string mapping. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
kligarski
left a comment
There was a problem hiding this comment.
For iOS, what is the intended flow for programmatic native operation?
Is it setPendingNavigationStateUpdate and then updateSelectedViewControllerIfNeeded? But this way we're missing setting _isHandlingExplicitSelectionUpdate flag which is done in performContainerUpdate and performContainerUpdate is not public.
Currently, if we use updateSelectedViewControllerIfNeeded, reconcileNavigationStateWithUIKitState is called (because we're not setting _isHandlingExplicitSelectionUpdate). Fortunately, we're early-returning via [_navigationState.selectedScreenKey isEqualToString:selectedScreenKey] check but I'm not sure if this was intended?
I also thought whether we should allow anyone to use setPendingNavigationStateUpdate and set any actionOrigin or maybe only expose a method that will accept only the key (and baseProvenance?) that will enforce ProgrammaticNative origin.
Description
Adds a fourth value to the public
TabSelectedEvent.actionOriginunion:'user' | 'programmatic-js' | 'programmatic-native' | 'implicit'. The new origin is reserved for downstream libraries that integrate directly againstTabsContainer(Android) orRNSTabBarController(iOS) — this library itself does not produce it.The recent state/request split (PR #3950) already plumbs
actionOriginfrom the buffered request through to the emitted event, so adding the variant is mostly additive: union extension on the JS / codegen side, one enum case on each native platform, one switch arm in the iOS conversion helper.iOS already exposed the surface that downstream needs (
RNSTabBarController.setPendingNavigationStateUpdate:is on the public header). On Android,TabsContainerand its pending-op + flush methods wereinternal, so this PR also widens the Android surface to match: the class is public, the request-passing and flushing methods are public, and the rest stays internal.TabsHost.containerstaysprivate— downstream is expected to obtain the container reference through its own integration path, not via the React view.Stacked on #3950.
Changes
TabSelectedEvent.actionOriginunion extended with'programmatic-native'. Codegen specs (Android + iOS) mirror it.RNSTabsActionOriginProgrammaticNativeenum case + matching arm inRNSOnTabSelectedActionOriginFromRNSTabsActionOriginmapping the Obj-C enum to the codegen C++ scoped enum.TabsActionOrigin.PROGRAMMATIC_NATIVE+toString()mapping.TabsContainerclass is now public; constructor staysinternal(it takes the still-internalTabsContainerDelegate).setContainerOperation(TabsContainerOp)replaced with publicsetPendingNavigationStateUpdate(TabsNavStateUpdateRequest)— takes the request directly, wraps inTabSelectOpinternally. Naming mirrors the iOS selector.performContainerUpdateIfNeeded()is public — the flush trigger.TabsContainerOp/TabSelectOpstayinternal.RNSTabsNavigationState.hreformatted: per-case/** … */blocks forRNSTabsActionOrigin(and the rest of the file made consistent with/**\n * …\n */style).Test plan
yarn check-typesclean.yarn lintclean for changed files (only pre-existing warnings in unrelated code).yarn androidfromFabricExample/— Android codegen + Kotlin compile clean. App installs on emulator.git grep -n "programmatic-native" -- src/→ 3 hits (public TS type + both codegen specs).git grep -n "RNSTabsActionOriginProgrammaticNative" -- ios/→ hits inRNSTabsNavigationState.handRNSConversions-Tabs.mm.git grep -n "PROGRAMMATIC_NATIVE" -- android/→ hits inTabsActionOrigin.kt.git grep -n "setContainerOperation" -- android/→ zero hits (renamed).git grep -nE "internal class TabsContainer\b" -- android/→ zero hits (class made public).bundle exec pod install && yarn ios(regenerates iOS codegen with the extended union) — pending local Mac toolchain run.programmatic-nativeis out of scope — this library has no producer call site.Checklist