import React, { memo, useEffect, useMemo } from 'react'; import { requireNativeComponent, ViewProps } from 'react-native'; import { AdaptyOnboarding, WebPresentation } from '@/types'; import { coderFactory } from '@/coders/factory'; import { generateId, shouldEnableMock } from '@/utils'; import { OnboardingEventHandlers, NativeAdaptyOnboardingViewProps, } from './types'; import { createOnboardingEventHandlers } from './create-onboarding-event-handlers'; import { DEFAULT_ONBOARDING_PARAMS } from './onboarding-view-controller'; import { AdaptyOnboardingViewMock } from './AdaptyOnboardingView.mock'; import { filterUndefined } from '@adapty/core'; /** * Props for the {@link AdaptyOnboardingView} component. * @public */ export type AdaptyOnboardingViewProps = ViewProps & { onboarding: AdaptyOnboarding; externalUrlsPresentation?: WebPresentation; onClose?: OnboardingEventHandlers['onClose']; onCustom?: OnboardingEventHandlers['onCustom']; onPaywall?: OnboardingEventHandlers['onPaywall']; onStateUpdated?: OnboardingEventHandlers['onStateUpdated']; onFinishedLoading?: OnboardingEventHandlers['onFinishedLoading']; onAnalytics?: OnboardingEventHandlers['onAnalytics']; onError?: OnboardingEventHandlers['onError']; /** * @deprecated Use individual event handler props instead (onClose, onCustom, onPaywall, etc.) * This prop is kept for backward compatibility and will be removed in a future version. */ eventHandlers?: Partial; }; const NativeAdaptyOnboardingView = shouldEnableMock() ? AdaptyOnboardingViewMock : requireNativeComponent( 'AdaptyOnboardingView', ); const AdaptyOnboardingViewComponent: React.FC = ({ onboarding, externalUrlsPresentation = DEFAULT_ONBOARDING_PARAMS.externalUrlsPresentation, eventHandlers, onClose, onCustom, onPaywall, onStateUpdated, onFinishedLoading, onAnalytics, onError, ...rest }) => { const uniqueViewId = useMemo( () => `${onboarding.id}_${generateId()}`, [onboarding.id], ); const onboardingJson = useMemo(() => { const encodedOnboarding = coderFactory .createOnboardingCoder() .encode(onboarding); const encodedParams = coderFactory .createUiCreateOnboardingViewParamsCoder() .encode({ externalUrlsPresentation, }); return JSON.stringify({ onboarding: encodedOnboarding, ...encodedParams, }); }, [onboarding, externalUrlsPresentation]); const combinedEventHandlers = useMemo((): Partial => { // Merge legacy eventHandlers with individual props (individual props take priority) return { ...eventHandlers, ...filterUndefined({ onClose, onCustom, onPaywall, onStateUpdated, onFinishedLoading, onAnalytics, onError, }), }; }, [ onClose, onCustom, onPaywall, onStateUpdated, onFinishedLoading, onAnalytics, onError, eventHandlers, ]); useEffect(() => { const unsubscribe = createOnboardingEventHandlers( combinedEventHandlers, uniqueViewId, ); return unsubscribe; }, [uniqueViewId, combinedEventHandlers]); return ( ); }; /** * React component that renders a native onboarding view. * * @remarks * Accepts an onboarding object and optional event handler props. * Under the hood, it creates a native view and subscribes to onboarding events. * * @see {@link AdaptyOnboardingViewProps} for available props * @public */ export const AdaptyOnboardingView = memo(AdaptyOnboardingViewComponent);