Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions FabricExample/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ import { featureFlags } from '../src';
featureFlags.experiment.synchronousScreenUpdatesEnabled = false
featureFlags.experiment.synchronousHeaderConfigUpdatesEnabled = false
featureFlags.experiment.synchronousHeaderSubviewUpdatesEnabled = false
featureFlags.experiment.earlyScreenOrientationChangeEnabled = true

export default App;
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,11 @@ open class ScreenViewManager :

// END mark: iOS-only

override fun setEarlyScreenOrientationChangeEnabled(
view: Screen?,
value: Boolean,
) = Unit // represents a feature flag and is checked via getProps() in RNSScreenComponentDescriptor.h

@ReactProp(name = "sheetAllowedDetents")
override fun setSheetAllowedDetents(
view: Screen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case "synchronousShadowStateUpdatesEnabled":
mViewManager.setSynchronousShadowStateUpdatesEnabled(view, value == null ? false : (boolean) value);
break;
case "earlyScreenOrientationChangeEnabled":
mViewManager.setEarlyScreenOrientationChangeEnabled(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 @@ -53,4 +53,5 @@ public interface RNSScreenManagerInterface<T extends View> {
void setRightScrollEdgeEffect(T view, @Nullable String value);
void setTopScrollEdgeEffect(T view, @Nullable String value);
void setSynchronousShadowStateUpdatesEnabled(T view, boolean value);
void setEarlyScreenOrientationChangeEnabled(T view, boolean value);
}
138 changes: 138 additions & 0 deletions apps/src/tests/Test2933.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import React, { useEffect, useState } from 'react';
import type { PropsWithChildren } from 'react';
import {
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';

import { Screen, ScreenStack } from 'react-native-screens';

type SectionProps = PropsWithChildren<{
title: string;
}>;

function Section({ children, title }: SectionProps): React.JSX.Element {
return (
<View style={styles.sectionContainer}>
<Text>{title}</Text>
<Text>{children}</Text>
</View>
);
}

function floodJsThread() {
setInterval(() => {
const end = Date.now() + 10;
while (Date.now() < end) {
// Intentionally do nothing; just burn CPU cycles.
Math.sqrt(Math.random());
}
}, 12);
}

/*
* create artificial pressure in the JS thread to show off thep problem.
* */
floodJsThread();
floodJsThread();

function AppMain(): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';

const backgroundStyle = {
flex: 1,
backgroundColor: 'white',
};

const [num, setNum] = useState(0);

useEffect(() => {
let i = 0;
setInterval(() => {
i++;
setNum(i);
}, 2)
}, []);

/*
* To keep the template simple and small we're adding padding to prevent view
* from rendering under the System UI.
* For bigger apps the reccomendation is to use `react-native-safe-area-context`:
* https://github.com/AppAndFlow/react-native-safe-area-context
*
* You can read more about it here:
* https://github.com/react-native-community/discussions-and-proposals/discussions/827
*/
const safePadding = '5%';

return (
<View style={backgroundStyle}>
{/* // TODO nowy task
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={backgroundStyle.backgroundColor}
/> */}
<View
style={{
flex: 1,
paddingHorizontal: safePadding,
paddingBottom: safePadding,
justifyContent: 'center',
alignContent: 'center',
}}>
<Section title="Step One">
This test shows how the native layout update triggers a layout shift.
</Section>
<Section title="Step One">
There is a view with a blue background. We don't expect to ever see
flashes of the blue background.
</Section>
<Section title="Orientation"> {num} </Section>
</View>
</View>
);
}

const styles = StyleSheet.create({
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
},
highlight: {
fontWeight: '700',
},
});

function App() {
return (
<ScreenStack style={{ flex: 1, backgroundColor: 'red' }}>
<Screen
style={{
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
backgroundColor: 'blue',
padding: 20,
}}
enabled
isNativeStack>
<AppMain />
</Screen>
</ScreenStack>
);
}

export default App;
1 change: 1 addition & 0 deletions apps/src/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export { default as Test2855 } from './Test2855';
export { default as Test2877 } from './Test2877'; // [E2E created](iOS): issue is related to formSheet on iOS
export { default as Test2895 } from './Test2895';
export { default as Test2899 } from './Test2899';
export { default as Test2933 } from './Test2933';
export { default as Test2926 } from './Test2926'; // [E2E created](iOS): PR related to iOS search bar
export { default as Test2949 } from './Test2949'; // [E2E skipped]: can't check system bars styles
export { default as Test2963 } from './Test2963'; // [E2E created](iOS): issue related to iOS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

#ifdef ANDROID
#include <fbjni/fbjni.h>
#include "RNSScreenShadowNodeCommitHook.h"
#endif // ANDROID
#include <react/debug/react_native_assert.h>
#include <react/renderer/components/rnscreens/Props.h>
#include <react/renderer/components/rnscreens/utils/RectUtil.h>
#include <react/renderer/components/root/RootShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
#include <react/renderer/uimanager/UIManager.h>
#include <react/renderer/uimanager/UIManagerCommitHook.h>
#include "RNSScreenShadowNode.h"

namespace facebook {
Expand All @@ -16,12 +20,22 @@ using namespace rnscreens;

class RNSScreenComponentDescriptor final
: public ConcreteComponentDescriptor<RNSScreenShadowNode> {
private:
#ifdef ANDROID
/*
* A commit hook that triggers on `shadowTreeWillCommit` event,
* and can read the properties of RootShadowNodes for determining screen
* orientation.
*/
mutable std::shared_ptr<RNSScreenShadowNodeCommitHook> commitHook_;
#endif // ANDROID

public:
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;

void adopt(ShadowNode &shadowNode) const override {
react_native_assert(dynamic_cast<RNSScreenShadowNode *>(&shadowNode));
auto &screenShadowNode = static_cast<RNSScreenShadowNode &>(shadowNode);
auto &screenShadowNode = dynamic_cast<RNSScreenShadowNode &>(shadowNode);

react_native_assert(
dynamic_cast<YogaLayoutableShadowNode *>(&screenShadowNode));
Expand All @@ -32,8 +46,26 @@ class RNSScreenComponentDescriptor final
std::static_pointer_cast<const RNSScreenShadowNode::ConcreteState>(
shadowNode.getState());
auto stateData = state->getData();

#ifdef ANDROID
// get featureFlags.experiment.earlyScreenOrientationChangeEnabled from
// Screen props enable the commit hook only when the developer asks to
react_native_assert(
dynamic_cast<const RNSScreenProps *>(
screenShadowNode.getProps().get()));
auto props =
dynamic_cast<const RNSScreenProps *>(screenShadowNode.getProps().get());

if (!commitHook_ && props->earlyScreenOrientationChangeEnabled) {
// For the the application that needs to react to orientation change
// as early as possible, we attach a commit hook that checks for the
// change in the old vs new RootShadowNode. The hook cannot be attached in
// the constructor because UIManager is still missing from
// ContextContainer. Instead, we do it here, on the first call to the
// function.
commitHook_ =
std::make_shared<RNSScreenShadowNodeCommitHook>(contextContainer_);
}

if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) {
// When we receive dimensions from JVM side we can remove padding used for
// correction, and we can stop applying height and offset corrections for
Expand Down Expand Up @@ -66,9 +98,15 @@ class RNSScreenComponentDescriptor final
FrameCorrectionModes::Mode::FrameHeightCorrection);
screenShadowNode.getFrameCorrectionModes().unset(
FrameCorrectionModes::Mode::FrameOriginCorrection);

layoutableShadowNode.setSize(
Size{stateData.frameSize.width, stateData.frameSize.height});
} else if (
stateData.frameSize.width == 0 && stateData.frameSize.height == 0) {
// Reset YogaNode so it recalculates its layout. Useful for the case
// when native orientation changes and react has not been notified yet.
// The if condition holds true on first render and when it is reset inside
// RNSScreenShadowNodeCommitHook.
layoutableShadowNode.setSize({YGUndefined, YGUndefined});
}
#else
if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ FrameCorrectionModes &RNSScreenShadowNode::getFrameCorrectionModes() {
return getStateDataMutable().getFrameCorrectionModes();
}

void RNSScreenShadowNode::resetFrameSizeState() {
getStateDataMutable().frameSize = {0, 0};
}

RNSScreenShadowNode::StateData &RNSScreenShadowNode::getStateDataMutable() {
// We assume that this method is called to mutate the data, so we ensure
// we're unsealed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ class JSI_EXPORT RNSScreenShadowNode final : public ConcreteViewShadowNode<

FrameCorrectionModes &getFrameCorrectionModes();

private:
#ifdef ANDROID
void resetFrameSizeState();

private:
void applyFrameCorrections();

StateData &getStateDataMutable();
Expand Down
Loading
Loading