react-native-screens 3.32.0 → 3.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +17 -13
  2. package/RNScreens.podspec +10 -52
  3. package/android/CMakeLists.txt +48 -4
  4. package/android/build.gradle +9 -81
  5. package/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +25 -16
  6. package/android/src/fabric/java/com/swmansion/rnscreens/NativeProxy.kt +53 -0
  7. package/android/src/main/cpp/NativeProxy.cpp +51 -0
  8. package/android/src/main/cpp/NativeProxy.h +35 -0
  9. package/android/src/main/cpp/OnLoad.cpp +8 -0
  10. package/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt +5 -2
  11. package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +4 -1
  12. package/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt +2 -2
  13. package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +36 -17
  14. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +134 -38
  15. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +52 -30
  16. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainerViewManager.kt +17 -7
  17. package/android/src/main/java/com/swmansion/rnscreens/ScreenEventDispatcher.kt +10 -2
  18. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +56 -27
  19. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragmentWrapper.kt +8 -1
  20. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +50 -19
  21. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +60 -37
  22. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragmentWrapper.kt +4 -0
  23. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +85 -58
  24. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt +128 -37
  25. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubview.kt +19 -4
  26. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubviewManager.kt +16 -10
  27. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackViewManager.kt +28 -25
  28. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +173 -78
  29. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +59 -24
  30. package/android/src/main/java/com/swmansion/rnscreens/ScreensModule.kt +30 -8
  31. package/android/src/main/java/com/swmansion/rnscreens/ScreensShadowNode.kt +3 -1
  32. package/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +101 -50
  33. package/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +29 -22
  34. package/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +7 -2
  35. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderAttachedEvent.kt +4 -1
  36. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderBackButtonClickedEvent.kt +4 -1
  37. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderDetachedEvent.kt +4 -1
  38. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderHeightChangeEvent.kt +5 -5
  39. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenAppearEvent.kt +4 -1
  40. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenDisappearEvent.kt +4 -1
  41. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenDismissedEvent.kt +8 -4
  42. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenTransitionProgressEvent.kt +7 -6
  43. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillAppearEvent.kt +4 -1
  44. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillDisappearEvent.kt +4 -1
  45. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarBlurEvent.kt +4 -1
  46. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarChangeTextEvent.kt +4 -3
  47. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarCloseEvent.kt +4 -1
  48. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarFocusEvent.kt +4 -1
  49. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarOpenEvent.kt +4 -1
  50. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarSearchButtonPressEvent.kt +9 -4
  51. package/android/src/main/java/com/swmansion/rnscreens/events/StackFinishTransitioningEvent.kt +4 -1
  52. package/android/src/main/java/com/swmansion/rnscreens/utils/DeviceUtils.kt +1 -5
  53. package/android/src/main/java/com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper.kt +245 -0
  54. package/android/src/main/jni/CMakeLists.txt +5 -4
  55. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java +3 -0
  56. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerInterface.java +1 -0
  57. package/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +10 -5
  58. package/android/src/paper/java/com/swmansion/rnscreens/NativeProxy.kt +19 -0
  59. package/android/src/paper/java/com/swmansion/rnscreens/NativeScreensModuleSpec.java +4 -0
  60. package/common/cpp/react/renderer/components/rnscreens/FrameCorrectionModes.h +51 -0
  61. package/common/cpp/react/renderer/components/rnscreens/RNSModalScreenShadowNode.cpp +2 -1
  62. package/common/cpp/react/renderer/components/rnscreens/RNSModalScreenShadowNode.h +1 -1
  63. package/common/cpp/react/renderer/components/rnscreens/RNSScreenComponentDescriptor.h +140 -1
  64. package/common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.cpp +51 -1
  65. package/common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.h +23 -1
  66. package/common/cpp/react/renderer/components/rnscreens/RNSScreenState.cpp +20 -0
  67. package/common/cpp/react/renderer/components/rnscreens/RNSScreenState.h +23 -1
  68. package/common/cpp/react/renderer/components/rnscreens/utils/RectUtil.h +36 -0
  69. package/cpp/RNSScreenRemovalListener.cpp +25 -0
  70. package/cpp/RNSScreenRemovalListener.h +20 -0
  71. package/ios/RNSConvert.h +1 -0
  72. package/ios/RNSModalScreen.mm +22 -0
  73. package/ios/RNSModule.mm +1 -1
  74. package/ios/RNSScreen.h +2 -1
  75. package/ios/RNSScreen.mm +27 -19
  76. package/ios/RNSScreenStack.mm +24 -77
  77. package/ios/RNSScreenStackAnimator.mm +43 -6
  78. package/ios/RNSScreenStackHeaderConfig.mm +49 -11
  79. package/ios/RNSScreenStackHeaderSubview.mm +8 -0
  80. package/ios/utils/UIView+RNSUtility.h +23 -0
  81. package/ios/utils/UIView+RNSUtility.mm +55 -0
  82. package/lib/commonjs/components/ScreenStack.js +8 -1
  83. package/lib/commonjs/components/ScreenStack.js.map +1 -1
  84. package/lib/commonjs/fabric/ModalScreenNativeComponent.js.map +1 -1
  85. package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
  86. package/lib/commonjs/native-stack/views/NativeStackView.js +2 -0
  87. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  88. package/lib/module/components/ScreenStack.js +8 -1
  89. package/lib/module/components/ScreenStack.js.map +1 -1
  90. package/lib/module/fabric/ModalScreenNativeComponent.js.map +1 -1
  91. package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
  92. package/lib/module/native-stack/views/NativeStackView.js +2 -0
  93. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  94. package/lib/typescript/components/ScreenStack.d.ts.map +1 -1
  95. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts +1 -0
  96. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts.map +1 -1
  97. package/lib/typescript/fabric/ScreenNativeComponent.d.ts +1 -0
  98. package/lib/typescript/fabric/ScreenNativeComponent.d.ts.map +1 -1
  99. package/lib/typescript/native-stack/types.d.ts +10 -0
  100. package/lib/typescript/native-stack/types.d.ts.map +1 -1
  101. package/lib/typescript/native-stack/views/NativeStackView.d.ts.map +1 -1
  102. package/lib/typescript/types.d.ts +10 -0
  103. package/lib/typescript/types.d.ts.map +1 -1
  104. package/native-stack/README.md +110 -99
  105. package/package.json +6 -3
  106. package/react-native.config.js +17 -15
  107. package/src/TransitionProgressContext.tsx +1 -1
  108. package/src/components/Screen.tsx +4 -4
  109. package/src/components/ScreenStack.tsx +11 -1
  110. package/src/components/ScreenStackHeaderConfig.tsx +5 -5
  111. package/src/components/ScreenStackHeaderConfig.web.tsx +6 -6
  112. package/src/components/SearchBar.tsx +4 -4
  113. package/src/core.ts +1 -1
  114. package/src/fabric/ModalScreenNativeComponent.ts +1 -0
  115. package/src/fabric/ScreenNativeComponent.ts +1 -0
  116. package/src/fabric/ScreenNavigationContainerNativeComponent.ts +1 -1
  117. package/src/fabric/ScreenStackHeaderConfigNativeComponent.ts +1 -1
  118. package/src/fabric/ScreenStackHeaderSubviewNativeComponent.ts +1 -1
  119. package/src/fabric/SearchBarNativeComponent.ts +1 -1
  120. package/src/gesture-handler/ScreenGestureDetector.tsx +5 -5
  121. package/src/gesture-handler/constraints.ts +5 -5
  122. package/src/gesture-handler/fabricUtils.ts +1 -1
  123. package/src/native-stack/contexts/GHContext.tsx +1 -1
  124. package/src/native-stack/navigators/createNativeStackNavigator.tsx +3 -3
  125. package/src/native-stack/types.tsx +14 -4
  126. package/src/native-stack/utils/getDefaultHeaderHeight.tsx +1 -1
  127. package/src/native-stack/utils/getStatusBarHeight.tsx +1 -1
  128. package/src/native-stack/utils/useAnimatedHeaderHeight.tsx +1 -1
  129. package/src/native-stack/utils/useBackPressSubscription.tsx +1 -1
  130. package/src/native-stack/utils/useHeaderHeight.tsx +1 -1
  131. package/src/native-stack/views/FontProcessor.tsx +1 -1
  132. package/src/native-stack/views/HeaderConfig.tsx +1 -1
  133. package/src/native-stack/views/NativeStackView.tsx +11 -9
  134. package/src/reanimated/ReanimatedHeaderHeightContext.tsx +1 -1
  135. package/src/reanimated/ReanimatedNativeStackScreen.tsx +5 -5
  136. package/src/reanimated/ReanimatedScreen.tsx +2 -2
  137. package/src/reanimated/ReanimatedScreenProvider.tsx +1 -1
  138. package/src/reanimated/useReanimatedHeaderHeight.tsx +1 -1
  139. package/src/reanimated/useReanimatedTransitionProgress.tsx +1 -1
  140. package/src/types.tsx +15 -5
  141. package/src/useTransitionProgress.tsx +1 -1
  142. package/windows/README.md +4 -1
