Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,72 @@ test('animate marginLeft layout prop', () => {
);
});

// A layout animation on one view must not push the other views of the
// surface through a shadow tree commit: their non-layout props keep taking
// the direct path to the mounted views.
test('non-layout props stay on the direct path while another view animates layout', () => {
const movingRef = createRef<HostInstance>();

let _translateX;
let _translateXAnimation;
let _siblingHeight;
let _siblingHeightAnimation;

function MyApp() {
const translateX = useAnimatedValue(0);
const siblingHeight = useAnimatedValue(10);
_translateX = translateX;
_siblingHeight = siblingHeight;
return (
<View collapsable={false}>
<Animated.View
ref={movingRef}
style={{width: 100, height: 100, transform: [{translateX}]}}
/>
<Animated.View style={{width: 100, height: siblingHeight}} />
</View>
);
}

const root = Fantom.createRoot();

Fantom.runTask(() => {
root.render(<MyApp />);
});

Fantom.runTask(() => {
_translateXAnimation = Animated.timing(_translateX, {
toValue: 100,
duration: 200,
useNativeDriver: true,
}).start();
_siblingHeightAnimation = Animated.timing(_siblingHeight, {
toValue: 110,
duration: 200,
useNativeDriver: true,
}).start();
});

Fantom.unstable_produceFramesForDuration(100);

// The sibling's height went through a commit; the transform did not.
expect(root.getRenderedOutput({props: ['height']}).toJSX()).toEqual(
<rn-view>
<rn-view key={0} height="100" />
<rn-view key={1} height="60" />
</rn-view>,
);
expect(
Fantom.unstable_getDirectManipulationProps(nullthrows(movingRef.current))
.transform,
).toEqual([{translateX: 50}]);

Fantom.runTask(() => {
_translateXAnimation?.stop();
_siblingHeightAnimation?.stop();
});
});

test('animated opacity', () => {
let _opacity;
let _opacityAnimation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.fabric.mounting.SurfaceMountingManager;
import com.facebook.react.fabric.mounting.mountitems.BatchMountItem;
import com.facebook.react.fabric.mounting.mountitems.BatchedAnimatedPropsMountItem;
import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem;
import com.facebook.react.fabric.mounting.mountitems.MountItem;
import com.facebook.react.fabric.mounting.mountitems.MountItemFactory;
Expand Down Expand Up @@ -837,6 +838,27 @@ public void synchronouslyUpdateViewOnUIThread(final int reactTag, final Readable
ReactMarkerConstants.FABRIC_UPDATE_UI_MAIN_THREAD_END, null, commitNumber);
}

/** Called from C++ via JNI with a batch encoded by AnimatedPropBufferEncoder.cpp. */
@SuppressLint("NotInvokedPrivateMethod")
@SuppressWarnings("unused")
@UiThread
@ThreadConfined(UI)
private void synchronouslyUpdateViewBatch(
int[] intBuffer, double[] doubleBuffer, Object[] rawPropsBuffer) {
UiThreadUtil.assertOnUiThread();

int commitNumber = mCurrentSynchronousCommitNumber++;
ReactMarker.logFabricMarker(
ReactMarkerConstants.FABRIC_UPDATE_UI_MAIN_THREAD_START, null, commitNumber);

new BatchedAnimatedPropsMountItem(
intBuffer, doubleBuffer, rawPropsBuffer, mMountItemDispatcher)
.execute(mMountingManager);

ReactMarker.logFabricMarker(
ReactMarkerConstants.FABRIC_UPDATE_UI_MAIN_THREAD_END, null, commitNumber);
}

/** Called from C++ via JNI. */
@SuppressLint("NotInvokedPrivateMethod")
@SuppressWarnings("unused")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,16 @@ internal class MountingManager(
getSurfaceManagerForView(reactTag)?.storeSynchronousMountPropsOverride(reactTag, props)
}

/** Returns false when no surface owns [reactTag] yet, so the caller can queue the update. */
@UiThread
fun updateAnimatedPropsSynchronously(reactTag: Int, props: ReadableMap): Boolean {
assertOnUiThread()
val surfaceMountingManager = getSurfaceManagerForView(reactTag) ?: return false
surfaceMountingManager.storeSynchronousMountPropsOverride(reactTag, props)
surfaceMountingManager.updatePropsSynchronously(reactTag, props)
return true
}

@UiThread
fun updatePropsSynchronously(reactTag: Int, props: ReadableMap?) {
assertOnUiThread()
Expand Down
Loading
Loading