Skip to content

Commit

Permalink
Fix InteropUIBlockListener to support react-native-view-shot on Bridg…
Browse files Browse the repository at this point in the history
…eless (#43594)

Summary:
Pull Request resolved: #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
  • Loading branch information
cortinico authored and facebook-github-bot committed Mar 21, 2024
1 parent 9d79f05 commit 24a3dad
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 4 deletions.
2 changes: 1 addition & 1 deletion packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
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;
Expand Down Expand Up @@ -99,7 +100,7 @@
*/
@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand All @@ -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()
Expand All @@ -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)
}
}

0 comments on commit 24a3dad

Please sign in to comment.