'use strict'; import type { SerializableRef, WorkletFunction } from 'react-native-worklets'; import { getUIRuntimeHolder, getUISchedulerHolder, } from 'react-native-worklets'; import { IS_JEST } from '../common'; import type { InternalHostInstance, LayoutAnimationBatchItem, SettledUpdate, ShadowNodeWrapper, StyleProps, Value3D, ValueRotation, } from '../commonTypes'; import type { CSSAnimationUpdates, CSSEventHandler, CSSPseudoStyleConfig, CSSTransitionConfig, NormalizedCSSAnimationKeyframesConfig, } from '../css/native'; import { getShadowNodeWrapperFromRef } from '../fabricUtils'; import { checkCppVersion } from '../platform-specific/checkCppVersion'; import { jsVersion } from '../platform-specific/jsVersion'; import { assertWorkletsVersion } from '../platform-specific/workletsVersion'; import { ReanimatedTurboModule } from '../specs'; import type { IReanimatedModule, ReanimatedModuleProxy, } from './reanimatedModuleProxy'; export function createNativeReanimatedModule(): IReanimatedModule { return new NativeReanimatedModule(); } function assertSingleReanimatedInstance() { if ( global._REANIMATED_VERSION_JS !== undefined && global._REANIMATED_VERSION_JS !== jsVersion ) { throw new Error( `[Reanimated] Another instance of Reanimated was detected. See \`https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#another-instance-of-reanimated-was-detected\` for more details. Previous: ${global._REANIMATED_VERSION_JS}, current: ${jsVersion}.` ); } } class NativeReanimatedModule implements IReanimatedModule { /** * We keep the instance of `WorkletsModule` here to keep correct coupling of * the modules and initialization order. */ #reanimatedModuleProxy: ReanimatedModuleProxy; constructor() { // These checks have to split since version checking depend on the execution order if (__DEV__) { assertSingleReanimatedInstance(); assertWorkletsVersion(); } global._REANIMATED_VERSION_JS = jsVersion; if (ReanimatedTurboModule && !installTurboModule()) { throw new Error( '[Reanimated] Failed to install the native module because React Native has no active surface on its discarded instance. This happens when the app is reloaded while a previous reload is still in progress, for example during an OTA update immediately upon launch. No action is required; the app will continue to function as usual.' ); } if (global.__reanimatedModuleProxy === undefined) { throw new Error( `[Reanimated] Native part of Reanimated doesn't seem to be initialized. See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#native-part-of-reanimated-doesnt-seem-to-be-initialized for more details.` ); } if (__DEV__ && !globalThis.RN$Bridgeless && !IS_JEST) { throw new Error( '[Reanimated] Reanimated 4 supports only the React Native New Architecture and web.' ); } if (__DEV__) { checkCppVersion(); } this.#reanimatedModuleProxy = global.__reanimatedModuleProxy; } registerSensor( sensorType: number, interval: number, iosReferenceFrame: number, handler: SerializableRef<(data: Value3D | ValueRotation) => void> ) { return this.#reanimatedModuleProxy.registerSensor( sensorType, interval, iosReferenceFrame, handler ); } unregisterSensor(sensorId: number) { return this.#reanimatedModuleProxy.unregisterSensor(sensorId); } registerEventHandler( eventHandler: SerializableRef, eventName: string, emitterReactTag: number ) { return this.#reanimatedModuleProxy.registerEventHandler( eventHandler, eventName, emitterReactTag ); } unregisterEventHandler(id: number) { return this.#reanimatedModuleProxy.unregisterEventHandler(id); } getViewProp( viewTag: number, propName: string, component: InternalHostInstance, callback?: (result: T) => void ) { const shadowNodeWrapper = getShadowNodeWrapperFromRef(component); return this.#reanimatedModuleProxy.getViewProp( shadowNodeWrapper, propName, callback ); } configureLayoutAnimationBatch( layoutAnimationsBatch: LayoutAnimationBatchItem[] ) { this.#reanimatedModuleProxy.configureLayoutAnimationBatch( layoutAnimationsBatch ); } setShouldAnimateExitingForTag(viewTag: number, shouldAnimate: boolean) { this.#reanimatedModuleProxy.setShouldAnimateExitingForTag( viewTag, shouldAnimate ); } getStaticFeatureFlag(name: string): boolean { return this.#reanimatedModuleProxy.getStaticFeatureFlag(name); } setDynamicFeatureFlag(name: string, value: boolean) { this.#reanimatedModuleProxy.setDynamicFeatureFlag(name, value); } subscribeForKeyboardEvents( handler: SerializableRef, isStatusBarTranslucent: boolean, isNavigationBarTranslucent: boolean ) { return this.#reanimatedModuleProxy.subscribeForKeyboardEvents( handler, isStatusBarTranslucent, isNavigationBarTranslucent ); } unsubscribeFromKeyboardEvents(listenerId: number) { this.#reanimatedModuleProxy.unsubscribeFromKeyboardEvents(listenerId); } setViewStyle(viewTag: number, style: StyleProps) { this.#reanimatedModuleProxy.setViewStyle(viewTag, style); } setCSSEventHandler(handler: CSSEventHandler) { this.#reanimatedModuleProxy.setCSSEventHandler(handler); } markNodeAsRemovable(shadowNodeWrapper: ShadowNodeWrapper) { this.#reanimatedModuleProxy.markNodeAsRemovable(shadowNodeWrapper); } unmarkNodeAsRemovable(viewTag: number) { this.#reanimatedModuleProxy.unmarkNodeAsRemovable(viewTag); } registerCSSKeyframes( animationName: string, compoundComponentName: string, keyframesConfig: NormalizedCSSAnimationKeyframesConfig ) { this.#reanimatedModuleProxy.registerCSSKeyframes( animationName, compoundComponentName, keyframesConfig ); } unregisterCSSKeyframes(animationName: string, compoundComponentName: string) { this.#reanimatedModuleProxy.unregisterCSSKeyframes( animationName, compoundComponentName ); } applyCSSAnimations( shadowNodeWrapper: ShadowNodeWrapper, compoundComponentName: string, animationUpdates: CSSAnimationUpdates ) { this.#reanimatedModuleProxy.applyCSSAnimations( shadowNodeWrapper, compoundComponentName, animationUpdates ); } unregisterCSSAnimations(viewTag: number) { this.#reanimatedModuleProxy.unregisterCSSAnimations(viewTag); } runCSSTransition( shadowNodeWrapper: ShadowNodeWrapper, transitionConfig: CSSTransitionConfig, eventMask: number ): void { this.#reanimatedModuleProxy.runCSSTransition( shadowNodeWrapper, transitionConfig, eventMask ); } unregisterCSSTransition(viewTag: number) { this.#reanimatedModuleProxy.unregisterCSSTransition(viewTag); } getSettledUpdates(): SettledUpdate[] { return this.#reanimatedModuleProxy.getSettledUpdates(); } registerPseudoStyles( shadowNodeWrapper: ShadowNodeWrapper, config: CSSPseudoStyleConfig ) { this.#reanimatedModuleProxy.registerPseudoStyles(shadowNodeWrapper, config); } unregisterPseudoStyles(viewTag: number) { this.#reanimatedModuleProxy.unregisterPseudoStyles(viewTag); } } function installTurboModule() { if (globalThis.__reanimatedModuleProxy) { return true; } globalThis.__UI_WORKLET_RUNTIME_HOLDER = getUIRuntimeHolder(); globalThis.__UI_SCHEDULER_HOLDER = getUISchedulerHolder(); const status = ReanimatedTurboModule!.installTurboModule(); delete globalThis.__UI_WORKLET_RUNTIME_HOLDER; delete globalThis.__UI_SCHEDULER_HOLDER; return status; }