Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 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 @@ -205,6 +205,13 @@ class ScreenStackHeaderConfigViewManager :
config.setDirection(direction)
}

// synchronousShadowStateUpdatesEnabled is not available on Android atm,
// however we must override their setters
override fun setSynchronousShadowStateUpdatesEnabled(
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 @@ -50,6 +50,13 @@ class ScreenStackHeaderSubviewManager :
Log.w("[RNScreens]", "hidesSharedBackground prop is not available on Android")
}

// synchronousShadowStateUpdatesEnabled is not available on Android atm,
// however we must override their setters
override fun setSynchronousShadowStateUpdatesEnabled(
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 setSynchronousShadowStateUpdatesEnabled(
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 "synchronousShadowStateUpdatesEnabled":
mViewManager.setSynchronousShadowStateUpdatesEnabled(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 setSynchronousShadowStateUpdatesEnabled(T view, boolean value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case "headerRightBarButtonItems":
mViewManager.setHeaderRightBarButtonItems(view, (ReadableArray) value);
break;
case "synchronousShadowStateUpdatesEnabled":
mViewManager.setSynchronousShadowStateUpdatesEnabled(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 @@ -45,4 +45,5 @@ public interface RNSScreenStackHeaderConfigManagerInterface<T extends View> {
void setTopInsetEnabled(T view, boolean value);
void setHeaderLeftBarButtonItems(T view, @Nullable ReadableArray value);
void setHeaderRightBarButtonItems(T view, @Nullable ReadableArray value);
void setSynchronousShadowStateUpdatesEnabled(T view, boolean value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case "hidesSharedBackground":
mViewManager.setHidesSharedBackground(view, value == null ? false : (boolean) value);
break;
case "synchronousShadowStateUpdatesEnabled":
mViewManager.setSynchronousShadowStateUpdatesEnabled(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 @@ -16,4 +16,5 @@
public interface RNSScreenStackHeaderSubviewManagerInterface<T extends View> {
void setType(T view, @Nullable String value);
void setHidesSharedBackground(T view, boolean value);
void setSynchronousShadowStateUpdatesEnabled(T view, boolean value);
}
96 changes: 96 additions & 0 deletions apps/src/tests/Test3282.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React 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';

featureFlags.experiment.synchronousScreenUpdatesEnabled = true
featureFlags.experiment.synchronousHeaderConfigUpdatesEnabled = true
featureFlags.experiment.synchronousHeaderSubviewUpdatesEnabled = true

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

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

const Stack = createNativeStackNavigator();

const HomeScreen = ({ navigation }: RouteProps) => {
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: 5 additions & 0 deletions apps/src/tests/TestSplitView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import {
SplitViewWithNativeStackSheet
} from './SplitViewWithNativeStack';
import { SplitViewBaseConfig } from './helpers/types';
import { featureFlags } from 'react-native-screens';

featureFlags.experiment.synchronousScreenUpdatesEnabled = true
featureFlags.experiment.synchronousHeaderConfigUpdatesEnabled = true
featureFlags.experiment.synchronousHeaderSubviewUpdatesEnabled = true

const App = () => {
const 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 @@ -154,6 +154,7 @@ export { default as Test3236 } from './Test3236';
export { default as Test3239 } from './Test3239';
export { default as Test3265 } from './Test3265';
export { default as Test3271 } from './Test3271';
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, readwrite) BOOL synchronousShadowStateUpdatesEnabled;

@property (nonatomic, retain) NSNumber *transitionDuration;
@property (nonatomic, readonly) BOOL dismissed;
Expand Down
13 changes: 12 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
,
_synchronousShadowStateUpdatesEnabled ? 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 @@ -1372,6 +1381,8 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::

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

[self setSynchronousShadowStateUpdatesEnabled:newScreenProps.synchronousShadowStateUpdatesEnabled];

#if !TARGET_OS_TV
if (newScreenProps.statusBarHidden != oldScreenProps.statusBarHidden) {
[self setStatusBarHidden:newScreenProps.statusBarHidden];
Expand Down
1 change: 1 addition & 0 deletions ios/RNSScreenStackHeaderConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) RCTDirectEventBlock onPressHeaderBarButtonItem;
@property (nonatomic) RCTDirectEventBlock onPressHeaderBarButtonMenuItem;
#endif
@property (nonatomic, readwrite) BOOL synchronousShadowStateUpdatesEnabled;

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 @@ -5,6 +5,7 @@
#import <React/RCTMountingTransactionObserving.h>
#import <React/UIView+React.h>
#import <ReactCommon/TurboModuleUtils.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 @@ -233,7 +234,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
,
_synchronousShadowStateUpdatesEnabled ? facebook::react::EventQueue::UpdateMode::unstable_Immediate
: facebook::react::EventQueue::UpdateMode::Asynchronous
#endif
);
}
}

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

_synchronousShadowStateUpdatesEnabled = newScreenProps.synchronousShadowStateUpdatesEnabled;

_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, readwrite) BOOL synchronousShadowStateUpdatesEnabled;

@property (nonatomic, weak) UIView *reactSuperview;

Expand Down
12 changes: 11 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 @@ -99,7 +100,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
,
_synchronousShadowStateUpdatesEnabled ? facebook::react::EventQueue::UpdateMode::unstable_Immediate
: facebook::react::EventQueue::UpdateMode::Asynchronous
#endif
);

_lastScheduledFrame = frame;
}
}
Expand Down Expand Up @@ -140,6 +149,7 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::

[self setType:[RNSConvert RNSScreenStackHeaderSubviewTypeFromCppEquivalent:newHeaderSubviewProps.type]];
[self setHidesSharedBackground:newHeaderSubviewProps.hidesSharedBackground];
[self setSynchronousShadowStateUpdatesEnabled:newHeaderSubviewProps.synchronousShadowStateUpdatesEnabled];
[super updateProps:props oldProps:oldProps];
}

Expand Down
6 changes: 5 additions & 1 deletion src/components/Screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
resolveSheetLargestUndimmedDetent,
} from './helpers/sheet';
import { parseBooleanToOptionalBooleanNativeProp } from '../utils';
import featureFlags from '../flags';

