react-native-screens 4.7.0-beta.2 → 4.7.0-beta.4

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.
@@ -45,8 +45,9 @@ import com.swmansion.rnscreens.bottomsheet.useSingleDetent
45
45
  import com.swmansion.rnscreens.bottomsheet.useThreeDetents
46
46
  import com.swmansion.rnscreens.bottomsheet.useTwoDetents
47
47
  import com.swmansion.rnscreens.bottomsheet.usesFormSheetPresentation
48
+ import com.swmansion.rnscreens.events.ScreenAnimationDelegate
48
49
  import com.swmansion.rnscreens.events.ScreenDismissedEvent
49
- import com.swmansion.rnscreens.events.ScreenEventDelegate
50
+ import com.swmansion.rnscreens.events.ScreenEventEmitter
50
51
  import com.swmansion.rnscreens.ext.recycle
51
52
  import com.swmansion.rnscreens.transition.ExternalBoundaryValuesEvaluator
52
53
  import com.swmansion.rnscreens.utils.DeviceUtils
@@ -366,7 +367,14 @@ class ScreenStackFragment :
366
367
  }
367
368
  animatorSet.play(alphaAnimator).with(slideAnimator)
368
369
  }
369
- animatorSet.addListener(ScreenEventDelegate(this))
370
+ animatorSet.addListener(
371
+ ScreenAnimationDelegate(
372
+ this,
373
+ ScreenEventEmitter(this.screen),
374
+ if (enter) ScreenAnimationDelegate.AnimationType.ENTER
375
+ else ScreenAnimationDelegate.AnimationType.EXIT
376
+ )
377
+ )
370
378
  return animatorSet
371
379
  }
372
380
 
