Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,13 @@ class ScreenStackHeaderConfigViewManager :
config.setDirection(direction)
}

// unstable_synchronousUpdatesEnabled is not available on Android atm,
// however we must override their setters
override fun setUnstable_synchronousUpdatesEnabled(
config: ScreenStackHeaderConfig?,
value: Boolean,
) = Unit

override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> =
hashMapOf(
HeaderAttachedEvent.EVENT_NAME to hashMapOf("registrationName" to "onAttached"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ class ScreenStackHeaderSubviewManager :
}
}

// unstable_synchronousUpdatesEnabled is not available on Android atm,
// however we must override their setters
override fun setUnstable_synchronousUpdatesEnabled(
view: ScreenStackHeaderSubview?,
value: Boolean,
) = Unit

override fun updateState(
view: ScreenStackHeaderSubview,
props: ReactStylesDiffMap?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ open class ScreenViewManager :
view?.sheetElevation = value.toFloat()
}

// mark: iOS-only
// these props are not available on Android, however we must override their setters
override fun setFullScreenSwipeEnabled(
view: Screen?,
Expand Down Expand Up @@ -334,6 +335,13 @@ open class ScreenViewManager :
value: String?,
) = Unit

override fun setUnstable_synchronousUpdatesEnabled(
view: Screen?,
value: Boolean,
) = Unit

// END mark: iOS-only

