Skip to content

Commit 24406c3

Browse files
committed
Merge branch 'main' into @kligarski/add-e2e-tests
2 parents d493666 + 29c3ae2 commit 24406c3

24 files changed

Lines changed: 277 additions & 90 deletions

File tree

FabricExample/ios/Podfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,7 +1733,7 @@ PODS:
17331733
- ReactCommon/turbomodule/bridging
17341734
- ReactCommon/turbomodule/core
17351735
- Yoga
1736-
- RNScreens (4.10.0-beta.2):
1736+
- RNScreens (4.10.0-beta.3):
17371737
- DoubleConversion
17381738
- glog
17391739
- hermes-engine
@@ -1754,9 +1754,9 @@ PODS:
17541754
- ReactCodegen
17551755
- ReactCommon/turbomodule/bridging
17561756
- ReactCommon/turbomodule/core
1757-
- RNScreens/common (= 4.10.0-beta.2)
1757+
- RNScreens/common (= 4.10.0-beta.3)
17581758
- Yoga
1759-
- RNScreens/common (4.10.0-beta.2):
1759+
- RNScreens/common (4.10.0-beta.3):
17601760
- DoubleConversion
17611761
- glog
17621762
- hermes-engine
@@ -2075,7 +2075,7 @@ SPEC CHECKSUMS:
20752075
ReactCommon: 179964ffc47fa62ad0e1eebac704e88c59b46667
20762076
RNGestureHandler: 4e7defe5095e936424173fc75f0bf2af5bba8e23
20772077
RNReanimated: 183ca222293bd622678e387100e54d03d952c73b
2078-
RNScreens: 2aa993d221ba928ee75682f3c0940620a8d46865
2078+
RNScreens: b40d97d6ad4b6f1f55552bed30b845ae01ff3d2c
20792079
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
20802080
Yoga: 330be28eee1242da875db9e851b19a4df496b999
20812081

RNScreens.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ require "json"
33
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
44

55
new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
6-
platform = new_arch_enabled ? "11.0" : "9.0"
6+
min_supported_ios_version = new_arch_enabled ? "15.1" : "15.1"
77
source_files = new_arch_enabled ? 'ios/**/*.{h,m,mm,cpp}' : ["ios/**/*.{h,m,mm}", "cpp/RNScreensTurboModule.cpp", "cpp/RNScreensTurboModule.h"]
88

99
Pod::Spec.new do |s|
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
1616
s.homepage = "https://github.com/software-mansion/react-native-screens"
1717
s.license = "MIT"
1818
s.author = { "author" => "author@domain.cn" }
19-
s.platforms = { :ios => platform, :tvos => "11.0", :visionos => "1.0" }
19+
s.platforms = { :ios => min_supported_ios_version, :tvos => "11.0", :visionos => "1.0" }
2020
s.source = { :git => "https://github.com/software-mansion/react-native-screens.git", :tag => "#{s.version}" }
2121
s.source_files = source_files
2222
s.project_header_files = "cpp/**/*.h" # Don't expose C++ headers publicly to allow importing framework into Swift files

