Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class TabsHost(
TabsContainerDelegate,
UIManagerListener {
private val renderedScreens: ArrayList<TabsScreen> = arrayListOf()
private var jsNavState: TabsNavState = TabsNavState.EMPTY
private var jsNavStateRequest: TabsNavState = TabsNavState.EMPTY

private val container: TabsContainer =
TabsContainer(reactContext, this).apply {
Expand Down Expand Up @@ -109,9 +109,9 @@ class TabsHost(
container.removeAllTabsScreens()
}

internal fun updateJSNavState(navState: TabsNavState) {
jsNavState = navState
container.setContainerOperation(TabSelectOp(jsNavState.copy()))
internal fun updateJSNavStateRequest(navStateRequest: TabsNavState) {
jsNavStateRequest = navStateRequest
container.setContainerOperation(TabSelectOp(jsNavStateRequest.copy()))
}

private val layoutCallback =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ class TabsHostViewManager :
view.onViewManagerAddEventEmitters()
}

override fun setNavState(
override fun setNavStateRequest(
view: TabsHost,
value: ReadableMap?,
) {
val navStateMap = requireNotNull(value) { "[RNScreens] NavState must not be nullish" }
val selectedScreenKey = requireNotNull(navStateMap.getString("selectedScreenKey"))
val provenance = requireNotNull(navStateMap.getInt("provenance"))
view.updateJSNavState(TabsNavState(selectedScreenKey, provenance))
val navStateRequestMap = requireNotNull(value) { "[RNScreens] navStateRequest must not be nullish" }
val selectedScreenKey = requireNotNull(navStateRequestMap.getString("selectedScreenKey"))
val baseProvenance = requireNotNull(navStateRequestMap.getInt("baseProvenance"))
view.updateJSNavStateRequest(TabsNavState(selectedScreenKey, baseProvenance))
}

override fun setRejectStaleNavStateUpdates(
Expand Down
16 changes: 8 additions & 8 deletions apps/src/shared/gamma/containers/tabs/TabsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { I18nManager, type NativeSyntheticEvent } from 'react-native';
import {
type TabSelectedEvent,
Tabs,
type TabsHostNavState,
type TabsHostNavStateRequest,
} from 'react-native-screens';
import type {
SelectTabMethod,
Expand Down Expand Up @@ -43,7 +43,7 @@ export function TabsContainer(props: TabsContainerProps) {
determineInitialTabsContainerState,
);

const hostNavState = useTabsHostNavState(tabsNavState);
const hostNavStateRequest = useTabsHostNavStateRequest(tabsNavState);

const onTabSelectedCallback = React.useCallback(
(event: NativeSyntheticEvent<TabSelectedEvent>) => {
Expand Down Expand Up @@ -74,7 +74,7 @@ export function TabsContainer(props: TabsContainerProps) {

return (
<Tabs.Host
navState={hostNavState}
navStateRequest={hostNavStateRequest}
onTabSelected={onTabSelectedCallback}
direction={I18nManager.isRTL ? 'rtl' : 'ltr'}
{...restProps}>
Expand All @@ -90,17 +90,17 @@ export function TabsContainer(props: TabsContainerProps) {
);
}

function useTabsHostNavState(
function useTabsHostNavStateRequest(
tabsNavState: TabsContainerState,
): TabsHostNavState {
const hostNavState: TabsHostNavState = React.useMemo(() => {
): TabsHostNavStateRequest {
const hostNavStateRequest: TabsHostNavStateRequest = React.useMemo(() => {
return {
selectedScreenKey: tabsNavState.suggestedState.selectedRouteKey,
provenance: tabsNavState.suggestedState.provenance,
baseProvenance: tabsNavState.suggestedState.provenance,
};
}, [tabsNavState.suggestedState]);

return hostNavState;
return hostNavStateRequest;
}

function useSanitizeRouteConfigs(routeConfigs: TabRouteConfig[]) {
Expand Down
4 changes: 2 additions & 2 deletions apps/src/shared/gamma/containers/tabs/TabsContainer.types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ export type TabsNavigationAction =
export type TabsHostConfig = Omit<
TabsHostProps,
| 'children'
| 'navState'
| 'navStateRequest'
>;

export type TabsContainerProps = Omit<
TabsHostProps,
'children' | 'navState'
'children' | 'navStateRequest'
> & {
routeConfigs: TabRouteConfig[];
/**
Expand Down
2 changes: 1 addition & 1 deletion ios/tabs/host/RNSTabsHostComponentView.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
* Last navigation state requested by JS. Will be nonnull after first prop update.
*/
@property (nonatomic, strong, readonly, nullable) RNSTabsNavigationState *navState;
@property (nonatomic, strong, readonly, nullable) RNSTabsNavigationState *navStateRequest;

@property (nonatomic, readonly) BOOL rejectStaleNavStateUpdates;

Expand Down
17 changes: 9 additions & 8 deletions ios/tabs/host/RNSTabsHostComponentView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ @implementation RNSTabsHostComponentView {
BOOL _hasModifiedBottomAccessoryInCurrentTransation;
BOOL _needsTabBarAppearanceUpdate;

RNSTabsNavigationState *_Nullable _jsNavState;
RNSTabsNavigationState *_Nullable _navStateRequest;
}

- (instancetype)initWithFrame:(CGRect)frame
Expand Down Expand Up @@ -268,14 +268,15 @@ - (void)updateProps:(const facebook::react::Props::Shared &)props
const auto &oldComponentProps = *std::static_pointer_cast<const react::RNSTabsHostIOSProps>(_props);
const auto &newComponentProps = *std::static_pointer_cast<const react::RNSTabsHostIOSProps>(props);

if (newComponentProps.navState.selectedScreenKey != oldComponentProps.navState.selectedScreenKey ||
newComponentProps.navState.provenance != oldComponentProps.navState.provenance) {
NSString *selectedScreenKey = RCTNSStringFromStringNilIfEmpty(newComponentProps.navState.selectedScreenKey);
if (newComponentProps.navStateRequest.selectedScreenKey != oldComponentProps.navStateRequest.selectedScreenKey ||
newComponentProps.navStateRequest.baseProvenance != oldComponentProps.navStateRequest.baseProvenance) {
NSString *selectedScreenKey = RCTNSStringFromStringNilIfEmpty(newComponentProps.navStateRequest.selectedScreenKey);
RCTAssert(selectedScreenKey != nil, @"[RNScreens] selectedScreenKey MUST NOT be nil");
RCTAssert(newComponentProps.navState.provenance >= 0, @"[RNScreens] provenance MUST BE >= 0]");
_jsNavState = [RNSTabsNavigationState stateWithSelectedScreenKey:selectedScreenKey
provenance:newComponentProps.navState.provenance];
[_controller setPendingNavigationStateUpdate:[_jsNavState cloneState]];
RCTAssert(newComponentProps.navStateRequest.baseProvenance >= 0, @"[RNScreens] baseProvenance MUST BE >= 0");
_navStateRequest =
[RNSTabsNavigationState stateWithSelectedScreenKey:selectedScreenKey
provenance:newComponentProps.navStateRequest.baseProvenance];
[_controller setPendingNavigationStateUpdate:[_navStateRequest cloneState]];
}

if (newComponentProps.rejectStaleNavStateUpdates != oldComponentProps.rejectStaleNavStateUpdates) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/tabs/host/TabsHost.android.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function TabsHost(props: TabsHostProps) {
direction,
nativeContainerStyle,
onTabSelected,
navState,
navStateRequest,
...filteredBaseProps
} = baseProps;

Expand All @@ -41,7 +41,7 @@ function TabsHost(props: TabsHostProps) {
return (
<TabsHostAndroidNativeComponent
style={[styles.fillParent, { direction }]}
navState={navState}
navStateRequest={navStateRequest}
onTabSelected={onTabSelectedCallback}
nativeContainerBackgroundColor={nativeContainerStyle?.backgroundColor}
// @ts-ignore suppress ref - debug only
Expand Down
4 changes: 2 additions & 2 deletions src/components/tabs/host/TabsHost.ios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function TabsHost(props: TabsHostProps) {
direction,
nativeContainerStyle,
onTabSelected,
navState,
navStateRequest,
...filteredBaseProps
} = baseProps;

Expand All @@ -47,7 +47,7 @@ function TabsHost(props: TabsHostProps) {
return (
<TabsHostIOSNativeComponent
style={styles.fillParent}
navState={navState}
navStateRequest={navStateRequest}
onTabSelected={onTabSelectedCallback}
nativeContainerBackgroundColor={nativeContainerStyle?.backgroundColor}
// @ts-ignore suppress ref - debug only
Expand Down
28 changes: 15 additions & 13 deletions src/components/tabs/host/TabsHost.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { ColorScheme, Direction } from '../../shared/types';

// #region Control

export type TabsHostNavState = {
export type TabsHostNavStateRequest = {
/**
* @summary Valid screen key.
*
Expand All @@ -14,13 +14,12 @@ export type TabsHostNavState = {
*/
selectedScreenKey: string;
/**
* @summary A number describing the provenance of the state instance.
* @summary Provenance of the navigation state this request is derived from.
*
* @description
* The provenance value establishes a relationship between different navigation state instances
* held by given state holder. The assumption here is that when the navigation
* state is progressed (modified), the provenance number is incremented.
* This creates a relationship where we can say that:
* held by given state holder. The assumption is that when the navigation state is progressed
* (modified), the provenance number is incremented. This creates a relationship where we can say:
*
* 1. State with provenance = n + 1 has been derived from state with provenance = n.
* 2. For two given navigation states A and B, we can say that A *is stale* iff
Expand All @@ -31,12 +30,15 @@ export type TabsHostNavState = {
*
* Currently, the native implementation of TabsHost is the state holder.
*
* When you use object of this shape to trigger a navigation via {@link TabsHostPropsBase#navState},
* pass here THE PROVENANCE OF THE LAST ACKNOWLEDGED state you received from native side
* via {@link TabsHostPropsBase#onTabSelected}. In other words this should be the provenance number
* of last confirmed state you base your update request on.
* Pass here THE PROVENANCE OF THE LAST ACKNOWLEDGED state you received from native side
* via {@link TabsHostPropsBase#onTabSelected}. In other words this should be the provenance
* number of last confirmed state you base your update request on.
*
* It is named `baseProvenance` (rather than `provenance`) to disambiguate it from the
* `provenance` field on the {@link TabSelectedEvent} payload, which carries the provenance
* of the state *resulting* from a transition.
*/
provenance: number;
baseProvenance: number;
};

// #endregion Control
Expand Down Expand Up @@ -140,15 +142,15 @@ export interface TabsHostPropsBase {
* the update might get accepted or rejected.
*
* @see {@link TabsHostPropsBase#rejectStaleNavStateUpdates}
* @see {@link TabsHostNavState} for description of the type model & accepted values.
* @see {@link TabsHostNavStateRequest} for description of the type model & accepted values.
*/
navState: TabsHostNavState;
navStateRequest: TabsHostNavStateRequest;
/**
* @summary If true, the native side will reject any navigation state updates coming from JS
* if they are stale.
*
* @description A navigation state update is considered stale if it is based of an stale state
* (@{link TabsHostNavState#provenance} indicates the base state).
* ({@link TabsHostNavStateRequest#baseProvenance} indicates the base state).
* A state is stale, when at the time of executing update, there already had been accepted a newer state
* of different origin.
*
Expand Down
2 changes: 1 addition & 1 deletion src/components/tabs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { TabsHost } from './host';
import { TabsScreen } from './screen';

export type {
TabsHostNavState,
TabsHostNavStateRequest,
TabSelectedEvent,
TabSelectionRejectedEvent,
TabSelectionRejectionReason,
Expand Down
6 changes: 3 additions & 3 deletions src/fabric/tabs/TabsHostAndroidNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ type TabSelectedEvent = {
isNativeAction: boolean;
};

type NavigationState = {
type NavigationStateRequest = {
selectedScreenKey: string;
provenance: CT.Int32;
baseProvenance: CT.Int32;
};

type TabSelectionRejectedEvent = Readonly<{
Expand All @@ -42,7 +42,7 @@ type TabsHostColorScheme = 'inherit' | 'light' | 'dark';

export interface NativeProps extends ViewProps {
// Control
navState: NavigationState;
navStateRequest: NavigationStateRequest;
rejectStaleNavStateUpdates?: CT.WithDefault<boolean, false>;

// Events
Expand Down
6 changes: 3 additions & 3 deletions src/fabric/tabs/TabsHostIOSNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ type TabSelectedEvent = Readonly<{
isNativeAction: boolean;
}>;

type NavigationState = Readonly<{
type NavigationStateRequest = Readonly<{
selectedScreenKey: string;
provenance: CT.Int32;
baseProvenance: CT.Int32;
}>;

type TabSelectionRejectedEvent = Readonly<{
Expand Down Expand Up @@ -57,7 +57,7 @@ type TabBarControllerMode = 'automatic' | 'tabBar' | 'tabSidebar';

export interface NativeProps extends ViewProps {
// Control
navState: NavigationState;
navStateRequest: NavigationStateRequest;
rejectStaleNavStateUpdates?: CT.WithDefault<boolean, false>;

// Events
Expand Down
Loading