@@ -1,17 +1,27 @@
1
1
  #pragma once
2
2
 
3
+ #ifdef ANDROID
4
+ #include <fbjni/fbjni.h>
5
+ #endif
3
6
  #include <react/debug/react_native_assert.h>
7
+ #include <react/renderer/components/rnscreens/Props.h>
4
8
  #include <react/renderer/core/ConcreteComponentDescriptor.h>
5
9
  #include "RNSScreenShadowNode.h"
10
+ #include "utils/RectUtil.h"
6
11
 
7
12
  namespace facebook {
8
13
  namespace react {
9
14
 
15
+ using namespace rnscreens;
16
+
10
17
  class RNSScreenComponentDescriptor final
11
18
  : public ConcreteComponentDescriptor<RNSScreenShadowNode> {
12
19
  public:
13
20
  using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
14
21
 
22
+ static constexpr const char *kScreenDummyLayoutHelperClass =
23
+ "com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper";
24
+
15
25
  void adopt(ShadowNode &shadowNode) const override {
16
26
  react_native_assert(dynamic_cast<RNSScreenShadowNode *>(&shadowNode));
17
27
  auto &screenShadowNode = static_cast<RNSScreenShadowNode &>(shadowNode);
@@ -26,13 +36,142 @@ class RNSScreenComponentDescriptor final
26
36
  shadowNode.getState());
27
37
  auto stateData = state->getData();
28
38
 
39
+ #ifdef ANDROID
29
40
  if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) {
41
+ // When we receive dimensions from JVM side we can remove padding used for
42
+ // correction, and we can stop applying height correction for the frame.
43
+ // We want to leave top offset correction though intact.
44
+ // TODO: In future, when we have dynamic header height we might want to
45
+ // update Y offset correction here.
46
+
47
+ #ifdef REACT_NATIVE_DEBUG
48
+ // We use the fact that height correction is disabled once we receive
49
+ // state from the native, so when we have incoming state & height
50
+ // correction is still enabled, we know this is the very first native
51
+ // state update.
52
+ if (screenShadowNode.getFrameCorrectionModes().check(
53
+ FrameCorrectionModes::Mode::FrameHeightCorrection) &&
54
+ !checkFrameSizesEqualWithEps(
55
+ screenShadowNode.layoutMetrics_.frame.size,
56
+ stateData.frameSize)) {
57
+ LOG(ERROR)
58
+ << "[RNScreens] The first frame received from state update: "
59
+ << stateData.frameSize.width << "x" << stateData.frameSize.height
60
+ << " differs from the one expected: "
61
+ << screenShadowNode.layoutMetrics_.frame.size.width << "x"
62
+ << screenShadowNode.layoutMetrics_.frame.size.height
63
+ << ". This is most likely a react-native-screens library bug. Please report this at https://github.com/software-mansion/react-native-screens/issues";
64
+ }
65
+ #endif
66
+
67
+ screenShadowNode.setPadding({0, 0, 0, 0});
68
+ screenShadowNode.getFrameCorrectionModes().unset(
69
+ FrameCorrectionModes::Mode::FrameHeightCorrection);
70
+
30
71
  layoutableShadowNode.setSize(
31
72
  Size{stateData.frameSize.width, stateData.frameSize.height});
32
- }
73
+ } else {
74
+ // This code path should be executed only on the very first (few)
75
+ // layout(s), when we haven't received state update from JVM side yet.
76
+
77
+ auto headerConfigChildOpt = findHeaderConfigChild(layoutableShadowNode);
78
+
79
+ // During creation of the shadow node children are not attached yet.
80
+ // We also do not want to set any padding in case.
81
+ if (headerConfigChildOpt) {
82
+ const auto &headerConfigChild = headerConfigChildOpt->get();
83
+ const auto &headerProps =
84
+ *std::static_pointer_cast<const RNSScreenStackHeaderConfigProps>(
85
+ headerConfigChild->getProps());
86
+
87
+ const auto headerHeight = headerProps.hidden
88
+ ? 0.f
89
+ : findHeaderHeight(
90
+ headerProps.titleFontSize, headerProps.title.empty())
91
+ .value_or(0.f);
33
92
 
93
+ screenShadowNode.setPadding({0, 0, 0, headerHeight});
94
+ screenShadowNode.setHeaderHeight(headerHeight);
95
+ screenShadowNode.getFrameCorrectionModes().set(
96
+ FrameCorrectionModes::Mode(
97
+ FrameCorrectionModes::Mode::FrameHeightCorrection |
98
+ FrameCorrectionModes::Mode::FrameOriginCorrection));
99
+ }
100
+ }
101
+ #else
102
+ if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) {
103
+ layoutableShadowNode.setSize(
104
+ Size{stateData.frameSize.width, stateData.frameSize.height});
105
+ }
106
+ #endif // ANDROID
34
107
  ConcreteComponentDescriptor::adopt(shadowNode);