@ReactProp(name = "sheetAllowedDetents")
override fun setSheetAllowedDetents(
view: Screen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case "topScrollEdgeEffect":
mViewManager.setTopScrollEdgeEffect(view, (String) value);
break;
case "unstable_synchronousUpdatesEnabled":
mViewManager.setUnstable_synchronousUpdatesEnabled(view, value == null ? false : (boolean) value);
break;
default:
super.setProperty(view, propName, value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,5 @@ public interface RNSScreenManagerInterface<T extends View> {
void setLeftScrollEdgeEffect(T view, @Nullable String value);
void setRightScrollEdgeEffect(T view, @Nullable String value);
void setTopScrollEdgeEffect(T view, @Nullable String value);
void setUnstable_synchronousUpdatesEnabled(T view, boolean value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case "topInsetEnabled":
mViewManager.setTopInsetEnabled(view, value == null ? false : (boolean) value);
break;
case "unstable_synchronousUpdatesEnabled":
mViewManager.setUnstable_synchronousUpdatesEnabled(view, value == null ? false : (boolean) value);
break;
default:
super.setProperty(view, propName, value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ public interface RNSScreenStackHeaderConfigManagerInterface<T extends View> {
void setBackButtonInCustomView(T view, boolean value);
void setBlurEffect(T view, @Nullable String value);
void setTopInsetEnabled(T view, boolean value);
void setUnstable_synchronousUpdatesEnabled(T view, boolean value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case "type":
mViewManager.setType(view, (String) value);
break;
case "unstable_synchronousUpdatesEnabled":
mViewManager.setUnstable_synchronousUpdatesEnabled(view, value == null ? false : (boolean) value);
break;
default:
super.setProperty(view, propName, value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@

public interface RNSScreenStackHeaderSubviewManagerInterface<T extends View> {
void setType(T view, @Nullable String value);
void setUnstable_synchronousUpdatesEnabled(T view, boolean value);
}
98 changes: 98 additions & 0 deletions apps/src/tests/Test3282.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { useEffect } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator, NativeStackNavigationProp } from '@react-navigation/native-stack';
import { featureFlags } from 'react-native-screens';

const SYNCHRONOUS_STATE_UPDATES_ENABLED = true;

type StackParamList = {
Home: undefined,
FormSheet: undefined,
}

type RouteProps = {
navigation: NativeStackNavigationProp<StackParamList>;
}

const Stack = createNativeStackNavigator();

const HomeScreen = ({ navigation }: RouteProps) => {
useEffect(() => {
featureFlags.experiment.unstable_synchronousStateUpdatesEnabled = SYNCHRONOUS_STATE_UPDATES_ENABLED
}, [])

return (
<View style={styles.container}>
<Button
title="Open Modal"
onPress={() => navigation.navigate('FormSheet')}
/>
</View>
);
};

const FormSheet = () => {
return (
<View style={styles.sheetContainer}>
<View style={styles.content}>
<Text>Modal Content</Text>
</View>
<View style={styles.blueBox}>
<Text style={styles.boxText}>Box</Text>
</View>
</View>
);
};

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen
name="FormSheet"
component={FormSheet}
options={{
presentation: 'formSheet',
title: 'Modal',
headerShown: true,
sheetAllowedDetents: [0.4, 0.8],
sheetGrabberVisible: true,
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
sheetContainer: {
flex: 1,
justifyContent: 'space-between',
backgroundColor: 'red',
minHeight: '100%',
position: 'relative',
},
content: {
padding: 20,
},
blueBox: {
height: 60,
backgroundColor: 'blue',
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
},
boxText: {
color: 'white',
},
});
5 changes: 4 additions & 1 deletion apps/src/tests/TestSplitView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import {
SplitViewWithNativeStackSheet
} from './SplitViewWithNativeStack';
import { SplitViewBaseConfig } from './helpers/types';
import { featureFlags } from 'react-native-screens';

featureFlags.experiment.unstable_synchronousStateUpdatesEnabled = true

const App = () => {
const splitViewBaseConfig: SplitViewBaseConfig = {
Expand All @@ -23,7 +26,7 @@ const App = () => {
}

return (
<SplitViewBaseApp splitViewBaseConfig={splitViewBaseConfig} />
<SplitViewWithNativeStackBase splitViewBaseConfig={splitViewBaseConfig} />
);
}

Expand Down
1 change: 1 addition & 0 deletions apps/src/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export { default as Test3212 } from './Test3212';
export { default as Test3236 } from './Test3236';
export { default as Test3239 } from './Test3239';
export { default as Test3265 } from './Test3265';
export { default as Test3282 } from './Test3282';
export { default as TestScreenAnimation } from './TestScreenAnimation';
export { default as TestScreenAnimationV5 } from './TestScreenAnimationV5';
export { default as TestHeader } from './TestHeader';
Expand Down
1 change: 1 addition & 0 deletions ios/RNSScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ namespace react = facebook::react;
@property (nonatomic) RNSScrollEdgeEffect leftScrollEdgeEffect;
@property (nonatomic) RNSScrollEdgeEffect rightScrollEdgeEffect;
@property (nonatomic) RNSScrollEdgeEffect topScrollEdgeEffect;
@property (nonatomic) BOOL unstable_synchronousUpdatesEnabled;

@property (nonatomic, retain) NSNumber *transitionDuration;
@property (nonatomic, readonly) BOOL dismissed;
Expand Down
19 changes: 18 additions & 1 deletion ios/RNSScreen.mm
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#import <React/RCTRootComponentView.h>
#import <React/RCTScrollViewComponentView.h>
#import <React/RCTSurfaceTouchHandler.h>
#import <cxxreact/ReactNativeVersion.h>
#import <react/renderer/components/rnscreens/EventEmitters.h>
#import <react/renderer/components/rnscreens/Props.h>
#import <react/renderer/components/rnscreens/RCTComponentViewHelpers.h>
Expand Down Expand Up @@ -188,7 +189,15 @@ - (void)updateBounds
: [_controller calculateHeaderHeightIsModal:self.isPresentedAsNativeModal];

auto newState = react::RNSScreenState{RCTSizeFromCGSize(self.bounds.size), {0, effectiveContentOffsetY}};
_state->updateState(std::move(newState));

_state->updateState(
std::move(newState)
#if REACT_NATIVE_VERSION_MINOR >= 82
,
_unstable_synchronousUpdatesEnabled ? facebook::react::EventQueue::UpdateMode::unstable_Immediate
: facebook::react::EventQueue::UpdateMode::Asynchronous
#endif
);

// TODO: Requesting layout on every layout is wrong. We should look for a way to get rid of this.
UINavigationController *navctr = _controller.navigationController;
Expand Down Expand Up @@ -468,6 +477,11 @@ - (void)setTopScrollEdgeEffect:(RNSScrollEdgeEffect)topScrollEdgeEffect
_topScrollEdgeEffect = topScrollEdgeEffect;
}

- (void)setUnstable_synchronousUpdatesEnabled:(BOOL)unstable_synchronousUpdatesEnabled
{
_unstable_synchronousUpdatesEnabled = unstable_synchronousUpdatesEnabled;
}

- (BOOL)isFullScreenSwipeEffectivelyEnabled
{
switch (_fullScreenSwipeEnabled) {
Expand Down Expand Up @@ -1372,6 +1386,8 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::

[self setSwipeDirection:[RNSConvert RNSScreenSwipeDirectionFromCppEquivalent:newScreenProps.swipeDirection]];

[self setUnstable_synchronousUpdatesEnabled:newScreenProps.unstable_synchronousUpdatesEnabled];

#if !TARGET_OS_TV
if (newScreenProps.statusBarHidden != oldScreenProps.statusBarHidden) {
[self setStatusBarHidden:newScreenProps.statusBarHidden];
Expand Down Expand Up @@ -2204,6 +2220,7 @@ @implementation RNSScreenManager
RCT_EXPORT_VIEW_PROPERTY(leftScrollEdgeEffect, RNSScrollEdgeEffect);
RCT_EXPORT_VIEW_PROPERTY(rightScrollEdgeEffect, RNSScrollEdgeEffect);
RCT_EXPORT_VIEW_PROPERTY(topScrollEdgeEffect, RNSScrollEdgeEffect);
RCT_EXPORT_VIEW_PROPERTY(unstable_synchronousUpdatesEnabled, BOOL);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we need this prop on Paper, as it won't work there but we might want to check if there is any warning if you pass a prop that hasn't been exported.

We can also leave the prop exported but for consistency I'd do it in other components as well (e.g. RNSScreenStackHeaderConfig).


RCT_EXPORT_VIEW_PROPERTY(onAppear, RCTDirectEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onDisappear, RCTDirectEventBlock);
Expand Down
1 change: 1 addition & 0 deletions ios/RNSScreenStackHeaderConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) UISemanticContentAttribute direction;
@property (nonatomic) UINavigationItemBackButtonDisplayMode backButtonDisplayMode;
@property (nonatomic) RNSBlurEffectStyle blurEffect;
@property (nonatomic) BOOL unstable_synchronousUpdatesEnabled;

NS_ASSUME_NONNULL_END

Expand Down
12 changes: 11 additions & 1 deletion ios/RNSScreenStackHeaderConfig.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#import <React/RCTImageComponentView.h>
#import <React/RCTMountingTransactionObserving.h>
#import <React/UIView+React.h>
#import <cxxreact/ReactNativeVersion.h>
#import <react/renderer/components/image/ImageProps.h>
#import <react/renderer/components/rnscreens/ComponentDescriptors.h>
#import <react/renderer/components/rnscreens/EventEmitters.h>
Expand Down Expand Up @@ -235,7 +236,14 @@ - (void)updateShadowStateWithSize:(CGSize)size edgeInsets:(NSDirectionalEdgeInse

if (newState != _lastSendState) {
_lastSendState = newState;
_state->updateState(std::move(newState));
_state->updateState(
std::move(newState)
#if REACT_NATIVE_VERSION_MINOR >= 82
,
_unstable_synchronousUpdatesEnabled ? facebook::react::EventQueue::UpdateMode::unstable_Immediate
: facebook::react::EventQueue::UpdateMode::Asynchronous
#endif
);
}
}

Expand Down Expand Up @@ -1068,6 +1076,8 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::
[self layoutNavigationControllerView];
}

_unstable_synchronousUpdatesEnabled = newScreenProps.unstable_synchronousUpdatesEnabled;

_initialPropsSet = YES;
_props = std::static_pointer_cast<react::RNSScreenStackHeaderConfigProps const>(props);

Expand Down
1 change: 1 addition & 0 deletions ios/RNSScreenStackHeaderSubview.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
#endif

@property (nonatomic) RNSScreenStackHeaderSubviewType type;
@property (nonatomic) BOOL unstable_synchronousUpdatesEnabled;

@property (nonatomic, weak) UIView *reactSuperview;

Expand Down
17 changes: 16 additions & 1 deletion ios/RNSScreenStackHeaderSubview.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#import "RNSScreenStackHeaderConfig.h"

#ifdef RCT_NEW_ARCH_ENABLED
#import <cxxreact/ReactNativeVersion.h>
#import <react/renderer/components/rnscreens/ComponentDescriptors.h>
#import <react/renderer/components/rnscreens/EventEmitters.h>
#import <react/renderer/components/rnscreens/RCTComponentViewHelpers.h>
Expand Down Expand Up @@ -95,7 +96,15 @@ - (void)updateShadowStateWithFrame:(CGRect)frame
if (!CGRectEqualToRect(frame, _lastScheduledFrame)) {
auto newState =
react::RNSScreenStackHeaderSubviewState(RCTSizeFromCGSize(frame.size), RCTPointFromCGPoint(frame.origin));
_state->updateState(std::move(newState));
_state->updateState(
std::move(newState)
#if REACT_NATIVE_VERSION_MINOR >= 82
,
_unstable_synchronousUpdatesEnabled ? facebook::react::EventQueue::UpdateMode::unstable_Immediate
: facebook::react::EventQueue::UpdateMode::Asynchronous
#endif
);

_lastScheduledFrame = frame;
}
}
Expand Down Expand Up @@ -135,9 +144,15 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::
const auto &newHeaderSubviewProps = *std::static_pointer_cast<const react::RNSScreenStackHeaderSubviewProps>(props);

[self setType:[RNSConvert RNSScreenStackHeaderSubviewTypeFromCppEquivalent:newHeaderSubviewProps.type]];
[self setUnstable_synchronousUpdatesEnabled:newHeaderSubviewProps.unstable_synchronousUpdatesEnabled];
[super updateProps:props oldProps:oldProps];
}

- (void)setUnstable_synchronousUpdatesEnabled:(BOOL)unstable_synchronousUpdatesEnabled
{
_unstable_synchronousUpdatesEnabled = unstable_synchronousUpdatesEnabled;
}

+ (react::ComponentDescriptorProvider)componentDescriptorProvider
{
return react::concreteComponentDescriptorProvider<react::RNSScreenStackHeaderSubviewComponentDescriptor>();
Expand Down
3 changes: 1 addition & 2 deletions ios/conversion/RNSConversions-BottomTabs.mm
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,7 @@ UITabBarControllerMode UITabBarControllerModeFromRNSBottomTabsTabBarControllerMo
}
#else // RCT_NEW_ARCH_ENABLED
API_AVAILABLE(ios(18.0))
UITabBarControllerMode UITabBarControllerModeFromRNSTabBarControllerMode(
RNSTabBarControllerMode tabBarDisplayMode)
UITabBarControllerMode UITabBarControllerModeFromRNSTabBarControllerMode(RNSTabBarControllerMode tabBarDisplayMode)
{
switch (tabBarDisplayMode) {
case RNSTabBarControllerModeAutomatic:
Expand Down
3 changes: 1 addition & 2 deletions ios/conversion/RNSConversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ UITabBarControllerMode UITabBarControllerModeFromRNSBottomTabsTabBarControllerMo
react::RNSBottomTabsTabBarControllerMode tabBarControllerMode);
#else // RCT_NEW_ARCH_ENABLED
API_AVAILABLE(ios(18.0))
UITabBarControllerMode UITabBarControllerModeFromRNSTabBarControllerMode(
RNSTabBarControllerMode tabBarControllerMode);
UITabBarControllerMode UITabBarControllerModeFromRNSTabBarControllerMode(RNSTabBarControllerMode tabBarControllerMode);
#endif // RCT_NEW_ARCH_ENABLED

#endif // Check for iOS >= 18
Expand Down
Loading
Loading