@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 +62 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +56 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
- package/src/components/NamiView.tsx +39 -10
- package/src/components/PaywallScreen.tsx +14 -0
- package/src/components/containers/NamiContentContainer.tsx +11 -0
- package/src/components/elements/NamiProgressIndicator.tsx +3 -1
- package/src/index.ts +5 -1
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
|
-
|
|
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
|
-
|
|
3369
|
-
|
|
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
|
-
||
|
|
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
|
-
|
|
3586
|
+
NamiFlowManager.registerStepHandoff((handoffTag, handoffData) => {
|
|
3551
3587
|
onHandoffRef.current?.(handoffTag, handoffData);
|
|
3552
3588
|
});
|
|
3553
|
-
|
|
3589
|
+
NamiFlowManager.registerEventHandler((eventData) => {
|
|
3554
3590
|
onFlowEventRef.current?.(eventData);
|
|
3555
3591
|
});
|
|
3556
3592
|
return () => {
|
|
3557
|
-
|
|
3558
|
-
|
|
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 =
|
|
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.
|
|
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 (
|
|
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, "
|
|
4046
|
+
Object.defineProperty(exports, "NamiPurchaseManager", {
|
|
3999
4047
|
enumerable: true,
|
|
4000
|
-
get: function () { return sdkCore.
|
|
4048
|
+
get: function () { return sdkCore.NamiPurchaseManager; }
|
|
4001
4049
|
});
|
|
4002
4050
|
exports.ExpoDeviceAdapter = ExpoDeviceAdapter;
|
|
4003
4051
|
exports.ExpoPurchaseAdapter = ExpoPurchaseAdapter;
|