@@ -0,0 +1,88 @@
1
+ package com.swmansion.rnscreens.events
2
+
3
+ import android.animation.Animator
4
+ import android.util.Log
5
+ import com.swmansion.rnscreens.ScreenStackFragmentWrapper
6
+
7
+ // The goal is to make this universal delegate for handling animation progress related logic.
8
+ // At this moment this class works only with form sheet presentation.
9
+ class ScreenAnimationDelegate(
10
+ private val wrapper: ScreenStackFragmentWrapper,
11
+ private val eventEmitter: ScreenEventEmitter?,
12
+ private val animationType: AnimationType,
13
+ ) : Animator.AnimatorListener {
14
+ enum class AnimationType {
15
+ ENTER,
16
+ EXIT
17
+ }
18
+
19
+ private var currentState: LifecycleState = LifecycleState.INITIALIZED
20
+
21
+ private fun progressState() {
22
+ currentState =
23
+ when (currentState) {
24
+ LifecycleState.INITIALIZED -> LifecycleState.START_DISPATCHED
25
+ LifecycleState.START_DISPATCHED -> LifecycleState.END_DISPATCHED
26
+ LifecycleState.END_DISPATCHED -> LifecycleState.END_DISPATCHED
27
+ }
28
+ }
29
+
30
+ override fun onAnimationStart(animation: Animator) {
31
+ if (currentState === LifecycleState.INITIALIZED) {
32
+ progressState()
33
+
34
+ // These callbacks do not work as expected from this call site, TODO: investigate it.
35
+ // To fix it quickly we emit required events manually
36
+ // wrapper.onViewAnimationStart()
37
+
38
+ when (animationType) {
39
+ AnimationType.ENTER -> eventEmitter?.dispatchOnWillAppear()
40
+ AnimationType.EXIT -> eventEmitter?.dispatchOnWillDisappear()
41
+ }
42
+
43
+ val isExitAnimation = animationType === AnimationType.EXIT
44
+ eventEmitter?.dispatchTransitionProgress(
45
+ 0.0f,
46
+ isExitAnimation,
47
+ isExitAnimation,
48
+ )
49
+ }
50
+ }
51
+
52
+ override fun onAnimationEnd(animation: Animator) {
53
+ if (currentState === LifecycleState.START_DISPATCHED) {
54
+ progressState()
55
+ animation.removeListener(this)
56
+
57
+ // wrapper.onViewAnimationEnd()
58
+
59
+ when (animationType) {
60
+ AnimationType.ENTER -> eventEmitter?.dispatchOnAppear()
61
+ AnimationType.EXIT -> eventEmitter?.dispatchOnDisappear()
62
+ }
63
+
64
+ val isExitAnimation = animationType === AnimationType.EXIT
65
+ eventEmitter?.dispatchTransitionProgress(
66
+ 1.0f,
67
+ isExitAnimation,
68
+ isExitAnimation,
69
+ )
70
+
71
+ wrapper.screen.endRemovalTransition()
72
+ }
73
+ }
74
+
75
+ override fun onAnimationCancel(animation: Animator) = Unit
76
+
77
+ override fun onAnimationRepeat(animation: Animator) = Unit
78
+
79
+ private enum class LifecycleState {
80
+ INITIALIZED,
81
+ START_DISPATCHED,
82
+ END_DISPATCHED,
83
+ }
84
+
85
+ companion object {
86
+ const val TAG = "ScreenEventDelegate"
87
+ }
88
+ }
@@ -0,0 +1,36 @@
1
+ package com.swmansion.rnscreens.events
2
+
3
+ import com.facebook.react.uimanager.UIManagerHelper
4
+ import com.swmansion.rnscreens.Screen
5
+ import com.swmansion.rnscreens.ScreenFragment
6
+
7
+ // TODO: Consider taking weak ref here or accepting screen as argument in every method
8
+ // to avoid reference cycle.
9
+ class ScreenEventEmitter(val screen: Screen) {
10
+ val reactEventDispatcher
11
+ get() = screen.reactEventDispatcher
12
+
13
+ val reactSurfaceId
14
+ get() = UIManagerHelper.getSurfaceId(screen)
15
+
16
+ fun dispatchOnWillAppear() =
17
+ reactEventDispatcher?.dispatchEvent(ScreenWillAppearEvent(reactSurfaceId, screen.id))
18
+
19
+ fun dispatchOnAppear() =
20
+ reactEventDispatcher?.dispatchEvent(ScreenAppearEvent(reactSurfaceId, screen.id))
21
+
22
+ fun dispatchOnWillDisappear() =
23
+ reactEventDispatcher?.dispatchEvent(ScreenWillDisappearEvent(reactSurfaceId, screen.id))
24
+
25
+ fun dispatchOnDisappear() =
26
+ reactEventDispatcher?.dispatchEvent(ScreenDisappearEvent(reactSurfaceId, screen.id))
27
+
28
+ fun dispatchOnDismissed() =
29
+ reactEventDispatcher?.dispatchEvent(ScreenDismissedEvent(reactSurfaceId, screen.id))
30
+
31
+ fun dispatchTransitionProgress(progress: Float, isExitAnimation: Boolean, isGoingForward: Boolean) {
32
+ val sanitizedProgress = progress.coerceIn(0.0f, 1.0f)
33
+ val coalescingKey = ScreenFragment.getCoalescingKey(sanitizedProgress)
34
+ reactEventDispatcher?.dispatchEvent(ScreenTransitionProgressEvent(reactSurfaceId, screen.id, sanitizedProgress, isExitAnimation, isGoingForward, coalescingKey))
35
+ }
36
+ }
@@ -0,0 +1,6 @@
1
+ package com.swmansion.rnscreens.utils
2
+
3
+ import com.facebook.react.views.view.ReactViewBackgroundDrawable
4
+ import com.facebook.react.views.view.ReactViewGroup
5
+
6
+ internal fun ReactViewGroup.resolveBackgroundColor(): Int? = (this.background as? ReactViewBackgroundDrawable)?.color
@@ -0,0 +1,9 @@
1
+ package com.swmansion.rnscreens.utils
2
+
3
+ import com.facebook.react.common.annotations.UnstableReactNativeAPI
4
+ import com.facebook.react.uimanager.drawable.CSSBackgroundDrawable
5
+ import com.facebook.react.views.view.ReactViewGroup
6
+
7
+ @OptIn(UnstableReactNativeAPI::class)
8
+ internal fun ReactViewGroup.resolveBackgroundColor(): Int? = (this.background as? CSSBackgroundDrawable)?.color
9
+
@@ -0,0 +1,9 @@
1
+ package com.swmansion.rnscreens.utils
2
+
3
+ import com.facebook.react.common.annotations.UnstableReactNativeAPI
4
+ import com.facebook.react.uimanager.BackgroundStyleApplicator
5
+ import com.facebook.react.uimanager.drawable.CSSBackgroundDrawable
6
+ import com.facebook.react.views.view.ReactViewGroup
7
+
8
+ internal fun ReactViewGroup.resolveBackgroundColor(): Int? = BackgroundStyleApplicator.getBackgroundColor(this)
9
+
@@ -73,15 +73,30 @@ namespace react = facebook::react;
73
73
 
