react-native-nami-sdk
Version:
React Native SDK for Nami - No-code paywall and onboarding flows with A/B testing.
101 lines (85 loc) • 3.22 kB
text/typescript
import {
TurboModuleRegistry,
NativeModules,
NativeEventEmitter,
} from 'react-native';
import type { Spec } from '../specs/NativeNamiFlowManager';
import type {
NamiFlowHandoffPayload,
NamiFlowHandoffStepHandler,
NamiHandoffComplete,
NamiHandoffOutcome,
} from './types';
const RNNamiFlowManager: Spec =
TurboModuleRegistry.getEnforcing?.<Spec>('RNNamiFlowManager') ??
NativeModules.RNNamiFlowManager;
const emitter = new NativeEventEmitter(NativeModules.RNNamiFlowManager);
export enum NamiFlowManagerEvents {
Handoff = 'Handoff',
FlowEvent = 'FlowEvent',
}
export const NamiFlowManager = {
emitter,
registerStepHandoff: (callback: NamiFlowHandoffStepHandler): (() => void) => {
console.info('[NamiFlowManager] Registering step handoff listener...');
const sub = emitter.addListener(
NamiFlowManagerEvents.Handoff,
(event: NamiFlowHandoffPayload) => {
console.info('[NamiFlowManager] Received handoff event:', event);
// Manufacture the per-handoff completion callback. The native event
// carries a `handoffId` that correlates this call back to the native
// pending handoff (TurboModule callbacks can't hold per-call closures).
let completed = false;
const complete: NamiHandoffComplete = (
outcome?: NamiHandoffOutcome,
) => {
if (completed) {
console.warn(
`[NamiFlowManager] complete() called more than once for handoff '${event.handoffTag}' — ignored`,
);
return;
}
completed = true;
// The ONE bridge-side defaulting point: bare complete() ≙ done.
// `outcome` stays required on the wire (native never sees a null).
const normalized: NamiHandoffOutcome = outcome ?? { kind: 'done' };
RNNamiFlowManager.completeHandoff?.(event.handoffId, normalized);
};
// Single handler shape `(tag, data, complete)`. Legacy `(tag, data)`
// handlers still run — the extra arg is ignored — and their `resume()`
// degrades to done-with-warning natively (the native pending token owns
// that, surviving Metro fast-refresh wiping this closure).
callback(event.handoffTag, event.handoffData, complete);
},
);
RNNamiFlowManager.registerStepHandoff?.();
return () => {
sub.remove();
RNNamiFlowManager.unregisterStepHandoff?.();
};
},
resume: (): void => {
console.info('[NamiFlowManager] resume from handoff requested');
RNNamiFlowManager.resume?.();
},
pause: (): void => {
console.info('[NamiFlowManager] pause from handoff requested');
RNNamiFlowManager.pause();
},
registerEventHandler: (
callback: (payload: Record<string, unknown>) => void,
): (() => void) => {
const sub = emitter.addListener(NamiFlowManagerEvents.FlowEvent, callback);
RNNamiFlowManager.registerEventHandler?.();
return () => {
sub.remove();
RNNamiFlowManager.unregisterEventHandler?.();
};
},
finish: (): void => {
RNNamiFlowManager.finish();
},
isFlowOpen: async (): Promise<boolean> => {
return RNNamiFlowManager.isFlowOpen();
},
};