android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ abstract class FabricEnabledViewGroup(
1414
) : ViewGroup(context) {
1515
private var mStateWrapper: StateWrapper? = null
1616

17-
private var lastSetWidth = 0f
18-
private var lastSetHeight = 0f
17+
private var lastWidth = 0f
18+
private var lastHeight = 0f
19+
private var lastHeaderHeight = 0f
1920

2021
fun setStateWrapper(wrapper: StateWrapper?) {
2122
mStateWrapper = wrapper
@@ -42,14 +43,16 @@ abstract class FabricEnabledViewGroup(
4243
// Check incoming state values. If they're already the correct value, return early to prevent
4344
// infinite UpdateState/SetState loop.
4445
val delta = 0.9f
45-
if (abs(lastSetWidth - realWidth) < delta &&
46-
abs(lastSetHeight - realHeight) < delta
46+
if (abs(lastWidth - realWidth) < delta &&
47+
abs(lastHeight - realHeight) < delta &&
48+
abs(lastHeaderHeight - realHeaderHeight) < delta
4749
) {
4850
return
4951
}
5052

51-
lastSetWidth = realWidth
52-
lastSetHeight = realHeight
53+
lastWidth = realWidth
54+
lastHeight = realHeight
55+
lastHeaderHeight = realHeaderHeight
5356
val map: WritableMap =
5457
WritableNativeMap().apply {
5558
putDouble("frameWidth", realWidth.toDouble())

android/src/fabric/java/com/swmansion/rnscreens/NativeProxy.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ class NativeProxy {
4141
}
4242
}
4343

44-
// Called from native
44+
// Called from native. Currently this method is called from MountingCoordinator thread,
45+
// which usually is not UI thread.
4546
@DoNotStrip
4647
public fun notifyScreenRemoved(screenTag: Int) {
4748
// Since RN 0.78 the screenTag we receive as argument here might not belong to a screen

android/src/main/java/com/swmansion/rnscreens/Screen.kt

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import com.google.android.material.shape.CornerFamily
2727
import com.google.android.material.shape.MaterialShapeDrawable
2828
import com.google.android.material.shape.ShapeAppearanceModel
2929
import com.swmansion.rnscreens.bottomsheet.isSheetFitToContents
30+
import com.swmansion.rnscreens.bottomsheet.useSingleDetent
3031
import com.swmansion.rnscreens.bottomsheet.usesFormSheetPresentation
3132
import com.swmansion.rnscreens.events.HeaderHeightChangeEvent
3233
import com.swmansion.rnscreens.events.SheetDetentChangedEvent
@@ -103,6 +104,9 @@ class Screen(
103104
field = value
104105
}
105106

107+
private val isNativeStackScreen: Boolean
108+
get() = container is ScreenStack
109+
106110
init {
107111
// we set layout params as WindowManager.LayoutParams to workaround the issue with TextInputs
108112
// not displaying modal menus (e.g., copy/paste or selection). The missing menus are due to the
@@ -132,11 +136,7 @@ class Screen(
132136
val height = bottom - top
133137

134138
if (isSheetFitToContents()) {
135-
sheetBehavior?.let {
136-
if (it.maxHeight != height) {
137-
it.maxHeight = height
138-
}
139-
}
139+
sheetBehavior?.useSingleDetent(height)
140140

141141
if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
142142
// On old architecture we delay enter transition in order to wait for initial frame.
@@ -173,23 +173,31 @@ class Screen(
173173
r: Int,
174174
b: Int,
175175
) {
176-
if (container is ScreenStack && changed) {
176+
// In case of form sheet we get layout notification a bit later, in `onBottomSheetBehaviorDidLayout`
177+
// after the attached behaviour laid out this view.
178+
if (changed && isNativeStackScreen && !usesFormSheetPresentation()) {
177179
val width = r - l
178180
val height = b - t
179181

180-
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
181-
updateScreenSizeFabric(width, height, t)
182-
} else {
183-
updateScreenSizePaper(width, height)
184-
}
182+
dispatchShadowStateUpdate(width, height, t)
185183

186-
footer?.onParentLayout(changed, l, t, r, b, container!!.height)
184+
// FormSheet has no header in current model.
187185
notifyHeaderHeightChange(t)
186+
}
187+
}
188188

189-
if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
190-
maybeTriggerPostponedTransition()
191-
}
189+
internal fun onBottomSheetBehaviorDidLayout(coordinatorLayoutDidChange: Boolean) {
190+
if (!usesFormSheetPresentation()) {
191+
return
192+
}
193+
if (coordinatorLayoutDidChange && isNativeStackScreen) {
194+
dispatchShadowStateUpdate(width, height, top)
195+
}
196+
if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
197+
maybeTriggerPostponedTransition()
192198
}
199+
200+
footer?.onParentLayout(coordinatorLayoutDidChange, left, top, right, bottom, container!!.height)
193201
}
194202

195203
private fun maybeTriggerPostponedTransition() {
@@ -214,6 +222,21 @@ class Screen(
214222
)
215223
}
216224

225+
/**
226+
* @param offsetY ignored on old architecture
227+
*/
228+
private fun dispatchShadowStateUpdate(
229+
width: Int,
230+
height: Int,
231+
offsetY: Int,
232+
) {
233+
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
234+
updateScreenSizeFabric(width, height, offsetY)
235+
} else {
236+
updateScreenSizePaper(width, height)
237+
}
238+
}
239+
217240
val headerConfig: ScreenStackHeaderConfig?
218241
get() = children.find { it is ScreenStackHeaderConfig } as? ScreenStackHeaderConfig
219242

@@ -487,6 +510,11 @@ class Screen(
487510
isStable: Boolean,
488511
) {
489512
dispatchSheetDetentChanged(detentIndex, isStable)
513+
// There is no need to update shadow state for transient sheet states -
514+
// we are unsure of the exact sheet position anyway.
515+
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED && isStable) {
516+
updateScreenSizeFabric(width, height, top)
517+
}
490518
}
491519

492520
private fun dispatchSheetDetentChanged(

android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -175,21 +175,18 @@ class ScreenStack(
175175
childDrawingOrderStrategy = null
176176

177177
// Determine new first & last visible screens.
178-
// Scope function to limit the scope of locals.
179-
run {
180-
val notDismissedWrappers =
181-
screenWrappers
182-
.asReversed()
183-
.asSequence()
184-
.filter { !dismissedWrappers.contains(it) && it.screen.activityState !== Screen.ActivityState.INACTIVE }
185-
186-
newTop = notDismissedWrappers.firstOrNull()
187-
visibleBottom =
188-
notDismissedWrappers
189-
.dropWhile { it.screen.isTransparent() }
190-
.firstOrNull()
191-
?.takeUnless { it === newTop }
192-
}
178+
val notDismissedWrappers =
179+
screenWrappers
180+
.asReversed()
181+
.asSequence()
182+
.filter { !dismissedWrappers.contains(it) && it.screen.activityState !== Screen.ActivityState.INACTIVE }
183+
184+
newTop = notDismissedWrappers.firstOrNull()
185+
visibleBottom =
186+
notDismissedWrappers
187+
.dropWhile { it.screen.isTransparent() }
188+
.firstOrNull()
189+
?.takeUnless { it === newTop }
193190

194191
var shouldUseOpenAnimation = true
195192
var stackAnimation: StackAnimation? = null

android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ class ScreenStackFragment :
480480
constructor(context: Context, fragment: ScreenStackFragment) : this(
481481
context,
482482
fragment,
483-
ScreensCoordinatorLayoutPointerEventsImpl(),
483+
PointerEventsBoxNoneImpl(),
484484
)
485485

486486
override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets = super.onApplyWindowInsets(insets)
@@ -543,6 +543,20 @@ class ScreenStackFragment :
543543
}
544544
}
545545

546+
override fun onLayout(
547+
changed: Boolean,
548+
l: Int,
549+
t: Int,
550+
r: Int,
551+
b: Int,
552+
) {
553+
super.onLayout(changed, l, t, r, b)
554+
555+
if (fragment.screen.usesFormSheetPresentation()) {
556+
fragment.screen.onBottomSheetBehaviorDidLayout(changed)
557+
}
558+
}
559+
546560
// override fun reactTagForTouch(touchX: Float, touchY: Float): Int {
547561
// throw IllegalStateException("Screen wrapper should never be asked for the view tag")
548562
// }

android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import androidx.fragment.app.Fragment
1515
import com.facebook.react.ReactApplication
1616
import com.facebook.react.bridge.JSApplicationIllegalArgumentException
1717
import com.facebook.react.bridge.ReactContext
18+
import com.facebook.react.uimanager.ReactPointerEventsView
1819
import com.facebook.react.uimanager.UIManagerHelper
1920
import com.facebook.react.views.text.ReactTypefaceUtils
2021
import com.swmansion.rnscreens.events.HeaderAttachedEvent
@@ -23,7 +24,11 @@ import kotlin.math.max
2324

2425
class ScreenStackHeaderConfig(
2526
context: Context,
26-
) : FabricEnabledHeaderConfigViewGroup(context) {
27+
private val pointerEventsImpl: ReactPointerEventsView
28+
) : FabricEnabledHeaderConfigViewGroup(context), ReactPointerEventsView by pointerEventsImpl {
29+
30+
constructor(context: Context): this(context, pointerEventsImpl = PointerEventsBoxNoneImpl())
31+
2732
private val configSubviews = ArrayList<ScreenStackHeaderSubview>(3)
2833
val toolbar: CustomToolbar
2934
var isHeaderHidden = false // named this way to avoid conflict with platform's isHidden

android/src/main/java/com/swmansion/rnscreens/ScreenStackViewManager.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class ScreenStackViewManager :
6161
index: Int,
6262
): View = parent.getScreenAt(index)
6363

64+
// Old architecture only.
6465
override fun createShadowNodeInstance(context: ReactApplicationContext): LayoutShadowNode = ScreensShadowNode(context)
6566

6667
override fun needsCustomLayoutForChildren() = true

android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetDelegate.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,15 @@ class SheetDelegate(
130130
behavior.apply {
131131
val height =
132132
if (screen.isSheetFitToContents()) {
133-
screen.contentWrapper
134-
.get()
135-
?.height
136-
.takeIf { screen.contentWrapper.get()?.isLaidOut == true }
133+
screen.contentWrapper.get()?.let { contentWrapper ->
134+
contentWrapper.height.takeIf {
135+
// subtree might not be laid out, e.g. after fragment reattachment
136+
// and view recreation, however since it is retained by
137+
// react-native it has its height cached. We want to use it.
138+
// Otherwise we would have to trigger RN layout manually.
139+
contentWrapper.isLaidOut || contentWrapper.height > 0
140+
}
141+
}
137142
} else {
138143
(screen.sheetDetents.first() * containerHeight).toInt()
139144
}

0 commit comments

Comments
 (0)