74
74
  - (void)attachToAncestorScreenView
75
75
  {
76
- if (![self.reactSuperview isKindOfClass:RNSScreenView.class]) {
77
- RCTLogError(@"Expected reactSuperview to be a RNSScreenView. Found %@", self.reactSuperview);
76
+ RNSScreen *_Nullable screen =
77
+ static_cast<RNSScreen *_Nullable>([[self findFirstScreenViewAncestor] reactViewController]);
78
+ if (screen == nil) {
79
+ RCTLogError(@"Failed to find parent screen controller from %@.", self);
78
80
  return;
79
81
  }
80
-
81
- RNSScreen *screen = (RNSScreen *)[self.reactSuperview reactViewController];
82
82
  [self attachToAncestorScreenViewStartingFrom:screen];
83
83
  }
84
84
 
85
+ - (nullable RNSScreenView *)findFirstScreenViewAncestor
86
+ {
87
+ UIView *currentView = self;
88
+
89
+ // In standard scenario this should do only a single iteration.
90
+ // Haven't got repro, but we got reports that there are scenarios
91
+ // when there are intermediate views between screen view & the content wrapper.
92
+ // https://github.com/software-mansion/react-native-screens/pull/2683
93
+ do {
94
+ currentView = currentView.reactSuperview;
95
+ } while (currentView != nil && ![currentView isKindOfClass:RNSScreenView.class]);
96
+
97
+ return static_cast<RNSScreenView *_Nullable>(currentView);
98
+ }
99
+
85
100
  #ifdef RCT_NEW_ARCH_ENABLED
86
101
 
87
102
  #pragma mark - RCTComponentViewProtocol
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-screens",
3
- "version": "4.7.0-beta.2",
3
+ "version": "4.7.0-beta.4",
4
4
  "description": "Native navigation primitives for your React Native app.",
5
5
  "scripts": {
6
6
  "submodules": "git submodule update --init --recursive && (cd react-navigation && yarn && yarn build && cd ../)",
@@ -41,6 +41,7 @@
41
41
  "android/src/main/res",
42
42
  "android/src/fabric/",
43
43
  "android/src/paper/",
44
+ "android/src/versioned/",
44
45
  "android/build.gradle",
45
46
  "android/CMakeLists.txt",
46
47
  "ios/",
@@ -1,48 +0,0 @@
1
- package com.swmansion.rnscreens.events
2
-
3
- import android.animation.Animator
4
- import com.swmansion.rnscreens.ScreenFragmentWrapper
5
-
6
- class ScreenEventDelegate(
7
- private val wrapper: ScreenFragmentWrapper,
8
- ) : Animator.AnimatorListener {
9
- private var currentState: LifecycleState = LifecycleState.INITIALIZED
10
-
11
- private fun progressState() {
12
- currentState =
13
- when (currentState) {
14
- LifecycleState.INITIALIZED -> LifecycleState.START_DISPATCHED
15
- LifecycleState.START_DISPATCHED -> LifecycleState.END_DISPATCHED
16
- LifecycleState.END_DISPATCHED -> LifecycleState.END_DISPATCHED
17
- }
18
- }
19
-
20
- override fun onAnimationStart(animation: Animator) {
21
- if (currentState === LifecycleState.INITIALIZED) {
22
- progressState()
23
- wrapper.onViewAnimationStart()
24
- }
25
- }
26
-
27
- override fun onAnimationEnd(animation: Animator) {
28
- if (currentState === LifecycleState.START_DISPATCHED) {
29
- progressState()
30
- animation.removeListener(this)
31
- wrapper.onViewAnimationEnd()
32
- }
33
- }
34
-
35
- override fun onAnimationCancel(animation: Animator) = Unit
36
-
37
- override fun onAnimationRepeat(animation: Animator) = Unit
38
-
39
- private enum class LifecycleState {
40
- INITIALIZED,
41
- START_DISPATCHED,
42
- END_DISPATCHED,
43
- }
44
-
45
- companion object {
46
- const val TAG = "ScreenEventDelegate"
47
- }
48
- }