import type { IUIAdapter, PaywallHandle, FlowNavigationOptions } from '@namiml/sdk-core'; import type { IPaywall } from '@namiml/sdk-core'; import type { NamiPaywallLaunchContext } from '@namiml/sdk-core'; type PaywallListener = (paywall: IPaywall, context: NamiPaywallLaunchContext) => void; type ReRenderListener = () => void; type FlowNavListener = (paywall: IPaywall, options: FlowNavigationOptions) => void; /** * Expo UI adapter. * Uses a listener pattern so the Expo PaywallView component can register * itself to receive create/reRender/flowNav events from the core SDK. */ export class ExpoUIAdapter implements IUIAdapter { private paywallListener: PaywallListener | null = null; private reRenderListener: ReRenderListener | null = null; private flowNavListener: FlowNavListener | null = null; onPaywallRequested(listener: PaywallListener): () => void { this.paywallListener = listener; return () => { this.paywallListener = null; }; } onReRenderRequested(listener: ReRenderListener): () => void { this.reRenderListener = listener; return () => { this.reRenderListener = null; }; } onFlowNavigationRequested(listener: FlowNavListener): () => void { this.flowNavListener = listener; return () => { this.flowNavListener = null; }; } createPaywall( type: string | undefined, value: string, context: NamiPaywallLaunchContext ): PaywallHandle { // In Expo, we don't create a DOM element. Instead, we notify the // registered PaywallView component to present the paywall. // The PaywallView resolves the paywall data using the same core logic. if (this.paywallListener) { // Build minimal paywall info for navigation const paywallInfo = { type, value, context }; this.paywallListener(paywallInfo as any, context); } return { type, value, context }; } reRenderPaywall(): void { this.reRenderListener?.(); } flowNavigateToScreen(paywall: IPaywall, options: FlowNavigationOptions): void { this.flowNavListener?.(paywall, options); } /** * Drop all registered listener refs. Called from `Nami.reset()` via the * IUIAdapter contract so the singleton adapter doesn't hold stale * closures from a pre-reset NamiView/PaywallView mount. If a NamiView * is still mounted when reset runs, it will re-register its listeners * on the next effect cycle. */ onReset(): void { this.paywallListener = null; this.reRenderListener = null; this.flowNavListener = null; } }