react-native-screens 3.33.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.
@@ -5,6 +5,7 @@ import android.util.Log
5
5
  import android.view.View
6
6
  import androidx.appcompat.widget.Toolbar
7
7
  import androidx.coordinatorlayout.widget.CoordinatorLayout
8
+ import com.facebook.react.bridge.LifecycleEventListener
8
9
  import com.facebook.react.bridge.ReactApplicationContext
9
10
  import com.facebook.react.uimanager.PixelUtil
10
11
  import com.google.android.material.appbar.AppBarLayout
@@ -19,7 +20,7 @@ import java.lang.ref.WeakReference
19
20
  */
20
21
  internal class ScreenDummyLayoutHelper(
21
22
  reactContext: ReactApplicationContext,
22
- ) {
23
+ ) : LifecycleEventListener {
23
24
  // The state required to compute header dimensions. We want this on instance rather than on class
24
25
  // for context access & being tied to instance lifetime.
25
26
  private lateinit var coordinatorLayout: CoordinatorLayout
@@ -39,7 +40,6 @@ internal class ScreenDummyLayoutHelper(
39
40
  WeakReference(reactContext)
40
41
 
41
42
  init {
42
-
43
43
  // We load the library so that we are able to communicate with our C++ code (descriptor & shadow nodes).
44
44
  // Basically we leak this object to C++, as its lifecycle should span throughout whole application
45
45
  // lifecycle anyway.
@@ -50,16 +50,25 @@ internal class ScreenDummyLayoutHelper(
50
50
  }
51
51
 
52
52
  weakInstance = WeakReference(this)
53
- ensureDummyLayoutWithHeader(reactContext)
53
+
54
+ if (!(reactContext.hasCurrentActivity() && maybeInitDummyLayoutWithHeader(reactContext))) {
55
+ reactContext.addLifecycleEventListener(this)
56
+ }
54
57
  }
55
58
 
56
59
  /**
57
60
  * Initializes dummy view hierarchy with CoordinatorLayout, AppBarLayout and dummy View.
58
61
  * We utilize this to compute header height (app bar layout height) from C++ layer when its needed.
62
+ *
63
+ * @return boolean whether the layout was initialised or not
59
64
  */
60
- private fun ensureDummyLayoutWithHeader(reactContext: ReactApplicationContext) {
61
- if (::coordinatorLayout.isInitialized) {
62
- return
65
+ private fun maybeInitDummyLayoutWithHeader(reactContext: ReactApplicationContext): Boolean {
66
+ if (isLayoutInitialized) {
67
+ return true
68
+ }
69
+
70
+ if (!reactContext.hasCurrentActivity()) {
71
+ return false
63
72
  }
64
73
 
65
74
  // We need to use activity here, as react context does not have theme attributes required by
@@ -108,6 +117,9 @@ internal class ScreenDummyLayoutHelper(
108
117
  addView(appBarLayout)
109
118
  addView(dummyContentView)
110
119
  }
120
+
121
+ isLayoutInitialized = true
122
+ return true
111
123
  }
112
124
 
113
125
  /**
@@ -121,12 +133,18 @@ internal class ScreenDummyLayoutHelper(
121
133
  fontSize: Int,
122
134
  isTitleEmpty: Boolean,
123
135
  ): Float {
124
- if (!::coordinatorLayout.isInitialized) {
125
- Log.e(
126
- TAG,
127
- "[RNScreens] Attempt to access dummy view hierarchy before it is initialized",
128
- )
129
- return 0.0f
136
+ if (!isLayoutInitialized) {
137
+ val reactContext =
138
+ requireReactContext { "[RNScreens] Context was null-ed before dummy layout was initialized" }
139
+ if (!maybeInitDummyLayoutWithHeader(reactContext)) {
140
+ // This theoretically might happen at Fabric + "bridgefull" combination, due to race condition where `reactContext.currentActivity`
141
+ // is still null at this execution point. We don't wanna crash in such case, thus returning zeroed height.
142
+ Log.e(
143
+ TAG,
144
+ "[RNScreens] Failed to late-init layout while computing header height. This is a race-condition-bug in react-native-screens, please file an issue at https://github.com/software-mansion/react-native-screens/issues"
145
+ )
146
+ return 0.0f
147
+ }
130
148
  }
131
149
 
132
150
  if (cache.hasKey(CacheKey(fontSize, isTitleEmpty))) {
@@ -168,10 +186,10 @@ internal class ScreenDummyLayoutHelper(
168
186
  return headerHeight
169
187
  }
170
188
 
171
- private fun requireReactContext(): ReactApplicationContext =
172
- requireNotNull(reactContextRef.get()) {
173
- "[RNScreens] Attempt to require missing react context"
174
- }
189
+ private fun requireReactContext(lazyMessage: (() -> Any)? = null): ReactApplicationContext =
190
+ requireNotNull(
191
+ reactContextRef.get(),
192
+ lazyMessage ?: { "[RNScreens] Attempt to require missing react context" })
175
193
 
176
194
  private fun requireActivity(): Activity =
177
195
  requireNotNull(requireReactContext().currentActivity) {
@@ -195,6 +213,19 @@ internal class ScreenDummyLayoutHelper(
195
213
  @JvmStatic
196
214
  fun getInstance(): ScreenDummyLayoutHelper? = weakInstance.get()
197
215
  }
216
+
217
+ private var isLayoutInitialized = false
218
+
219
+ override fun onHostResume() {
220
+ // This is the earliest we have guarantee that the context has a reference to an activity.
221
+ val reactContext = requireReactContext { "[RNScreens] ReactContext missing in onHostResume! This should not happen." }
222
+ check(maybeInitDummyLayoutWithHeader(reactContext)) { "[RNScreens] Failed to initialise dummy layout in onHostResume. This is not expected."}
223
+ reactContext.removeLifecycleEventListener(this)
224
+ }
225
+
226
+ override fun onHostPause() = Unit
227
+
228
+ override fun onHostDestroy() = Unit
198
229
  }
199
230
 
200
231
  private data class CacheKey(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-screens",
3
- "version": "3.33.0",
3
+ "version": "3.34.0",
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)",