35
108
  }
109
+
110
+ std::optional<std::reference_wrapper<const ShadowNode::Shared>>
111
+ findHeaderConfigChild(
112
+ const YogaLayoutableShadowNode &screenShadowNode) const {
113
+ for (const ShadowNode::Shared &child : screenShadowNode.getChildren()) {
114
+ if (std::strcmp(
115
+ child->getComponentName(), "RNSScreenStackHeaderConfig") == 0) {
116
+ return {std::cref(child)};
117
+ }
118
+ }
119
+ return {};
120
+ }
121
+
122
+ #ifdef ANDROID
123
+ std::optional<float> findHeaderHeight(
124
+ const int fontSize,
125
+ const bool isTitleEmpty) const {
126
+ JNIEnv *env = facebook::jni::Environment::current();
127
+
128
+ if (env == nullptr) {
129
+ LOG(ERROR) << "[RNScreens] Failed to retrieve env\n";
130
+ return {};
131
+ }
132
+
133
+ jclass layoutHelperClass = env->FindClass(kScreenDummyLayoutHelperClass);
134
+
135
+ if (layoutHelperClass == nullptr) {
136
+ LOG(ERROR) << "[RNScreens] Failed to find class with id "
137
+ << kScreenDummyLayoutHelperClass;
138
+ return {};
139
+ }
140
+
141
+ jmethodID computeDummyLayoutID =
142
+ env->GetMethodID(layoutHelperClass, "computeDummyLayout", "(IZ)F");
143
+
144
+ if (computeDummyLayoutID == nullptr) {
145
+ LOG(ERROR)
146
+ << "[RNScreens] Failed to retrieve computeDummyLayout method ID";
147
+ return {};
148
+ }
149
+
150
+ jmethodID getInstanceMethodID = env->GetStaticMethodID(
151
+ layoutHelperClass,
152
+ "getInstance",
153
+ "()Lcom/swmansion/rnscreens/utils/ScreenDummyLayoutHelper;");
154
+
155
+ if (getInstanceMethodID == nullptr) {
156
+ LOG(ERROR) << "[RNScreens] Failed to retrieve getInstanceMethodID";
157
+ return {};
158
+ }
159
+
160
+ jobject packageInstance =
161
+ env->CallStaticObjectMethod(layoutHelperClass, getInstanceMethodID);
162
+
163
+ if (packageInstance == nullptr) {
164
+ LOG(ERROR)
165
+ << "[RNScreens] Failed to retrieve packageInstance or the package instance was null on JVM side";
166
+ return {};
167
+ }
168
+
169
+ jfloat headerHeight = env->CallFloatMethod(
170
+ packageInstance, computeDummyLayoutID, fontSize, isTitleEmpty);
171
+
172
+ return {headerHeight};
173
+ }
174
+ #endif // ANDROID
36
175
  };
