Fix InteropUIBlockListener to support react-native-view-shot on Bridgeless (#43594)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/43594

I've been migrating `react-native-view-shot` to Fabric by using the `InteropUiBlockListener`
and I've realized that the interop layer doesn't work well.

1. FabricUIManager needs to implement `UIBlockViewResolver` in order for the interop layer to work correctly.
2. We need to hook `addUIBlock` to the `didDispatchMountItems` callback otherwise the UIBlocks won't be executed at all.

Changelog:
[Android] [Fixed] - Fix InteropUIBlockListener to support react-native-view-shot on Bridgeless

Reviewed By: javache

Differential Revision: D55187939

fbshipit-source-id: d048b4b5eed77fa856fdfac17c0df5f23fd44844
This commit is contained in:
Nicola Corti
2024-03-21 16:18:28 -07:00
committed by Facebook GitHub Bot
parent 9d79f05e68
commit 24a3dade29
4 changed files with 59 additions and 4 deletions
@@ -2541,7 +2541,7 @@ public class com/facebook/react/fabric/FabricSoLoader {
public static fun staticInit ()V
}
public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/bridge/LifecycleEventListener, com/facebook/react/bridge/UIManager {
public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/bridge/LifecycleEventListener, com/facebook/react/bridge/UIManager, com/facebook/react/fabric/interop/UIBlockViewResolver {
public static final field ENABLE_FABRIC_LOGS Z
public static final field ENABLE_FABRIC_PERF_LOGS Z
public static final field IS_DEVELOPMENT_ENVIRONMENT Z
@@ -57,6 +57,7 @@ import com.facebook.react.fabric.events.EventEmitterWrapper;
import com.facebook.react.fabric.events.FabricEventEmitter;
import com.facebook.react.fabric.internal.interop.InteropUIBlockListener;
import com.facebook.react.fabric.interop.UIBlock;
import com.facebook.react.fabric.interop.UIBlockViewResolver;
import com.facebook.react.fabric.mounting.MountItemDispatcher;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.fabric.mounting.SurfaceMountingManager;
@@ -99,7 +100,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
@SuppressLint("MissingNativeLoadLibrary")
@DoNotStripAny
public class FabricUIManager implements UIManager, LifecycleEventListener {
public class FabricUIManager implements UIManager, LifecycleEventListener, UIBlockViewResolver {
public static final String TAG = FabricUIManager.class.getSimpleName();
// The IS_DEVELOPMENT_ENVIRONMENT variable is used to log extra data when running fabric in a
@@ -39,6 +39,9 @@ internal class InteropUIBlockListener : UIManagerListener {
}
override fun willMountItems(uiManager: UIManager) {
if (beforeUIBlocks.isEmpty()) {
return
}
beforeUIBlocks.forEach {
if (uiManager is UIBlockViewResolver) {
it.execute(uiManager)
@@ -48,6 +51,9 @@ internal class InteropUIBlockListener : UIManagerListener {
}
override fun didMountItems(uiManager: UIManager) {
if (afterUIBlocks.isEmpty()) {
return
}
afterUIBlocks.forEach {
if (uiManager is UIBlockViewResolver) {
it.execute(uiManager)
@@ -56,9 +62,9 @@ internal class InteropUIBlockListener : UIManagerListener {
afterUIBlocks.clear()
}
override fun willDispatchViewUpdates(uiManager: UIManager) = Unit
override fun didDispatchMountItems(uiManager: UIManager) = didMountItems(uiManager)
override fun didDispatchMountItems(uiManager: UIManager) = Unit
override fun willDispatchViewUpdates(uiManager: UIManager) = willMountItems(uiManager)
override fun didScheduleMountItems(uiManager: UIManager) = Unit
}
@@ -45,6 +45,18 @@ class InteropUiBlockListenerTest {
assertEquals(1, underTest.afterUIBlocks.size)
}
@Test
fun willDispatchViewUpdates_emptiesBeforeUIBlocks() {
val underTest = InteropUIBlockListener()
underTest.prependUIBlock {}
underTest.addUIBlock {}
underTest.willDispatchViewUpdates(FakeUIManager())
assertEquals(0, underTest.beforeUIBlocks.size)
assertEquals(1, underTest.afterUIBlocks.size)
}
@Test
fun didMountItems_emptiesAfterUIBlocks() {
val underTest = InteropUIBlockListener()
@@ -57,6 +69,18 @@ class InteropUiBlockListenerTest {
assertEquals(0, underTest.afterUIBlocks.size)
}
@Test
fun didDispatchMountItems_emptiesAfterUIBlocks() {
val underTest = InteropUIBlockListener()
underTest.prependUIBlock {}
underTest.addUIBlock {}
underTest.didDispatchMountItems(FakeUIManager())
assertEquals(1, underTest.beforeUIBlocks.size)
assertEquals(0, underTest.afterUIBlocks.size)
}
@Test
fun willMountItems_deliversUiManagerCorrectly() {
val fakeUIManager = FakeUIManager()
@@ -69,6 +93,18 @@ class InteropUiBlockListenerTest {
assertEquals(1, fakeUIManager.resolvedViewCount)
}
@Test
fun willDispatchViewUpdates_deliversUiManagerCorrectly() {
val fakeUIManager = FakeUIManager()
val underTest = InteropUIBlockListener()
underTest.prependUIBlock { uiManager -> uiManager.resolveView(0) }
underTest.willDispatchViewUpdates(fakeUIManager)
assertEquals(1, fakeUIManager.resolvedViewCount)
}
@Test
fun didMountItems_deliversUiManagerCorrectly() {
val fakeUIManager = FakeUIManager()
@@ -80,4 +116,16 @@ class InteropUiBlockListenerTest {
assertEquals(1, fakeUIManager.resolvedViewCount)
}
@Test
fun didDispatchMountItems_deliversUiManagerCorrectly() {
val fakeUIManager = FakeUIManager()
val underTest = InteropUIBlockListener()
underTest.addUIBlock { uiManager -> uiManager.resolveView(0) }
underTest.didDispatchMountItems(fakeUIManager)
assertEquals(1, fakeUIManager.resolvedViewCount)
}
}