@namiml/expo-sdk 3.4.1-dev.202605272004 → 3.4.1-dev.202605300006

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  var sdkCore = require('@namiml/sdk-core');
4
4
  var reactNative = require('react-native');
5
5
  var require$$0 = require('react');
6
+ var reactNativeSafeAreaContext = require('react-native-safe-area-context');
6
7
 
7
8
  class ExpoStorageAdapter {
8
9
  constructor() {
@@ -2941,6 +2942,17 @@ function renderResolvedComponent(componentType, resolvedComponent, scaleFactor,
2941
2942
  function splitContainerStyles(style) {
2942
2943
  const { paddingLeft, paddingRight, paddingTop, paddingBottom, flexDirection, alignItems, justifyContent, ...rest } = style;
2943
2944
  const outer = { ...rest };
2945
+ // The content container is always the page's vertical fill region: styles.outer
2946
+ // declares flex:1 and the body scrolls internally. A payload height of
2947
+ // 'fitContent' makes sizeStyles emit flexGrow:0, which would otherwise override
2948
+ // that fill and collapse the body to 0 height — the flex:1 ScrollView child
2949
+ // can't give an unbounded parent a height. Drop the flex-sizing keys so the
2950
+ // fill stays authoritative, matching the native renderers which always
2951
+ // fill-and-scroll this region regardless of the authored height.
2952
+ delete outer.flex;
2953
+ delete outer.flexGrow;
2954
+ delete outer.flexBasis;
2955
+ delete outer.flexShrink;
2944
2956
  const inner = {
2945
2957
  ...(paddingLeft != null ? { paddingLeft } : {}),
2946
2958
  ...(paddingRight != null ? { paddingRight } : {}),
@@ -3142,7 +3154,19 @@ catch {
3142
3154
  const fontGateCache = new Map();
3143
3155
  const PaywallScreen = ({ paywall, onClose, onCommitted, holdInteractionUntilFocus = false, isActive = true, }) => {
3144
3156
  const ctx = usePaywallContext();
3157
+ const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
3145
3158
  const focusReadyCtx = useFirstFocusReadyContext();
3159
+ // Push the measured top safe-area inset into paywall state so templates can
3160
+ // position chrome via ${state.safeAreaTop} (e.g. header topPadding,
3161
+ // background topMargin). Mirrors the native Android renderer, which measures
3162
+ // the display cutout and calls setSafeAreaTop. The full-bleed root lets the
3163
+ // background extend under the status bar; this offsets the content back into
3164
+ // the safe area. No safeAreaBottom equivalent exists in core/Android state.
3165
+ // Depend only on insets.top (stable per device/orientation) — depending on
3166
+ // ctx would re-run every render and loop through setState.
3167
+ require$$0.useEffect(() => {
3168
+ ctx.setSafeAreaTop(insets.top);
3169
+ }, [insets.top]);
3146
3170
  const scaleFactor = sdkCore.getDeviceScaleFactor(ctx.state.formFactor);
3147
3171
  const userInteractionEnabled = ctx.state.userInteractionEnabled !== false;
3148
3172
  const currentPageName = ctx.state.selectedPaywall === paywall
@@ -3282,6 +3306,7 @@ const FocusContext = require$$0.createContext(true);
3282
3306
  require$$0.createContext(null);
3283
3307
  const FocusProvider = ({ children, enabled }) => (jsxRuntimeExports.jsx(FocusContext.Provider, { value: enabled, children: children }));
3284
3308
 
3309
+ const { NamiFlowManager } = sdkCore._internal;
3285
3310
  const DEFAULT_CONTEXT = {
3286
3311
  productGroups: [],
3287
3312
  customAttributes: {},
@@ -3329,8 +3354,13 @@ const NamiView = ({ placement, url, onClose, onSignIn, onDeepLink, onPurchase, o
3329
3354
  return launchRequest;
3330
3355
  if (url)
3331
3356
  return { type: 'url', value: url };
3332
- if (placement)
3333
- return { type: 'label', value: placement };
3357
+ if (placement) {
3358
+ // A URL/deeplink campaign carries its URL in the value. If one is handed
3359
+ // in via the placement prop, classify it as a url launch so core takes
3360
+ // the URL-match branch — otherwise the label lookup misses, no paywall
3361
+ // resolves, and NamiView spins on the placeholder forever.
3362
+ return { type: sdkCore.isValidUrl(placement) ? 'url' : 'label', value: placement };
3363
+ }
3334
3364
  return { type: undefined, value: '' };
3335
3365
  }, [launchRequest, url, placement]);
3336
3366
  const ctx = require$$0.useMemo(() => ({
@@ -3365,8 +3395,14 @@ const NamiView = ({ placement, url, onClose, onSignIn, onDeepLink, onPurchase, o
3365
3395
  const activeFlow = flowRef.current;
3366
3396
  if (!activeFlow)
3367
3397
  return;
3368
- if (sdkCore.NamiFlowManager.instance.currentFlow === activeFlow || sdkCore.NamiFlowManager.instance.flowOpen) {
3369
- sdkCore.NamiFlowManager.finish();
3398
+ // Cast comparison sides to `unknown` first — `currentFlow` is typed via
3399
+ // the `_internal` namespace's NamiFlow class declaration (NAM-1217),
3400
+ // `activeFlow` (via flowRef) is typed via the public-barrel's NamiFlow.
3401
+ // Identical runtime class, but bundled .d.ts emits each separately and
3402
+ // TS treats them as nominally distinct. See the cast in the
3403
+ // presentFlow block below.
3404
+ if (NamiFlowManager.instance.currentFlow === activeFlow || NamiFlowManager.instance.flowOpen) {
3405
+ NamiFlowManager.finish();
3370
3406
  }
3371
3407
  flowRef.current = undefined;
3372
3408
  lastAppearStepIdRef.current = '';
@@ -3397,7 +3433,7 @@ const NamiView = ({ placement, url, onClose, onSignIn, onDeepLink, onPurchase, o
3397
3433
  && attempt.flow === activeFlow
3398
3434
  && activePaywallId === attempt.paywallId
3399
3435
  && (activeFlow?.currentFlowStep?.type === 'exit'
3400
- || sdkCore.NamiFlowManager.instance.flowOpen === false);
3436
+ || NamiFlowManager.instance.flowOpen === false);
3401
3437
  const shouldFallbackClose = !isClosing
3402
3438
  && !!attempt
3403
3439
  && ((attempt.flow === activeFlow
@@ -3547,15 +3583,15 @@ const NamiView = ({ placement, url, onClose, onSignIn, onDeepLink, onPurchase, o
3547
3583
  }, [finishActiveFlow]);
3548
3584
  // ─── Flow handlers ───────────────────────────────────────────────────
3549
3585
  require$$0.useEffect(() => {
3550
- sdkCore.NamiFlowManager.registerStepHandoff((handoffTag, handoffData) => {
3586
+ NamiFlowManager.registerStepHandoff((handoffTag, handoffData) => {
3551
3587
  onHandoffRef.current?.(handoffTag, handoffData);
3552
3588
  });
3553
- sdkCore.NamiFlowManager.registerEventHandler((eventData) => {
3589
+ NamiFlowManager.registerEventHandler((eventData) => {
3554
3590
  onFlowEventRef.current?.(eventData);
3555
3591
  });
3556
3592
  return () => {
3557
- sdkCore.NamiFlowManager.registerStepHandoff(undefined);
3558
- sdkCore.NamiFlowManager.registerEventHandler(() => { });
3593
+ NamiFlowManager.registerStepHandoff(undefined);
3594
+ NamiFlowManager.registerEventHandler(undefined);
3559
3595
  };
3560
3596
  }, []);
3561
3597
  // ─── ExpoUIAdapter listener subscriptions ────────────────────────────
@@ -3676,11 +3712,16 @@ const NamiView = ({ placement, url, onClose, onSignIn, onDeepLink, onPurchase, o
3676
3712
  return;
3677
3713
  if (flowRef.current)
3678
3714
  return;
3679
- const flow = sdkCore.NamiFlowManager.instance.presentFlow(campaignData, {
3715
+ const flow = NamiFlowManager.instance.presentFlow(campaignData, {
3680
3716
  type: resolvedLaunch.type,
3681
3717
  value: resolvedLaunch.value,
3682
3718
  context: ctx,
3683
3719
  }, ctx);
3720
+ // `presentFlow` returns the class from the `_internal` namespace
3721
+ // (NAM-1217); flowRef.current is typed via the public-barrel re-export
3722
+ // of the same class. Bundled .d.ts files emit each declaration in
3723
+ // isolation, so TypeScript sees them as nominally distinct classes
3724
+ // even though they're the same runtime constructor. Cast to align.
3684
3725
  flowRef.current = flow;
3685
3726
  }, [campaignData, resolvedLaunch.type, resolvedLaunch.value, ctx, reloadKey]);
3686
3727
  require$$0.useEffect(() => {
@@ -3731,12 +3772,19 @@ const NamiView = ({ placement, url, onClose, onSignIn, onDeepLink, onPurchase, o
3731
3772
  setInitialScreenCommitted(true);
3732
3773
  }, []);
3733
3774
  if (isClosing) {
3734
- return jsxRuntimeExports.jsx(reactNative.SafeAreaView, { style: styles.root });
3775
+ return jsxRuntimeExports.jsx(reactNative.View, { style: styles.root });
3735
3776
  }
3736
3777
  if (loading || !campaignData || (!isFlowCampaign && !paywallData) || (isFlowCampaign && !hasFlowScreen)) {
3737
3778
  return jsxRuntimeExports.jsx(LaunchPlaceholder, { paywall: launchPlaceholderPaywall });
3738
3779
  }
3739
- return (jsxRuntimeExports.jsxs(reactNative.SafeAreaView, { style: styles.root, children: [jsxRuntimeExports.jsx(reactNative.StatusBar, { barStyle: "light-content" }), isFlowCampaign ? (jsxRuntimeExports.jsx(FlowRenderer, { paywalls: flowState.paywalls, currentIndex: flowState.index, animation: flowState.animation ?? undefined, onSettled: handleFlowAnimationSettled, onTransitionVisible: () => setPendingTransitionPaywall(null), onInitialCommitted: handleInitialCommitted, onClose: onClose, campaign: campaignData, context: ctx, flow: flowRef.current })) : (jsxRuntimeExports.jsx(PaywallProvider, { paywall: paywallData, context: ctx, campaign: campaignData, children: jsxRuntimeExports.jsx(PaywallScreen, { paywall: paywallData, onClose: requestClose, onCommitted: handleInitialCommitted, isActive: true }) })), !isClosing && showLaunchPlaceholder && jsxRuntimeExports.jsx(LaunchPlaceholder, { paywall: launchPlaceholderPaywall, overlay: true }), showTransitionPlaceholder && jsxRuntimeExports.jsx(LaunchPlaceholder, { paywall: pendingTransitionPaywall, overlay: true })] }));
3780
+ return (
3781
+ // Full-bleed root so the paywall background extends under the status bar and
3782
+ // home indicator (edge-to-edge). Safe-area insets are applied only to the
3783
+ // chrome (header/footer) via useSafeAreaInsets, not the whole tree — the
3784
+ // previous core SafeAreaView inset every edge and let the #000 root show
3785
+ // through as letterbox bands. SafeAreaProvider guarantees an inset context
3786
+ // even if the host app didn't mount one (nested providers are supported).
3787
+ jsxRuntimeExports.jsxs(reactNativeSafeAreaContext.SafeAreaProvider, { initialMetrics: reactNativeSafeAreaContext.initialWindowMetrics, style: styles.root, children: [jsxRuntimeExports.jsx(reactNative.StatusBar, { barStyle: "light-content", translucent: true, backgroundColor: "transparent" }), isFlowCampaign ? (jsxRuntimeExports.jsx(FlowRenderer, { paywalls: flowState.paywalls, currentIndex: flowState.index, animation: flowState.animation ?? undefined, onSettled: handleFlowAnimationSettled, onTransitionVisible: () => setPendingTransitionPaywall(null), onInitialCommitted: handleInitialCommitted, onClose: onClose, campaign: campaignData, context: ctx, flow: flowRef.current })) : (jsxRuntimeExports.jsx(PaywallProvider, { paywall: paywallData, context: ctx, campaign: campaignData, children: jsxRuntimeExports.jsx(PaywallScreen, { paywall: paywallData, onClose: requestClose, onCommitted: handleInitialCommitted, isActive: true }) })), !isClosing && showLaunchPlaceholder && jsxRuntimeExports.jsx(LaunchPlaceholder, { paywall: launchPlaceholderPaywall, overlay: true }), showTransitionPlaceholder && jsxRuntimeExports.jsx(LaunchPlaceholder, { paywall: pendingTransitionPaywall, overlay: true })] }));
3740
3788
  };
3741
3789
  const LaunchPlaceholder = ({ paywall, overlay = false }) => {
3742
3790
  const initialPageName = paywall?.template?.initialState?.currentPage ?? 'page1';
@@ -3995,9 +4043,9 @@ Object.defineProperty(exports, "NamiPaywallManager", {
3995
4043
  enumerable: true,
3996
4044
  get: function () { return sdkCore.NamiPaywallManager; }
3997
4045
  });
3998
- Object.defineProperty(exports, "NamiProfileManager", {
4046
+ Object.defineProperty(exports, "NamiPurchaseManager", {
3999
4047
  enumerable: true,
4000
- get: function () { return sdkCore.NamiProfileManager; }
4048
+ get: function () { return sdkCore.NamiPurchaseManager; }
4001
4049
  });
4002
4050
  exports.ExpoDeviceAdapter = ExpoDeviceAdapter;
4003
4051
  exports.ExpoPurchaseAdapter = ExpoPurchaseAdapter;