37
176
 
38
177
  } // namespace react
@@ -3,13 +3,63 @@
3
3
  namespace facebook {
4
4
  namespace react {
5
5
 
6
+ namespace yoga = facebook::yoga;
7
+ using namespace rnscreens;
8
+
6
9
  extern const char RNSScreenComponentName[] = "RNSScreen";
7
10
 
8
- Point RNSScreenShadowNode::getContentOriginOffset() const {
11
+ Point RNSScreenShadowNode::getContentOriginOffset(
12
+ bool /*includeTransform*/) const {
9
13
  auto stateData = getStateData();
10
14
  auto contentOffset = stateData.contentOffset;
11
15
  return {contentOffset.x, contentOffset.y};
12
16
  }
13
17
 
18
+ void RNSScreenShadowNode::layout(facebook::react::LayoutContext layoutContext) {
19
+ YogaLayoutableShadowNode::layout(layoutContext);
20
+
21
+ #ifdef ANDROID
22
+ applyFrameCorrections();
23
+ #endif // ANDROID
24
+ }
25
+
26
+ #ifdef ANDROID
27
+ void RNSScreenShadowNode::applyFrameCorrections() {
28
+ ensureUnsealed();
29
+
30
+ // On the very first layout we want to correct both Y offset and frame size.
31
+ // On consecutive layouts we want to correct only Y offset, as the frame size
32
+ // is received from JVM side. This is done so if the Screen dimensions are
33
+ // read from ShadowTree (e.g by reanimated) they have chance of being
34
+ // accurate. On JVM side we do ignore this frame anyway, because
35
+ // ScreenStackViewManager.needsCustomLayoutForChildren() == true.
36
+ const auto &stateData = getStateData();
37
+ const float lastKnownHeaderHeight = stateData.getLastKnownHeaderHeight();
38
+ const auto &headerCorrectionModes = stateData.getHeaderCorrectionModes();
39
+ layoutMetrics_.frame.origin.y += lastKnownHeaderHeight *
40
+ headerCorrectionModes.check(
41
+ FrameCorrectionModes::Mode::FrameOriginCorrection);
42
+ layoutMetrics_.frame.size.height -= lastKnownHeaderHeight *
43
+ headerCorrectionModes.check(
44
+ FrameCorrectionModes::Mode::FrameHeightCorrection);
45
+ }
46
+
47
+ void RNSScreenShadowNode::setHeaderHeight(float headerHeight) {
48
+ getStateDataMutable().setHeaderHeight(headerHeight);
49
+ }
50
+
51
+ FrameCorrectionModes &RNSScreenShadowNode::getFrameCorrectionModes() {
52
+ return getStateDataMutable().getFrameCorrectionModes();
53
+ }
54
+
55
+ RNSScreenShadowNode::StateData &RNSScreenShadowNode::getStateDataMutable() {
56
+ // We assume that this method is called to mutate the data, so we ensure
57
+ // we're unsealed.
58
+ ensureUnsealed();
59
+ return const_cast<RNSScreenShadowNode::StateData &>(getStateData());
60
+ }
61
+
62
+ #endif // ANDROID
63
+
14
64
  } // namespace react
15
65
  } // namespace facebook
@@ -4,11 +4,15 @@
4
4
  #include <react/renderer/components/rnscreens/EventEmitters.h>
5
5
  #include <react/renderer/components/rnscreens/Props.h>
6
6
  #include <react/renderer/components/view/ConcreteViewShadowNode.h>
7
+ #include <react/renderer/core/LayoutContext.h>
8
+ #include "FrameCorrectionModes.h"
7
9
  #include "RNSScreenState.h"
8
10
 
9
11
  namespace facebook {
10
12
  namespace react {
11
13
 
14
+ using namespace rnscreens;
15
+
12
16
  JSI_EXPORT extern const char RNSScreenComponentName[];
13
17
 
14
18
  class JSI_EXPORT RNSScreenShadowNode final : public ConcreteViewShadowNode<
@@ -18,8 +22,26 @@ class JSI_EXPORT RNSScreenShadowNode final : public ConcreteViewShadowNode<
18
22
  RNSScreenState> {
19
23
  public:
20
24
  using ConcreteViewShadowNode::ConcreteViewShadowNode;
25
+ using StateData = ConcreteViewShadowNode::ConcreteStateData;
26
+
27
+ #pragma mark - ShadowNode overrides
28
+
29
+ Point getContentOriginOffset(bool includeTransform) const override;
30
+
31
+ void layout(LayoutContext layoutContext) override;
32
+
33
+ #pragma mark - Custom interface
34
+
35
+ void setHeaderHeight(float headerHeight);
36
+
37
+ FrameCorrectionModes &getFrameCorrectionModes();
38
+
39
+ private:
40
+ #ifdef ANDROID
41
+ void applyFrameCorrections();
21
42
 
22
- Point getContentOriginOffset() const override;
43
+ StateData &getStateDataMutable();
44
+ #endif // ANDROID
23
45
  };
24
46
 
25
47
  } // namespace react
@@ -3,12 +3,32 @@
3
3
  namespace facebook {
4
4
  namespace react {
5
5
 
6
+ using namespace rnscreens;
7
+
6
8
  #ifdef ANDROID
7
9
  folly::dynamic RNSScreenState::getDynamic() const {
8
10
  return folly::dynamic::object("frameWidth", frameSize.width)(
9
11
  "frameHeight", frameSize.height)("contentOffsetX", contentOffset.x)(
10
12
  "contentOffsetY", contentOffset.y);
11
13
  }
14
+
15
+ void RNSScreenState::setHeaderHeight(float headerHeight) {
16
+ lastKnownHeaderHeight_ = headerHeight;
17
+ }
18
+
19
+ float RNSScreenState::getLastKnownHeaderHeight() const noexcept {
20
+ return lastKnownHeaderHeight_;
21
+ }
22
+
23
+ FrameCorrectionModes &RNSScreenState::getFrameCorrectionModes() noexcept {
24
+ return headerCorrectionModes_;
25
+ }
26
+
27
+ const FrameCorrectionModes &RNSScreenState::getHeaderCorrectionModes()
28
+ const noexcept {
29
+ return headerCorrectionModes_;
30
+ }
31
+
12
32
  #endif
13
33
 
14
34
  } // namespace react
@@ -9,9 +9,13 @@
9
9
  #include <react/renderer/mapbuffer/MapBufferBuilder.h>
10
10
  #endif
11
11
 
12
+ #include "FrameCorrectionModes.h"
13
+
12
14
  namespace facebook {
13
15
  namespace react {
14
16
 
17
+ using namespace rnscreens;
18
+
15
19
  class JSI_EXPORT RNSScreenState final {
16
20
  public:
17
21
  using Shared = std::shared_ptr<const RNSScreenState>;
@@ -27,7 +31,9 @@ class JSI_EXPORT RNSScreenState final {
27
31
  (Float)data["frameHeight"].getDouble()}),
28
32
  contentOffset(Point{
29
33
  (Float)data["contentOffsetX"].getDouble(),
30
- (Float)data["contentOffsetY"].getDouble()}){};
34
+ (Float)data["contentOffsetY"].getDouble()}),
35
+ lastKnownHeaderHeight_{previousState.lastKnownHeaderHeight_},
36
+ headerCorrectionModes_{previousState.headerCorrectionModes_} {};
31
37
  #endif
32
38
 
33
39
  const Size frameSize{};
@@ -39,8 +45,24 @@ class JSI_EXPORT RNSScreenState final {
39
45
  return MapBufferBuilder::EMPTY();
40
46
  };
41
47
 
48
+ void setHeaderHeight(float headerHeight);
49
+
50
+ float getLastKnownHeaderHeight() const noexcept;
51
+
52
+ FrameCorrectionModes &getFrameCorrectionModes() noexcept;
53
+
54
+ const FrameCorrectionModes &getHeaderCorrectionModes() const noexcept;
55
+
42
56
  #endif
43
57
 
58
+ private:
59
+ #ifdef ANDROID
60
+ // Header height as measured on dummy layout
61
+ float lastKnownHeaderHeight_{0.f};
62
+
63
+ FrameCorrectionModes headerCorrectionModes_{};
64
+ #endif // ANDROID
65
+
44
66
  #pragma mark - Getters
45
67
  };
46
68
 
@@ -0,0 +1,36 @@
1
+ #pragma once
2
+
3
+ #include <react/renderer/graphics/Float.h>
4
+ #include <react/renderer/graphics/Size.h>
5
+ #include <cmath>
6
+ #include <concepts>
7
+
8
+ namespace rnscreens {
9
+
10
+ namespace react = facebook::react;
11
+
12
+ template <typename T>
13
+ requires std::is_floating_point_v<T>
14
+ inline constexpr bool equalWithRespectToEps(const T a, const T b, const T eps) {
15
+ return std::abs(a - b) <= eps;
16
+ }
17
+
18
+ /**
19
+ * Compares given two frame sizes with respect to the epsilon.
20
+ *
21
+ * @param first first frame size
22
+ * @param second second frame size
23
+ * @param eps comparison precision, defaults to 0.01, which should ensure that
24
+ * precision of comparison is under 1px
25
+ * @return whether the frame dimensions are the same with respect to given
26
+ * epsilon
27
+ */
28
+ inline constexpr bool checkFrameSizesEqualWithEps(
29
+ const react::Size &first,
30
+ const react::Size &second,
31
+ const react::Float eps = 0.01) {
32
+ return equalWithRespectToEps(first.width, second.width, eps) &&
33
+ equalWithRespectToEps(first.height, second.height, eps);
34
+ }
35
+
36
+ } // namespace rnscreens
@@ -0,0 +1,25 @@
1
+ #include "RNSScreenRemovalListener.h"
2
+ #include <react/renderer/mounting/ShadowViewMutation.h>
3
+ using namespace facebook::react;
4
+
5
+ std::optional<MountingTransaction> RNSScreenRemovalListener::pullTransaction(
6
+ SurfaceId surfaceId,
7
+ MountingTransaction::Number transactionNumber,
8
+ const TransactionTelemetry &telemetry,
9
+ ShadowViewMutationList mutations) const {
10
+ for (const ShadowViewMutation &mutation : mutations) {
11
+ if (mutation.type == ShadowViewMutation::Type::Remove &&
12
+ mutation.oldChildShadowView.componentName != nullptr &&
13
+ strcmp(mutation.parentShadowView.componentName, "RNSScreenStack") ==
14
+ 0) {
15
+ listenerFunction_(mutation.oldChildShadowView.tag);
16
+ }
17
+ }
18
+
19
+ return MountingTransaction{
20
+ surfaceId, transactionNumber, std::move(mutations), telemetry};
21
+ }
22
+
23
+ bool RNSScreenRemovalListener::shouldOverridePullTransaction() const {
24
+ return true;
25
+ }
@@ -0,0 +1,20 @@
1
+ #pragma once
2
+
3
+ #include <react/renderer/componentregistry/ComponentDescriptorFactory.h>
4
+ #include <react/renderer/mounting/MountingOverrideDelegate.h>
5
+ #include <react/renderer/mounting/ShadowView.h>
6
+
7
+ using namespace facebook::react;
8
+
9
+ struct RNSScreenRemovalListener : public MountingOverrideDelegate {
10
+ std::function<void(int)> listenerFunction_;
11
+ RNSScreenRemovalListener(std::function<void(int)> &&listenerFunction_)
12
+ : listenerFunction_(std::move(listenerFunction_)) {}
13
+
14
+ bool shouldOverridePullTransaction() const override;
15
+ std::optional<MountingTransaction> pullTransaction(
16
+ SurfaceId surfaceId,
17
+ MountingTransaction::Number number,
18
+ const TransactionTelemetry &telemetry,
19
+ ShadowViewMutationList mutations) const override;
20
+ };
package/ios/RNSConvert.h CHANGED
@@ -1,4 +1,5 @@
1
1
  #ifdef RCT_NEW_ARCH_ENABLED
2
+ #import <UIKit/UIKit.h>
2
3
  #import <react/renderer/components/rnscreens/Props.h>
3
4
  #import "RNSEnums.h"
4
5
 
@@ -7,6 +7,28 @@
7
7
 
8
8
  @implementation RNSModalScreen
9
9
 
10
+ // When using UIModalPresentationStyleFullScreen the whole view hierarchy mounted under primary `UITransitionView` is
11
+ // removed, including React's root view, which observes for trait collection changes & sends it to `Appearance` module
12
+ // via system notification centre. To workaround this detached-root-view-situation we emit the event to React's
13
+ // `Appearance` module ourselves. For the RCTRootView observer, visit
14
+ // https://github.com/facebook/react-native/blob/d3e0430deac573fd44792e6005d5de20e9ad2797/packages/react-native/React/Base/RCTRootView.m#L362
15
+ // For more information, see https://github.com/software-mansion/react-native-screens/pull/2211.
16
+ - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
17
+ {
18
+ [super traitCollectionDidChange:previousTraitCollection];
19
+ if (RCTSharedApplication().applicationState == UIApplicationStateBackground ||
20
+ self.stackPresentation != RNSScreenStackPresentationFullScreenModal) {
21
+ return;
22
+ }
23
+
24
+ [[NSNotificationCenter defaultCenter]
25
+ postNotificationName:RCTUserInterfaceStyleDidChangeNotification
26
+ object:self
27
+ userInfo:@{
28
+ RCTUserInterfaceStyleDidChangeNotificationTraitCollectionKey : self.traitCollection,
29
+ }];
30
+ }
31
+
10
32
  #ifdef RCT_NEW_ARCH_ENABLED
11
33
  + (react::ComponentDescriptorProvider)componentDescriptorProvider
12
34
  {
package/ios/RNSModule.mm CHANGED
@@ -116,7 +116,7 @@ RCT_EXPORT_MODULE()
116
116
  {
117
117
  RNSScreenStackView *view = [self getScreenStackView:stackTag];
118
118
  if (view != nil && ![view isKindOfClass:[RNSScreenStackView class]]) {
119
- RCTLogError(@"Invalid view type, expecting RNSScreenStackView, got: %@", view);
119
+ RCTLogError(@"[RNScreens] Invalid view type, expecting RNSScreenStackView, got: %@", view);
120
120
  return nil;
121
121
  }
122
122
  return view;
package/ios/RNSScreen.h CHANGED
@@ -40,7 +40,7 @@ namespace react = facebook::react;
40
40
  - (void)notifyFinishTransitioning;
41
41
  - (RNSScreenView *)screenView;
42
42
  #ifdef RCT_NEW_ARCH_ENABLED
43
- - (void)setViewToSnapshot:(UIView *)snapshot;
43
+ - (void)setViewToSnapshot;
44
44
  - (CGFloat)calculateHeaderHeightIsModal:(BOOL)isModal;
45
45
  #endif
46
46
 
@@ -56,6 +56,7 @@ namespace react = facebook::react;
56
56
  #endif
57
57
 
58
58
  @property (nonatomic) BOOL fullScreenSwipeEnabled;
59
+ @property (nonatomic) BOOL fullScreenSwipeShadowEnabled;
59
60
  @property (nonatomic) BOOL gestureEnabled;
60
61
  @property (nonatomic) BOOL hasStatusBarHiddenSet;
61
62
  @property (nonatomic) BOOL hasStatusBarStyleSet;
package/ios/RNSScreen.mm CHANGED
@@ -25,6 +25,8 @@
25
25
  #import "RNSScreenStack.h"
26
26
  #import "RNSScreenStackHeaderConfig.h"
27
27
 
28
+ #import "UIView+RNSUtility.h"
29
+
28
30
  #ifdef RCT_NEW_ARCH_ENABLED
29
31
  namespace react = facebook::react;
30
32
  #endif // RCT_NEW_ARCH_ENABLED
@@ -414,7 +416,7 @@ namespace react = facebook::react;
414
416
  [[RNSHeaderHeightChangeEvent alloc] initWithEventName:@"onHeaderHeightChange"
415
417
  reactTag:[NSNumber numberWithInt:self.tag]
416
418
  headerHeight:headerHeight];
417
- [[RCTBridge currentBridge].eventDispatcher notifyObserversOfEvent:event];
419
+ [self postNotificationForEventDispatcherObserversWithEvent:event];
418
420
  #else
419
421
  if (self.onHeaderHeightChange) {
420
422
  self.onHeaderHeightChange(@{
@@ -473,22 +475,13 @@ namespace react = facebook::react;
473
475
  }
474
476
  }
475
477
 
476
- #ifdef RCT_NEW_ARCH_ENABLED
477
- - (RCTSurfaceTouchHandler *)touchHandler
478
- #else
479
- - (RCTTouchHandler *)touchHandler
480
- #endif
478
+ - (nullable RNS_TOUCH_HANDLER_ARCH_TYPE *)touchHandler
481
479
  {
482
480
  if (_touchHandler != nil) {
483
481
  return _touchHandler;
484
482
  }
485
- UIView *parent = [self superview];
486
- while (parent != nil && ![parent respondsToSelector:@selector(touchHandler)])
487
- parent = parent.superview;
488
- if (parent != nil) {
489
- return [parent performSelector:@selector(touchHandler)];
490
- }
491
- return nil;
483
+
484
+ return [self rnscreens_findTouchHandlerInAncestorChain];
492
485
  }
493
486
 
494
487
  - (void)notifyFinishTransitioning
@@ -509,7 +502,7 @@ namespace react = facebook::react;
509
502
  progress:progress
510
503
  closing:closing
511
504
  goingForward:goingForward];
512
- [[RCTBridge currentBridge].eventDispatcher notifyObserversOfEvent:event];
505
+ [self postNotificationForEventDispatcherObserversWithEvent:event];
513
506
  #else
514
507
  if (self.onTransitionProgress) {
515
508
  self.onTransitionProgress(@{
@@ -667,6 +660,14 @@ namespace react = facebook::react;
667
660
  #pragma mark - Fabric specific
668
661
  #ifdef RCT_NEW_ARCH_ENABLED
669
662
 
663
+ - (void)postNotificationForEventDispatcherObserversWithEvent:(NSObject<RCTEvent> *)event
664
+ {
665
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:event, @"event", nil];
666
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTNotifyEventDispatcherObserversOfEvent_DEPRECATED"
667
+ object:nil
668
+ userInfo:userInfo];
669
+ }
670
+
670
671
  - (BOOL)hasHeaderConfig
671
672
  {
672
673
  return _config != nil;
@@ -710,6 +711,8 @@ namespace react = facebook::react;
710
711
 
711
712
  [self setFullScreenSwipeEnabled:newScreenProps.fullScreenSwipeEnabled];
712
713
 
714
+ [self setFullScreenSwipeShadowEnabled:newScreenProps.fullScreenSwipeShadowEnabled];
715
+
713
716
  [self setGestureEnabled:newScreenProps.gestureEnabled];
714
717
 
715
718
  [self setTransitionDuration:[NSNumber numberWithInt:newScreenProps.transitionDuration]];
@@ -793,7 +796,7 @@ namespace react = facebook::react;
793
796
  _newLayoutMetrics = layoutMetrics;
794
797
  _oldLayoutMetrics = oldLayoutMetrics;
795
798
  UIViewController *parentVC = self.reactViewController.parentViewController;
796
- if (parentVC != nil && ![parentVC isKindOfClass:[RNSNavigationController class]]) {
799
+ if (parentVC == nil || ![parentVC isKindOfClass:[RNSNavigationController class]]) {
797
800
  [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
798
801
  }
799
802
  // when screen is mounted under RNSNavigationController it's size is controller
@@ -1388,12 +1391,16 @@ Class<RCTComponentViewProtocol> RNSScreenCls(void)
1388
1391
  #ifdef RCT_NEW_ARCH_ENABLED
1389
1392
  #pragma mark - Fabric specific
1390
1393
 
1391
- - (void)setViewToSnapshot:(UIView *)snapshot
1394
+ - (void)setViewToSnapshot
1392
1395
  {
1393
1396
  UIView *superView = self.view.superview;
1394
- [self.view removeFromSuperview];
1395
- self.view = snapshot;
1396
- [superView addSubview:self.view];
1397
+ // if we dismissed the view natively, it will already be detached from view hierarchy
1398
+ if (self.view.window != nil) {
1399
+ UIView *snapshot = [self.view snapshotViewAfterScreenUpdates:NO];
1400
+ [self.view removeFromSuperview];
1401
+ self.view = snapshot;
1402
+ [superView addSubview:snapshot];
1403
+ }
1397
1404
  }
1398
1405
 
1399
1406
  #else
@@ -1434,6 +1441,7 @@ RCT_EXPORT_MODULE()
1434
1441
  RCT_REMAP_VIEW_PROPERTY(activityState, activityStateOrNil, NSNumber)
1435
1442
  RCT_EXPORT_VIEW_PROPERTY(customAnimationOnSwipe, BOOL);
1436
1443
  RCT_EXPORT_VIEW_PROPERTY(fullScreenSwipeEnabled, BOOL);
1444
+ RCT_EXPORT_VIEW_PROPERTY(fullScreenSwipeShadowEnabled, BOOL);
1437
1445
  RCT_EXPORT_VIEW_PROPERTY(gestureEnabled, BOOL)
1438
1446
  RCT_EXPORT_VIEW_PROPERTY(gestureResponseDistance, NSDictionary)
1439
1447
  RCT_EXPORT_VIEW_PROPERTY(hideKeyboardOnSwipe, BOOL)