type NativeProps = ScreenNativeComponentProps | ModalScreenNativeComponentProps;
const AnimatedNativeScreen = Animated.createAnimatedComponent(
Expand Down Expand Up @@ -70,7 +71,7 @@
React.useImperativeHandle(ref, () => innerRef.current!, []);
const prevActivityState = usePrevious(props.activityState);

const setRef = (ref: ViewConfig) => {

Check warning on line 74 in src/components/Screen.tsx

View workflow job for this annotation

GitHub Actions / lint-js

'ref' is already declared in the upper scope on line 69 column 31
innerRef.current = ref;
props.onComponentRef?.(ref);
};
Expand Down Expand Up @@ -150,7 +151,7 @@
scrollEdgeEffects,
onGestureCancel,
style,
...props

Check warning on line 154 in src/components/Screen.tsx

View workflow job for this annotation

GitHub Actions / lint-js

'props' is already declared in the upper scope on line 69 column 24
} = rest;

if (active !== undefined && activityState === undefined) {
Expand All @@ -172,7 +173,7 @@
}
}

const handleRef = (ref: ViewConfig) => {

Check warning on line 176 in src/components/Screen.tsx

View workflow job for this annotation

GitHub Actions / lint-js

'ref' is already declared in the upper scope on line 69 column 31
// Workaround is necessary to prevent React Native from hiding frozen screens.
// See this PR: https://github.com/grahammendick/navigation/pull/860
if (ref?.viewConfig?.validAttributes?.style) {
Expand Down Expand Up @@ -263,7 +264,10 @@
bottomScrollEdgeEffect={scrollEdgeEffects?.bottom}
leftScrollEdgeEffect={scrollEdgeEffects?.left}
rightScrollEdgeEffect={scrollEdgeEffects?.right}
topScrollEdgeEffect={scrollEdgeEffects?.top}>
topScrollEdgeEffect={scrollEdgeEffects?.top}
synchronousShadowStateUpdatesEnabled={
featureFlags.experiment.synchronousScreenUpdatesEnabled
}>
{!isNativeStack ? ( // see comment of this prop in types.tsx for information why it is needed
children
) : (
Expand All @@ -287,7 +291,7 @@
style,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onComponentRef,
...props

Check warning on line 294 in src/components/Screen.tsx

View workflow job for this annotation

GitHub Actions / lint-js

'props' is already declared in the upper scope on line 69 column 24
} = rest;

if (active !== undefined && activityState === undefined) {
Expand All @@ -295,7 +299,7 @@
}
return (
<Animated.View
style={[style, { display: activityState !== 0 ? 'flex' : 'none' }]}

Check warning on line 302 in src/components/Screen.tsx

View workflow job for this annotation

GitHub Actions / lint-js

Inline style: { display: "activityState !== 0 ? 'flex' : 'none'" }
ref={setRef}
{...props}
/>
Expand Down
Loading
Loading