@virex-tech/paywallo-sdk 2.2.6 → 2.2.7

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.js CHANGED
@@ -2386,8 +2386,8 @@ var init_NotificationHandlerSetup = __esm({
2386
2386
  // src/core/version.ts
2387
2387
  function resolveVersion() {
2388
2388
  try {
2389
- if ("2.2.6") {
2390
- return "2.2.6";
2389
+ if ("2.2.7") {
2390
+ return "2.2.7";
2391
2391
  }
2392
2392
  } catch {
2393
2393
  }
@@ -3057,6 +3057,41 @@ var init_emitPaywallClosed = __esm({
3057
3057
  }
3058
3058
  });
3059
3059
 
3060
+ // src/domains/paywall/extractFirstPageBackground.ts
3061
+ function extractFirstPageBackground(craftData) {
3062
+ if (!craftData) return DEFAULT_BACKGROUND;
3063
+ try {
3064
+ const data = JSON.parse(craftData);
3065
+ const nodes = { ...data.nodes, ...data.sharedNodes };
3066
+ const rootId = data.rootId;
3067
+ if (!rootId || !nodes[rootId]) return DEFAULT_BACKGROUND;
3068
+ const firstPageId = nodes[rootId].children?.[0];
3069
+ if (!firstPageId) return DEFAULT_BACKGROUND;
3070
+ const bg = nodes[firstPageId]?.props?.backgroundColor;
3071
+ return typeof bg === "string" && bg.length > 0 && bg !== "transparent" ? bg : DEFAULT_BACKGROUND;
3072
+ } catch {
3073
+ return DEFAULT_BACKGROUND;
3074
+ }
3075
+ }
3076
+ function isColorDark(color) {
3077
+ const hex = color.replace("#", "").trim();
3078
+ const normalized = hex.length === 3 ? hex.split("").map((c) => c + c).join("") : hex;
3079
+ if (normalized.length !== 6) return false;
3080
+ const r = parseInt(normalized.slice(0, 2), 16);
3081
+ const g = parseInt(normalized.slice(2, 4), 16);
3082
+ const b = parseInt(normalized.slice(4, 6), 16);
3083
+ if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return false;
3084
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
3085
+ return luminance < 0.5;
3086
+ }
3087
+ var DEFAULT_BACKGROUND;
3088
+ var init_extractFirstPageBackground = __esm({
3089
+ "src/domains/paywall/extractFirstPageBackground.ts"() {
3090
+ "use strict";
3091
+ DEFAULT_BACKGROUND = "#FFFFFF";
3092
+ }
3093
+ });
3094
+
3060
3095
  // src/domains/paywall/parseWebViewMessage.ts
3061
3096
  function deriveMessageId(message) {
3062
3097
  if (message.id) return message.id;
@@ -3299,6 +3334,66 @@ var init_localization = __esm({
3299
3334
  }
3300
3335
  });
3301
3336
 
3337
+ // src/domains/paywall/PaywallSkeleton.tsx
3338
+ function SkeletonBlock({ style, color, opacity }) {
3339
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native14.Animated.View, { style: [style, { backgroundColor: color, opacity }] });
3340
+ }
3341
+ function PaywallSkeleton({ backgroundColor = "#FFFFFF" }) {
3342
+ const pulse = (0, import_react3.useRef)(new import_react_native14.Animated.Value(0.4)).current;
3343
+ (0, import_react3.useEffect)(() => {
3344
+ const loop = import_react_native14.Animated.loop(
3345
+ import_react_native14.Animated.sequence([
3346
+ import_react_native14.Animated.timing(pulse, { toValue: 1, duration: 700, useNativeDriver: true }),
3347
+ import_react_native14.Animated.timing(pulse, { toValue: 0.4, duration: 700, useNativeDriver: true })
3348
+ ])
3349
+ );
3350
+ loop.start();
3351
+ return () => loop.stop();
3352
+ }, [pulse]);
3353
+ const placeholder = isColorDark(backgroundColor) ? "rgba(255,255,255,0.13)" : "rgba(0,0,0,0.07)";
3354
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native14.View, { style: [styles.container, { backgroundColor }], pointerEvents: "none", children: [
3355
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native14.View, { style: styles.header, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkeletonBlock, { style: styles.logo, color: placeholder, opacity: pulse }) }),
3356
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkeletonBlock, { style: styles.hero, color: placeholder, opacity: pulse }),
3357
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native14.View, { style: styles.titleGroup, children: [
3358
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkeletonBlock, { style: styles.titleLine, color: placeholder, opacity: pulse }),
3359
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkeletonBlock, { style: styles.titleLineShort, color: placeholder, opacity: pulse })
3360
+ ] }),
3361
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native14.View, { style: styles.cardsRow, children: [
3362
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkeletonBlock, { style: styles.card, color: placeholder, opacity: pulse }),
3363
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkeletonBlock, { style: styles.card, color: placeholder, opacity: pulse })
3364
+ ] }),
3365
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native14.View, { style: styles.footerGroup, children: [
3366
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkeletonBlock, { style: styles.cta, color: placeholder, opacity: pulse }),
3367
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkeletonBlock, { style: styles.footer, color: placeholder, opacity: pulse })
3368
+ ] })
3369
+ ] });
3370
+ }
3371
+ var import_react3, import_react_native14, import_jsx_runtime, RADIUS, styles;
3372
+ var init_PaywallSkeleton = __esm({
3373
+ "src/domains/paywall/PaywallSkeleton.tsx"() {
3374
+ "use strict";
3375
+ import_react3 = require("react");
3376
+ import_react_native14 = require("react-native");
3377
+ init_extractFirstPageBackground();
3378
+ import_jsx_runtime = require("react/jsx-runtime");
3379
+ RADIUS = 14;
3380
+ styles = import_react_native14.StyleSheet.create({
3381
+ card: { borderRadius: RADIUS, flex: 1, height: 120 },
3382
+ cardsRow: { flexDirection: "row", gap: 12, marginTop: "auto", paddingHorizontal: 24 },
3383
+ container: { flex: 1, paddingBottom: 40, paddingTop: 64 },
3384
+ cta: { borderRadius: 30, height: 58, marginHorizontal: 24 },
3385
+ footer: { alignSelf: "center", borderRadius: 6, height: 12, marginTop: 20, width: 180 },
3386
+ footerGroup: { marginTop: 28 },
3387
+ header: { alignItems: "center", paddingVertical: 12 },
3388
+ hero: { alignSelf: "center", borderRadius: 20, height: 220, marginTop: 16, width: "70%" },
3389
+ logo: { borderRadius: 6, height: 22, width: 110 },
3390
+ titleGroup: { alignItems: "center", gap: 12, marginTop: 28, paddingHorizontal: 24 },
3391
+ titleLine: { borderRadius: 8, height: 26, width: "85%" },
3392
+ titleLineShort: { borderRadius: 8, height: 26, width: "60%" }
3393
+ });
3394
+ }
3395
+ });
3396
+
3302
3397
  // src/domains/paywall/paywallScripts.ts
3303
3398
  function buildPaywallInjectionScript(data) {
3304
3399
  const paywallData = {
@@ -3347,7 +3442,7 @@ function getNativeWebView() {
3347
3442
  if (NativeWebViewComponent) return NativeWebViewComponent;
3348
3443
  if (nativeWebViewLookupFailed) return null;
3349
3444
  try {
3350
- NativeWebViewComponent = (0, import_react_native14.requireNativeComponent)("PaywalloWebView");
3445
+ NativeWebViewComponent = (0, import_react_native15.requireNativeComponent)("PaywalloWebView");
3351
3446
  return NativeWebViewComponent;
3352
3447
  } catch {
3353
3448
  nativeWebViewLookupFailed = true;
@@ -3356,9 +3451,9 @@ function getNativeWebView() {
3356
3451
  }
3357
3452
  function getWebViewEmitter() {
3358
3453
  if (webViewEmitter) return webViewEmitter;
3359
- const mod = import_react_native14.NativeModules.PaywalloWebViewModule;
3454
+ const mod = import_react_native15.NativeModules.PaywalloWebViewModule;
3360
3455
  if (!mod) return null;
3361
- webViewEmitter = new import_react_native14.NativeEventEmitter(mod);
3456
+ webViewEmitter = new import_react_native15.NativeEventEmitter(mod);
3362
3457
  return webViewEmitter;
3363
3458
  }
3364
3459
  async function triggerNativeHaptic(style) {
@@ -3400,16 +3495,16 @@ function PaywallWebView({
3400
3495
  onReady,
3401
3496
  onError
3402
3497
  }) {
3403
- const paywallDataSent = (0, import_react3.useRef)(false);
3404
- const loadEndReceived = (0, import_react3.useRef)(false);
3405
- const [scriptToInject, setScriptToInject] = (0, import_react3.useState)(void 0);
3406
- const webViewRef = (0, import_react3.useRef)(null);
3407
- const onErrorRef = (0, import_react3.useRef)(onError);
3498
+ const paywallDataSent = (0, import_react4.useRef)(false);
3499
+ const loadEndReceived = (0, import_react4.useRef)(false);
3500
+ const [scriptToInject, setScriptToInject] = (0, import_react4.useState)(void 0);
3501
+ const webViewRef = (0, import_react4.useRef)(null);
3502
+ const onErrorRef = (0, import_react4.useRef)(onError);
3408
3503
  onErrorRef.current = onError;
3409
- const onReadyRef = (0, import_react3.useRef)(onReady);
3504
+ const onReadyRef = (0, import_react4.useRef)(onReady);
3410
3505
  onReadyRef.current = onReady;
3411
- const NativeWebView = (0, import_react3.useMemo)(() => getNativeWebView(), []);
3412
- (0, import_react3.useEffect)(() => {
3506
+ const NativeWebView = (0, import_react4.useMemo)(() => getNativeWebView(), []);
3507
+ (0, import_react4.useEffect)(() => {
3413
3508
  if (!NativeWebView) {
3414
3509
  onErrorRef.current?.({
3415
3510
  code: -1,
@@ -3417,10 +3512,10 @@ function PaywallWebView({
3417
3512
  });
3418
3513
  }
3419
3514
  }, [NativeWebView]);
3420
- const [resolvedWebUrl, setResolvedWebUrl] = (0, import_react3.useState)(
3515
+ const [resolvedWebUrl, setResolvedWebUrl] = (0, import_react4.useState)(
3421
3516
  () => webUrlProp ?? PaywalloClient.getWebUrl()
3422
3517
  );
3423
- (0, import_react3.useEffect)(() => {
3518
+ (0, import_react4.useEffect)(() => {
3424
3519
  if (webUrlProp !== void 0) {
3425
3520
  setResolvedWebUrl(webUrlProp ?? null);
3426
3521
  return;
@@ -3439,7 +3534,7 @@ function PaywallWebView({
3439
3534
  }, 200);
3440
3535
  return () => clearInterval(interval);
3441
3536
  }, [webUrlProp]);
3442
- (0, import_react3.useEffect)(() => {
3537
+ (0, import_react4.useEffect)(() => {
3443
3538
  if (!resolvedWebUrl) {
3444
3539
  onErrorRef.current?.({
3445
3540
  code: -1,
@@ -3447,16 +3542,16 @@ function PaywallWebView({
3447
3542
  });
3448
3543
  }
3449
3544
  }, []);
3450
- const injectScript = (0, import_react3.useCallback)((script) => {
3451
- if (import_react_native14.Platform.OS === "android") {
3452
- const handle = (0, import_react_native14.findNodeHandle)(webViewRef.current);
3545
+ const injectScript = (0, import_react4.useCallback)((script) => {
3546
+ if (import_react_native15.Platform.OS === "android") {
3547
+ const handle = (0, import_react_native15.findNodeHandle)(webViewRef.current);
3453
3548
  if (handle == null) return;
3454
- import_react_native14.UIManager.dispatchViewManagerCommand(handle, "injectJavaScript", [script]);
3549
+ import_react_native15.UIManager.dispatchViewManagerCommand(handle, "injectJavaScript", [script]);
3455
3550
  return;
3456
3551
  }
3457
3552
  setScriptToInject(script);
3458
3553
  }, []);
3459
- const buildAndSetInjectionScript = (0, import_react3.useCallback)(() => {
3554
+ const buildAndSetInjectionScript = (0, import_react4.useCallback)(() => {
3460
3555
  if (!craftData || paywallDataSent.current) return;
3461
3556
  paywallDataSent.current = true;
3462
3557
  const script = buildPaywallInjectionScript({
@@ -3467,16 +3562,16 @@ function PaywallWebView({
3467
3562
  });
3468
3563
  injectScript(script);
3469
3564
  }, [craftData, products, primaryProductId, secondaryProductId, injectScript]);
3470
- const processedIdsRef = (0, import_react3.useRef)(/* @__PURE__ */ new Set());
3471
- const dedupeTimersRef = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
3472
- (0, import_react3.useEffect)(() => {
3565
+ const processedIdsRef = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
3566
+ const dedupeTimersRef = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
3567
+ (0, import_react4.useEffect)(() => {
3473
3568
  return () => {
3474
3569
  for (const timerId of dedupeTimersRef.current.values()) clearTimeout(timerId);
3475
3570
  dedupeTimersRef.current.clear();
3476
3571
  processedIdsRef.current.clear();
3477
3572
  };
3478
3573
  }, []);
3479
- const handleParsedMessage = (0, import_react3.useCallback)(
3574
+ const handleParsedMessage = (0, import_react4.useCallback)(
3480
3575
  (message) => {
3481
3576
  const msgId = deriveMessageId(message);
3482
3577
  if (processedIdsRef.current.has(msgId)) return;
@@ -3510,7 +3605,7 @@ function PaywallWebView({
3510
3605
  if (message.payload?.url) {
3511
3606
  const url = message.payload.url;
3512
3607
  if (/^https?:\/\//i.test(url) || /^mailto:/i.test(url) || /^itms-apps:/i.test(url) || /^market:/i.test(url)) {
3513
- void import_react_native14.Linking.openURL(url);
3608
+ void import_react_native15.Linking.openURL(url);
3514
3609
  }
3515
3610
  }
3516
3611
  break;
@@ -3521,7 +3616,7 @@ function PaywallWebView({
3521
3616
  },
3522
3617
  [buildAndSetInjectionScript, onPurchase, onClose, onRestore]
3523
3618
  );
3524
- (0, import_react3.useEffect)(() => {
3619
+ (0, import_react4.useEffect)(() => {
3525
3620
  const emitter = getWebViewEmitter();
3526
3621
  if (!emitter) return;
3527
3622
  const loadEndSub = emitter.addListener("PaywalloWebViewLoadEnd", () => {
@@ -3546,7 +3641,7 @@ function PaywallWebView({
3546
3641
  errorSub.remove();
3547
3642
  };
3548
3643
  }, [buildAndSetInjectionScript, handleParsedMessage]);
3549
- (0, import_react3.useEffect)(() => {
3644
+ (0, import_react4.useEffect)(() => {
3550
3645
  const timer = setTimeout(() => {
3551
3646
  if (!paywallDataSent.current) {
3552
3647
  onErrorRef.current?.({
@@ -3557,13 +3652,13 @@ function PaywallWebView({
3557
3652
  }, 1e4);
3558
3653
  return () => clearTimeout(timer);
3559
3654
  }, []);
3560
- const handleLoadEnd = (0, import_react3.useCallback)(() => {
3655
+ const handleLoadEnd = (0, import_react4.useCallback)(() => {
3561
3656
  if (!loadEndReceived.current) {
3562
3657
  loadEndReceived.current = true;
3563
3658
  buildAndSetInjectionScript();
3564
3659
  }
3565
3660
  }, [buildAndSetInjectionScript]);
3566
- const handleNativeError = (0, import_react3.useCallback)((event) => {
3661
+ const handleNativeError = (0, import_react4.useCallback)((event) => {
3567
3662
  const ne = event.nativeEvent;
3568
3663
  let errorData;
3569
3664
  if (ne["payload"] && typeof ne["payload"] === "object") {
@@ -3579,30 +3674,30 @@ function PaywallWebView({
3579
3674
  }
3580
3675
  onErrorRef.current?.(parseErrorEvent(errorData));
3581
3676
  }, []);
3582
- const handleMessage = (0, import_react3.useCallback)(
3677
+ const handleMessage = (0, import_react4.useCallback)(
3583
3678
  (event) => {
3584
3679
  const message = parseWebViewMessage(event.nativeEvent.data);
3585
3680
  if (message) handleParsedMessage(message);
3586
3681
  },
3587
3682
  [handleParsedMessage]
3588
3683
  );
3589
- (0, import_react3.useEffect)(() => {
3684
+ (0, import_react4.useEffect)(() => {
3590
3685
  if (!paywallDataSent.current) return;
3591
3686
  injectScript(buildPurchaseStateScript(isPurchasing));
3592
3687
  }, [isPurchasing, injectScript]);
3593
- (0, import_react3.useEffect)(() => {
3594
- if (import_react_native14.Platform.OS !== "android") return;
3688
+ (0, import_react4.useEffect)(() => {
3689
+ if (import_react_native15.Platform.OS !== "android") return;
3595
3690
  const onBackPress = () => {
3596
3691
  if (!paywallDataSent.current) return false;
3597
3692
  injectScript(buildBackButtonScript());
3598
3693
  return true;
3599
3694
  };
3600
- const subscription = import_react_native14.BackHandler.addEventListener("hardwareBackPress", onBackPress);
3695
+ const subscription = import_react_native15.BackHandler.addEventListener("hardwareBackPress", onBackPress);
3601
3696
  return () => subscription.remove();
3602
3697
  }, [injectScript]);
3603
3698
  if (!resolvedWebUrl || !NativeWebView) return null;
3604
3699
  const paywallUrl = `${resolvedWebUrl}/paywall/${paywallId}`;
3605
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native14.View, { style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3700
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native15.View, { style: styles2.container, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
3606
3701
  NativeWebView,
3607
3702
  {
3608
3703
  ref: webViewRef,
@@ -3611,24 +3706,24 @@ function PaywallWebView({
3611
3706
  onMessage: handleMessage,
3612
3707
  onLoadEnd: handleLoadEnd,
3613
3708
  onError: handleNativeError,
3614
- style: styles.webview
3709
+ style: styles2.webview
3615
3710
  }
3616
3711
  ) });
3617
3712
  }
3618
- var import_react3, import_react_native14, import_jsx_runtime, NativeWebViewComponent, nativeWebViewLookupFailed, webViewEmitter, styles;
3713
+ var import_react4, import_react_native15, import_jsx_runtime2, NativeWebViewComponent, nativeWebViewLookupFailed, webViewEmitter, styles2;
3619
3714
  var init_PaywallWebView = __esm({
3620
3715
  "src/domains/paywall/PaywallWebView.tsx"() {
3621
3716
  "use strict";
3622
- import_react3 = require("react");
3623
- import_react_native14 = require("react-native");
3717
+ import_react4 = require("react");
3718
+ import_react_native15 = require("react-native");
3624
3719
  init_PaywalloClient();
3625
3720
  init_parseWebViewMessage();
3626
3721
  init_paywallScripts();
3627
- import_jsx_runtime = require("react/jsx-runtime");
3722
+ import_jsx_runtime2 = require("react/jsx-runtime");
3628
3723
  NativeWebViewComponent = null;
3629
3724
  nativeWebViewLookupFailed = false;
3630
3725
  webViewEmitter = null;
3631
- styles = import_react_native14.StyleSheet.create({
3726
+ styles2 = import_react_native15.StyleSheet.create({
3632
3727
  container: { flex: 1 },
3633
3728
  webview: { flex: 1, backgroundColor: "transparent" }
3634
3729
  });
@@ -3763,7 +3858,7 @@ function mapNativePurchase(native) {
3763
3858
  transactionId: native.transactionId,
3764
3859
  transactionDate: native.transactionDate,
3765
3860
  receipt: native.receipt,
3766
- platform: import_react_native15.Platform.OS
3861
+ platform: import_react_native16.Platform.OS
3767
3862
  };
3768
3863
  }
3769
3864
  function normalizeTransactionUpdate(raw) {
@@ -3783,17 +3878,17 @@ function normalizeTransactionUpdate(raw) {
3783
3878
  if (typeof r.currency === "string") update.currency = r.currency;
3784
3879
  return update;
3785
3880
  }
3786
- var import_react_native15, TRANSACTION_UPDATE_EVENT, PaywalloStoreKitNative, nativeStoreKit, NativeStoreKit;
3881
+ var import_react_native16, TRANSACTION_UPDATE_EVENT, PaywalloStoreKitNative, nativeStoreKit, NativeStoreKit;
3787
3882
  var init_NativeStoreKit = __esm({
3788
3883
  "src/domains/iap/NativeStoreKit.ts"() {
3789
3884
  "use strict";
3790
- import_react_native15 = require("react-native");
3885
+ import_react_native16 = require("react-native");
3791
3886
  init_PaywalloClient();
3792
3887
  init_uuid();
3793
3888
  init_IdentityManager();
3794
3889
  init_PurchaseError();
3795
3890
  TRANSACTION_UPDATE_EVENT = "PaywalloTransactionUpdate";
3796
- PaywalloStoreKitNative = import_react_native15.NativeModules.PaywalloStoreKit ?? null;
3891
+ PaywalloStoreKitNative = import_react_native16.NativeModules.PaywalloStoreKit ?? null;
3797
3892
  nativeStoreKit = {
3798
3893
  isAvailable: isAvailable3,
3799
3894
  async getProducts(productIds) {
@@ -3862,7 +3957,7 @@ var init_NativeStoreKit = __esm({
3862
3957
  subscribeToTransactionUpdates(listener) {
3863
3958
  if (!PaywalloStoreKitNative) return null;
3864
3959
  try {
3865
- const EmitterCtor = import_react_native15.NativeEventEmitter;
3960
+ const EmitterCtor = import_react_native16.NativeEventEmitter;
3866
3961
  const emitter = new EmitterCtor(PaywalloStoreKitNative);
3867
3962
  const sub = emitter.addListener(TRANSACTION_UPDATE_EVENT, (payload) => {
3868
3963
  const update = normalizeTransactionUpdate(payload);
@@ -4518,11 +4613,11 @@ var init_types2 = __esm({
4518
4613
  });
4519
4614
 
4520
4615
  // src/core/network/NetworkMonitor.ts
4521
- var import_react_native16, NetworkMonitorClass, networkMonitor;
4616
+ var import_react_native17, NetworkMonitorClass, networkMonitor;
4522
4617
  var init_NetworkMonitor = __esm({
4523
4618
  "src/core/network/NetworkMonitor.ts"() {
4524
4619
  "use strict";
4525
- import_react_native16 = require("react-native");
4620
+ import_react_native17 = require("react-native");
4526
4621
  init_types2();
4527
4622
  NetworkMonitorClass = class {
4528
4623
  constructor() {
@@ -4551,7 +4646,7 @@ var init_NetworkMonitor = __esm({
4551
4646
  }, this.config.checkInterval);
4552
4647
  }
4553
4648
  setupAppStateListener() {
4554
- this.appStateSubscription = import_react_native16.AppState.addEventListener("change", (state) => {
4649
+ this.appStateSubscription = import_react_native17.AppState.addEventListener("change", (state) => {
4555
4650
  if (state === "active") {
4556
4651
  if (!this.checkIntervalId) {
4557
4652
  this.checkIntervalId = setInterval(() => {
@@ -4920,11 +5015,11 @@ var init_queue = __esm({
4920
5015
  });
4921
5016
 
4922
5017
  // src/domains/iap/IAPValidator.ts
4923
- var import_react_native17, IAPValidator;
5018
+ var import_react_native18, IAPValidator;
4924
5019
  var init_IAPValidator = __esm({
4925
5020
  "src/domains/iap/IAPValidator.ts"() {
4926
5021
  "use strict";
4927
- import_react_native17 = require("react-native");
5022
+ import_react_native18 = require("react-native");
4928
5023
  init_PaywalloClient();
4929
5024
  init_queue();
4930
5025
  init_PurchaseError();
@@ -4950,7 +5045,7 @@ var init_IAPValidator = __esm({
4950
5045
  async validateWithServer(purchase, productId, options) {
4951
5046
  const apiClient = this.getApiClientFn();
4952
5047
  const product = this.getProductFromCache(productId);
4953
- const platform = import_react_native17.Platform.OS;
5048
+ const platform = import_react_native18.Platform.OS;
4954
5049
  const distinctId = PaywalloClient.getDistinctId();
4955
5050
  return this.validateWithRetry(
4956
5051
  () => apiClient.validatePurchase(
@@ -4985,7 +5080,7 @@ var init_IAPValidator = __esm({
4985
5080
  try {
4986
5081
  const apiClient = this.getApiClientFn();
4987
5082
  const product = this.getProductFromCache(productId);
4988
- const platform = import_react_native17.Platform.OS;
5083
+ const platform = import_react_native18.Platform.OS;
4989
5084
  const distinctId = PaywalloClient.getDistinctId();
4990
5085
  const appKey = apiClient.getAppKey();
4991
5086
  const body = {
@@ -5025,11 +5120,11 @@ function getIAPService() {
5025
5120
  }
5026
5121
  return _instance;
5027
5122
  }
5028
- var import_react_native18, IAPService, _instance;
5123
+ var import_react_native19, IAPService, _instance;
5029
5124
  var init_IAPService = __esm({
5030
5125
  "src/domains/iap/IAPService.ts"() {
5031
5126
  "use strict";
5032
- import_react_native18 = require("react-native");
5127
+ import_react_native19 = require("react-native");
5033
5128
  init_PaywalloClient();
5034
5129
  init_IAPTransactionEmitter();
5035
5130
  init_IAPValidator();
@@ -5179,7 +5274,7 @@ var init_IAPService = __esm({
5179
5274
  const transactions = await nativeStoreKit.getActiveTransactions();
5180
5275
  const apiClient = this.getApiClient();
5181
5276
  const distinctId = PaywalloClient.getDistinctId();
5182
- const platform = import_react_native18.Platform.OS;
5277
+ const platform = import_react_native19.Platform.OS;
5183
5278
  const results = await Promise.allSettled(
5184
5279
  transactions.map((tx) => {
5185
5280
  const product = this.productsCache.get(tx.productId);
@@ -5237,11 +5332,11 @@ var init_IAPService = __esm({
5237
5332
 
5238
5333
  // src/domains/paywall/usePaywallTracking.ts
5239
5334
  function usePaywallTracking() {
5240
- const trackClosed = (0, import_react4.useCallback)(async (opts) => {
5335
+ const trackClosed = (0, import_react5.useCallback)(async (opts) => {
5241
5336
  await emitPaywallClosed(opts);
5242
5337
  await stopHeartbeat();
5243
5338
  }, []);
5244
- const trackDismissed = (0, import_react4.useCallback)(async (opts) => {
5339
+ const trackDismissed = (0, import_react5.useCallback)(async (opts) => {
5245
5340
  const presentedAt = Date.now() - Math.max(0, opts.duration_ms);
5246
5341
  await trackClosed({
5247
5342
  placement: opts.placement,
@@ -5253,17 +5348,17 @@ function usePaywallTracking() {
5253
5348
  ...opts.variantId && { variantId: opts.variantId }
5254
5349
  });
5255
5350
  }, [trackClosed]);
5256
- const trackProductSelected = (0, import_react4.useCallback)((_opts) => {
5351
+ const trackProductSelected = (0, import_react5.useCallback)((_opts) => {
5257
5352
  }, []);
5258
- const trackPurchased = (0, import_react4.useCallback)(async (_opts) => {
5353
+ const trackPurchased = (0, import_react5.useCallback)(async (_opts) => {
5259
5354
  }, []);
5260
5355
  return { trackClosed, trackDismissed, trackProductSelected, trackPurchased };
5261
5356
  }
5262
- var import_react4;
5357
+ var import_react5;
5263
5358
  var init_usePaywallTracking = __esm({
5264
5359
  "src/domains/paywall/usePaywallTracking.ts"() {
5265
5360
  "use strict";
5266
- import_react4 = require("react");
5361
+ import_react5 = require("react");
5267
5362
  init_emitPaywallClosed();
5268
5363
  init_paywallHeartbeat();
5269
5364
  }
@@ -5271,19 +5366,19 @@ var init_usePaywallTracking = __esm({
5271
5366
 
5272
5367
  // src/domains/paywall/usePaywallActions.ts
5273
5368
  function usePaywallActions(placement, paywallConfig, onResult, variantKey, campaignId, openAnim, variantId) {
5274
- const [isPurchasing, setIsPurchasing] = (0, import_react5.useState)(false);
5275
- const isPurchasingRef = (0, import_react5.useRef)(false);
5276
- const [isClosing, setIsClosing] = (0, import_react5.useState)(false);
5277
- const slideAnim = (0, import_react5.useRef)(new import_react_native19.Animated.Value(0)).current;
5278
- const presentedAtRef = (0, import_react5.useRef)(Date.now());
5279
- const closedRef = (0, import_react5.useRef)(false);
5369
+ const [isPurchasing, setIsPurchasing] = (0, import_react6.useState)(false);
5370
+ const isPurchasingRef = (0, import_react6.useRef)(false);
5371
+ const [isClosing, setIsClosing] = (0, import_react6.useState)(false);
5372
+ const slideAnim = (0, import_react6.useRef)(new import_react_native20.Animated.Value(0)).current;
5373
+ const presentedAtRef = (0, import_react6.useRef)(Date.now());
5374
+ const closedRef = (0, import_react6.useRef)(false);
5280
5375
  const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
5281
- const closeWithReason = (0, import_react5.useCallback)((reason, resultOverride) => {
5376
+ const closeWithReason = (0, import_react6.useCallback)((reason, resultOverride) => {
5282
5377
  if (closedRef.current) return;
5283
5378
  closedRef.current = true;
5284
5379
  const animTarget = openAnim ?? slideAnim;
5285
- import_react_native19.Animated.timing(animTarget, {
5286
- toValue: import_react_native19.Dimensions.get("window").height,
5380
+ import_react_native20.Animated.timing(animTarget, {
5381
+ toValue: import_react_native20.Dimensions.get("window").height,
5287
5382
  duration: 350,
5288
5383
  useNativeDriver: true
5289
5384
  }).start(() => {
@@ -5297,13 +5392,13 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
5297
5392
  onResult(resultOverride ?? { presented: true, purchased: false, cancelled: true, restored: false });
5298
5393
  });
5299
5394
  }, [openAnim, slideAnim, paywallConfig, placement, trackDismissed, variantKey, campaignId, variantId, onResult]);
5300
- const handleClose = (0, import_react5.useCallback)(() => {
5395
+ const handleClose = (0, import_react6.useCallback)(() => {
5301
5396
  if (isClosing) return;
5302
5397
  setIsClosing(true);
5303
5398
  closeWithReason("dismissed");
5304
5399
  }, [isClosing, closeWithReason]);
5305
- (0, import_react5.useEffect)(() => {
5306
- const sub = import_react_native19.AppState.addEventListener("change", (nextState) => {
5400
+ (0, import_react6.useEffect)(() => {
5401
+ const sub = import_react_native20.AppState.addEventListener("change", (nextState) => {
5307
5402
  if (isPurchasingRef.current) return;
5308
5403
  if (nextState === "background" || nextState === "inactive") {
5309
5404
  closeWithReason("backgrounded");
@@ -5311,13 +5406,13 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
5311
5406
  });
5312
5407
  return () => sub.remove();
5313
5408
  }, [closeWithReason]);
5314
- (0, import_react5.useEffect)(() => {
5409
+ (0, import_react6.useEffect)(() => {
5315
5410
  const timer = setTimeout(() => {
5316
5411
  closeWithReason("timeout");
5317
5412
  }, PAYWALL_TIMEOUT_MS);
5318
5413
  return () => clearTimeout(timer);
5319
5414
  }, [closeWithReason]);
5320
- const handlePurchase = (0, import_react5.useCallback)(async (productId) => {
5415
+ const handlePurchase = (0, import_react6.useCallback)(async (productId) => {
5321
5416
  if (isPurchasing) return;
5322
5417
  setIsPurchasing(true);
5323
5418
  isPurchasingRef.current = true;
@@ -5347,7 +5442,7 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
5347
5442
  setIsPurchasing(false);
5348
5443
  }
5349
5444
  }, [isPurchasing, onResult, paywallConfig, placement, trackDismissed, trackProductSelected, trackPurchased, variantKey, campaignId, variantId]);
5350
- const handleRestore = (0, import_react5.useCallback)(async () => {
5445
+ const handleRestore = (0, import_react6.useCallback)(async () => {
5351
5446
  if (isPurchasing) return;
5352
5447
  setIsPurchasing(true);
5353
5448
  isPurchasingRef.current = true;
@@ -5371,12 +5466,12 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
5371
5466
  }, [isPurchasing, onResult, paywallConfig, placement, trackDismissed, variantKey, campaignId, variantId]);
5372
5467
  return { isPurchasing, slideAnim, handleClose, handlePurchase, handleRestore };
5373
5468
  }
5374
- var import_react5, import_react_native19, PAYWALL_TIMEOUT_MS;
5469
+ var import_react6, import_react_native20, PAYWALL_TIMEOUT_MS;
5375
5470
  var init_usePaywallActions = __esm({
5376
5471
  "src/domains/paywall/usePaywallActions.ts"() {
5377
5472
  "use strict";
5378
- import_react5 = require("react");
5379
- import_react_native19 = require("react-native");
5473
+ import_react6 = require("react");
5474
+ import_react_native20 = require("react-native");
5380
5475
  init_IAPService();
5381
5476
  init_usePaywallTracking();
5382
5477
  PAYWALL_TIMEOUT_MS = 30 * 60 * 1e3;
@@ -5385,15 +5480,15 @@ var init_usePaywallActions = __esm({
5385
5480
 
5386
5481
  // src/domains/paywall/usePaywallLoader.ts
5387
5482
  function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts, variantKey, campaignId, variantId) {
5388
- const [error, setError] = (0, import_react6.useState)(null);
5389
- const [paywallConfig, setPaywallConfig] = (0, import_react6.useState)(null);
5390
- const [products, setProducts] = (0, import_react6.useState)(/* @__PURE__ */ new Map());
5391
- const preloadedConfigRef = (0, import_react6.useRef)(preloadedConfig);
5483
+ const [error, setError] = (0, import_react7.useState)(null);
5484
+ const [paywallConfig, setPaywallConfig] = (0, import_react7.useState)(null);
5485
+ const [products, setProducts] = (0, import_react7.useState)(/* @__PURE__ */ new Map());
5486
+ const preloadedConfigRef = (0, import_react7.useRef)(preloadedConfig);
5392
5487
  preloadedConfigRef.current = preloadedConfig;
5393
- const preloadedProductsRef = (0, import_react6.useRef)(preloadedProducts);
5488
+ const preloadedProductsRef = (0, import_react7.useRef)(preloadedProducts);
5394
5489
  preloadedProductsRef.current = preloadedProducts;
5395
- const trackingDoneRef = (0, import_react6.useRef)(false);
5396
- (0, import_react6.useEffect)(() => {
5490
+ const trackingDoneRef = (0, import_react7.useRef)(false);
5491
+ (0, import_react7.useEffect)(() => {
5397
5492
  if (!visible) {
5398
5493
  trackingDoneRef.current = false;
5399
5494
  return;
@@ -5478,11 +5573,11 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
5478
5573
  }, [placement, visible, variantKey, campaignId, variantId]);
5479
5574
  return { paywallConfig, products, error };
5480
5575
  }
5481
- var import_react6;
5576
+ var import_react7;
5482
5577
  var init_usePaywallLoader = __esm({
5483
5578
  "src/domains/paywall/usePaywallLoader.ts"() {
5484
5579
  "use strict";
5485
- import_react6 = require("react");
5580
+ import_react7 = require("react");
5486
5581
  init_PaywalloClient();
5487
5582
  init_IAPService();
5488
5583
  init_PaywallError();
@@ -5495,11 +5590,11 @@ function ErrorFallback({ description, onRetry, onClose }) {
5495
5590
  const title = overrides?.title ?? getSdkString("paywallErrorTitle");
5496
5591
  const retryLabel = overrides?.retry ?? getSdkString("paywallRetryButton");
5497
5592
  const closeLabel = overrides?.close ?? getSdkString("paywallCloseButton");
5498
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native20.View, { style: styles2.fallbackContainer, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native20.View, { style: styles2.fallbackCard, children: [
5499
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native20.Text, { style: styles2.fallbackTitle, children: title }),
5500
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native20.Text, { style: styles2.fallbackDescription, children: description }),
5501
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native20.Pressable, { style: styles2.retryButton, onPress: onRetry, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native20.Text, { style: styles2.retryButtonText, children: retryLabel }) }),
5502
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native20.Pressable, { style: styles2.closeButton, onPress: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native20.Text, { style: styles2.closeButtonText, children: closeLabel }) })
5593
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native21.View, { style: styles3.fallbackContainer, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native21.View, { style: styles3.fallbackCard, children: [
5594
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native21.Text, { style: styles3.fallbackTitle, children: title }),
5595
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native21.Text, { style: styles3.fallbackDescription, children: description }),
5596
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native21.Pressable, { style: styles3.retryButton, onPress: onRetry, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native21.Text, { style: styles3.retryButtonText, children: retryLabel }) }),
5597
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native21.Pressable, { style: styles3.closeButton, onPress: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native21.Text, { style: styles3.closeButtonText, children: closeLabel }) })
5503
5598
  ] }) });
5504
5599
  }
5505
5600
  function PaywallModal({
@@ -5521,18 +5616,20 @@ function PaywallModal({
5521
5616
  campaignId,
5522
5617
  variantId
5523
5618
  );
5524
- const screenHeight = import_react_native20.Dimensions.get("window").height;
5525
- const openAnim = (0, import_react7.useRef)(new import_react_native20.Animated.Value(screenHeight)).current;
5526
- const [modalVisible, setModalVisible] = (0, import_react7.useState)(false);
5527
- const handleResult = (0, import_react7.useCallback)((result) => {
5619
+ const screenHeight = import_react_native21.Dimensions.get("window").height;
5620
+ const openAnim = (0, import_react8.useRef)(new import_react_native21.Animated.Value(screenHeight)).current;
5621
+ const [modalVisible, setModalVisible] = (0, import_react8.useState)(false);
5622
+ const handleResult = (0, import_react8.useCallback)((result) => {
5528
5623
  setModalVisible(false);
5529
5624
  onResult(result);
5530
5625
  }, [onResult]);
5531
5626
  const { isPurchasing, slideAnim, handleClose, handlePurchase, handleRestore } = usePaywallActions(placement, paywallConfig, handleResult, variantKey, campaignId, openAnim, variantId);
5532
- const [webViewError, setWebViewError] = (0, import_react7.useState)(null);
5533
- const [retryCount, setRetryCount] = (0, import_react7.useState)(0);
5534
- const retryCountRef = (0, import_react7.useRef)(0);
5535
- const handleWebViewError = (0, import_react7.useCallback)(
5627
+ const [webViewError, setWebViewError] = (0, import_react8.useState)(null);
5628
+ const [retryCount, setRetryCount] = (0, import_react8.useState)(0);
5629
+ const [showSkeleton, setShowSkeleton] = (0, import_react8.useState)(true);
5630
+ const skeletonOpacity = (0, import_react8.useRef)(new import_react_native21.Animated.Value(1)).current;
5631
+ const retryCountRef = (0, import_react8.useRef)(0);
5632
+ const handleWebViewError = (0, import_react8.useCallback)(
5536
5633
  (err) => {
5537
5634
  if (retryCountRef.current >= 1) {
5538
5635
  setWebViewError({ code: err.code, description: err.description });
@@ -5543,26 +5640,44 @@ function PaywallModal({
5543
5640
  },
5544
5641
  []
5545
5642
  );
5546
- const handleRetry = (0, import_react7.useCallback)(() => {
5643
+ const handleRetry = (0, import_react8.useCallback)(() => {
5547
5644
  retryCountRef.current = 0;
5548
5645
  setWebViewError(null);
5549
5646
  setRetryCount(0);
5550
- }, []);
5551
- (0, import_react7.useEffect)(() => {
5647
+ setShowSkeleton(true);
5648
+ skeletonOpacity.setValue(1);
5649
+ }, [skeletonOpacity]);
5650
+ const handleWebViewReady = (0, import_react8.useCallback)(() => {
5651
+ import_react_native21.Animated.timing(skeletonOpacity, {
5652
+ toValue: 0,
5653
+ duration: 250,
5654
+ easing: import_react_native21.Easing.out(import_react_native21.Easing.cubic),
5655
+ useNativeDriver: true
5656
+ }).start(() => setShowSkeleton(false));
5657
+ }, [skeletonOpacity]);
5658
+ (0, import_react8.useEffect)(() => {
5659
+ if (!visible || !showSkeleton) return;
5660
+ const timer = setTimeout(handleWebViewReady, 8e3);
5661
+ return () => clearTimeout(timer);
5662
+ }, [visible, showSkeleton, handleWebViewReady]);
5663
+ (0, import_react8.useEffect)(() => {
5552
5664
  if (visible) {
5553
5665
  setModalVisible(true);
5554
- openAnim.setValue(import_react_native20.Dimensions.get("window").height);
5555
- import_react_native20.Animated.timing(openAnim, {
5666
+ setShowSkeleton(true);
5667
+ skeletonOpacity.setValue(1);
5668
+ openAnim.setValue(import_react_native21.Dimensions.get("window").height);
5669
+ import_react_native21.Animated.timing(openAnim, {
5556
5670
  toValue: 0,
5557
5671
  duration: 550,
5558
- easing: import_react_native20.Easing.out(import_react_native20.Easing.cubic),
5672
+ easing: import_react_native21.Easing.out(import_react_native21.Easing.cubic),
5559
5673
  useNativeDriver: true
5560
5674
  }).start();
5561
5675
  }
5562
- }, [visible, openAnim]);
5676
+ }, [visible, openAnim, skeletonOpacity]);
5563
5677
  if (!modalVisible && !visible) return null;
5564
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
5565
- import_react_native20.Modal,
5678
+ const skeletonBackground = paywallConfig && "craftData" in paywallConfig.config ? extractFirstPageBackground(paywallConfig.config.craftData) : "#FFFFFF";
5679
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5680
+ import_react_native21.Modal,
5566
5681
  {
5567
5682
  visible: modalVisible,
5568
5683
  animationType: "none",
@@ -5570,16 +5685,16 @@ function PaywallModal({
5570
5685
  onRequestClose: handleClose,
5571
5686
  transparent: true,
5572
5687
  statusBarTranslucent: true,
5573
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native20.View, { style: styles2.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native20.Animated.View, { style: [styles2.animatedContainer, { transform: [{ translateY: openAnim }] }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native20.View, { style: styles2.container, children: [
5574
- error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native20.View, { style: styles2.errorContainer, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native20.Text, { style: styles2.errorText, children: error.message }) }),
5575
- !error && paywallConfig && "craftData" in paywallConfig.config && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: webViewError ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
5688
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native21.View, { style: styles3.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native21.Animated.View, { style: [styles3.animatedContainer, { transform: [{ translateY: openAnim }] }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native21.View, { style: styles3.container, children: [
5689
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native21.View, { style: styles3.errorContainer, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native21.Text, { style: styles3.errorText, children: error.message }) }),
5690
+ !error && paywallConfig && "craftData" in paywallConfig.config && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: webViewError ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5576
5691
  ErrorFallback,
5577
5692
  {
5578
5693
  description: webViewError.description,
5579
5694
  onRetry: handleRetry,
5580
5695
  onClose: handleClose
5581
5696
  }
5582
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
5697
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5583
5698
  PaywallWebView,
5584
5699
  {
5585
5700
  paywallId: paywallConfig.id,
@@ -5588,30 +5703,41 @@ function PaywallModal({
5588
5703
  primaryProductId: paywallConfig.primaryProductId,
5589
5704
  secondaryProductId: paywallConfig.secondaryProductId,
5590
5705
  isPurchasing,
5706
+ onReady: handleWebViewReady,
5591
5707
  onClose: handleClose,
5592
5708
  onPurchase: handlePurchase,
5593
5709
  onRestore: handleRestore,
5594
5710
  onError: handleWebViewError
5595
5711
  },
5596
5712
  retryCount
5597
- ) })
5713
+ ) }),
5714
+ showSkeleton && !error && !webViewError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5715
+ import_react_native21.Animated.View,
5716
+ {
5717
+ style: [import_react_native21.StyleSheet.absoluteFill, { opacity: skeletonOpacity }],
5718
+ pointerEvents: "none",
5719
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PaywallSkeleton, { backgroundColor: skeletonBackground })
5720
+ }
5721
+ )
5598
5722
  ] }) }) })
5599
5723
  }
5600
5724
  );
5601
5725
  }
5602
- var import_react7, import_react_native20, import_jsx_runtime2, styles2;
5726
+ var import_react8, import_react_native21, import_jsx_runtime3, styles3;
5603
5727
  var init_PaywallModal = __esm({
5604
5728
  "src/domains/paywall/PaywallModal.tsx"() {
5605
5729
  "use strict";
5606
- import_react7 = require("react");
5607
- import_react_native20 = require("react-native");
5730
+ import_react8 = require("react");
5731
+ import_react_native21 = require("react-native");
5608
5732
  init_PaywalloClient();
5609
5733
  init_localization();
5734
+ init_extractFirstPageBackground();
5735
+ init_PaywallSkeleton();
5610
5736
  init_PaywallWebView();
5611
5737
  init_usePaywallActions();
5612
5738
  init_usePaywallLoader();
5613
- import_jsx_runtime2 = require("react/jsx-runtime");
5614
- styles2 = import_react_native20.StyleSheet.create({
5739
+ import_jsx_runtime3 = require("react/jsx-runtime");
5740
+ styles3 = import_react_native21.StyleSheet.create({
5615
5741
  overlay: { flex: 1, backgroundColor: "rgba(0,0,0,0.3)" },
5616
5742
  animatedContainer: { flex: 1, backgroundColor: "#fff" },
5617
5743
  container: { flex: 1, backgroundColor: "#fff" },
@@ -5667,12 +5793,175 @@ var init_PaywallModal = __esm({
5667
5793
  }
5668
5794
  });
5669
5795
 
5796
+ // src/domains/paywall/PaywallPreloadService.ts
5797
+ var PRELOAD_TTL_MS, STALE_THRESHOLD, PaywallPreloadService, paywallPreloadService;
5798
+ var init_PaywallPreloadService = __esm({
5799
+ "src/domains/paywall/PaywallPreloadService.ts"() {
5800
+ "use strict";
5801
+ init_PaywalloClient();
5802
+ init_IAPService();
5803
+ init_PaywallError();
5804
+ PRELOAD_TTL_MS = 5 * 60 * 1e3;
5805
+ STALE_THRESHOLD = 0.75;
5806
+ PaywallPreloadService = class {
5807
+ constructor() {
5808
+ this.preloaded = /* @__PURE__ */ new Map();
5809
+ this.activePreloads = /* @__PURE__ */ new Map();
5810
+ this.apiClientRef = null;
5811
+ }
5812
+ async preload(apiClient, placement) {
5813
+ this.apiClientRef = apiClient;
5814
+ const existing = this.activePreloads.get(placement);
5815
+ if (existing) return existing;
5816
+ const promise = this.doPreload(apiClient, placement);
5817
+ this.activePreloads.set(placement, promise);
5818
+ try {
5819
+ return await promise;
5820
+ } finally {
5821
+ this.activePreloads.delete(placement);
5822
+ }
5823
+ }
5824
+ async preloadMany(apiClient, placements) {
5825
+ this.apiClientRef = apiClient;
5826
+ if (placements.length === 0) return;
5827
+ const debug = PaywalloClient.getConfig()?.debug;
5828
+ if (debug) console.log(`[Paywallo PRELOAD] paywalls: ${placements.length} placements`);
5829
+ for (let i = 0; i < placements.length; i++) {
5830
+ if (i > 0) await new Promise((r) => setTimeout(r, 200));
5831
+ void this.preload(apiClient, placements[i]).catch((err) => {
5832
+ if (debug) console.log(`[Paywallo PRELOAD] paywall failed ${placements[i]}:`, err);
5833
+ });
5834
+ }
5835
+ }
5836
+ getPreloaded(placement) {
5837
+ const cached2 = this.preloaded.get(placement);
5838
+ if (!cached2) return null;
5839
+ const age = Date.now() - cached2.preloadedAt;
5840
+ if (age >= PRELOAD_TTL_MS) {
5841
+ this.preloaded.delete(placement);
5842
+ return null;
5843
+ }
5844
+ if (age >= PRELOAD_TTL_MS * STALE_THRESHOLD && this.apiClientRef) {
5845
+ void this.preload(this.apiClientRef, placement).catch(() => {
5846
+ });
5847
+ }
5848
+ return { config: cached2.config, products: new Map(cached2.products) };
5849
+ }
5850
+ isPreloaded(placement) {
5851
+ return this.getPreloaded(placement) !== null;
5852
+ }
5853
+ clear() {
5854
+ this.preloaded.clear();
5855
+ this.activePreloads.clear();
5856
+ }
5857
+ async doPreload(apiClient, placement) {
5858
+ try {
5859
+ const config = await apiClient.getPaywall(placement);
5860
+ if (!config) {
5861
+ return {
5862
+ success: false,
5863
+ error: new PaywallError(
5864
+ PAYWALL_ERROR_CODES.NOT_FOUND,
5865
+ `Paywall not found for placement: ${placement}`
5866
+ )
5867
+ };
5868
+ }
5869
+ const products = await this.loadProductsFor(config);
5870
+ this.preloaded.set(placement, { config, products, preloadedAt: Date.now() });
5871
+ if (PaywalloClient.getConfig()?.debug) {
5872
+ console.log(`[Paywallo PRELOAD] paywall cached \u2014 placement: ${placement}`);
5873
+ }
5874
+ return { success: true };
5875
+ } catch (error) {
5876
+ return {
5877
+ success: false,
5878
+ error: error instanceof Error ? error : new PaywallError(PAYWALL_ERROR_CODES.LOAD_FAILED, String(error))
5879
+ };
5880
+ }
5881
+ }
5882
+ async loadProductsFor(config) {
5883
+ const productIds = [];
5884
+ if (config.primaryProductId) productIds.push(config.primaryProductId);
5885
+ if (config.secondaryProductId) productIds.push(config.secondaryProductId);
5886
+ if (productIds.length === 0) return /* @__PURE__ */ new Map();
5887
+ try {
5888
+ const loaded = await getIAPService().loadProducts(productIds);
5889
+ const map = /* @__PURE__ */ new Map();
5890
+ for (const p of loaded) map.set(p.productId, p);
5891
+ return map;
5892
+ } catch (err) {
5893
+ if (PaywalloClient.getConfig()?.debug) {
5894
+ console.log("[Paywallo PRELOAD] products failed, caching config only:", err);
5895
+ }
5896
+ return /* @__PURE__ */ new Map();
5897
+ }
5898
+ }
5899
+ };
5900
+ paywallPreloadService = new PaywallPreloadService();
5901
+ }
5902
+ });
5903
+
5904
+ // src/domains/paywall/PaywallWebViewPrewarmer.tsx
5905
+ function getNativeWebView2() {
5906
+ if (NativeWebViewComponent2) return NativeWebViewComponent2;
5907
+ if (nativeLookupFailed) return null;
5908
+ try {
5909
+ NativeWebViewComponent2 = (0, import_react_native22.requireNativeComponent)("PaywalloWebView");
5910
+ return NativeWebViewComponent2;
5911
+ } catch {
5912
+ nativeLookupFailed = true;
5913
+ return null;
5914
+ }
5915
+ }
5916
+ function PaywallWebViewPrewarmer({
5917
+ webUrl,
5918
+ durationMs = DEFAULT_DURATION_MS
5919
+ }) {
5920
+ const NativeWebView = (0, import_react9.useMemo)(() => getNativeWebView2(), []);
5921
+ const [mounted, setMounted] = (0, import_react9.useState)(true);
5922
+ (0, import_react9.useEffect)(() => {
5923
+ if (!NativeWebView) return;
5924
+ const timer = setTimeout(() => setMounted(false), durationMs);
5925
+ return () => clearTimeout(timer);
5926
+ }, [NativeWebView, durationMs]);
5927
+ if (!NativeWebView || !mounted) return null;
5928
+ const preheatUrl = `${webUrl}/paywall/preheat`;
5929
+ if (PaywalloClient.getConfig()?.debug) {
5930
+ console.log(`[Paywallo PREWARM] mounting hidden WebView: ${preheatUrl}`);
5931
+ }
5932
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native22.View, { pointerEvents: "none", style: styles4.hidden, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NativeWebView, { sourceUrl: preheatUrl, style: styles4.fill }) });
5933
+ }
5934
+ var import_react9, import_react_native22, import_jsx_runtime4, NativeWebViewComponent2, nativeLookupFailed, DEFAULT_DURATION_MS, styles4;
5935
+ var init_PaywallWebViewPrewarmer = __esm({
5936
+ "src/domains/paywall/PaywallWebViewPrewarmer.tsx"() {
5937
+ "use strict";
5938
+ import_react9 = require("react");
5939
+ import_react_native22 = require("react-native");
5940
+ init_PaywalloClient();
5941
+ import_jsx_runtime4 = require("react/jsx-runtime");
5942
+ NativeWebViewComponent2 = null;
5943
+ nativeLookupFailed = false;
5944
+ DEFAULT_DURATION_MS = 60 * 1e3;
5945
+ styles4 = import_react_native22.StyleSheet.create({
5946
+ hidden: {
5947
+ height: 1,
5948
+ left: -9999,
5949
+ opacity: 0,
5950
+ position: "absolute",
5951
+ top: -9999,
5952
+ width: 1
5953
+ },
5954
+ fill: { flex: 1 }
5955
+ });
5956
+ }
5957
+ });
5958
+
5670
5959
  // src/domains/paywall/usePaywallSession.ts
5671
5960
  function usePaywallSession(options) {
5672
- const closedRef = (0, import_react8.useRef)(false);
5673
- const optsRef = (0, import_react8.useRef)(options);
5961
+ const closedRef = (0, import_react10.useRef)(false);
5962
+ const optsRef = (0, import_react10.useRef)(options);
5674
5963
  optsRef.current = options;
5675
- (0, import_react8.useEffect)(() => {
5964
+ (0, import_react10.useEffect)(() => {
5676
5965
  if (!options) return;
5677
5966
  closedRef.current = false;
5678
5967
  startHeartbeat({
@@ -5702,16 +5991,16 @@ function usePaywallSession(options) {
5702
5991
  });
5703
5992
  };
5704
5993
  }, [options?.paywallId, options?.placement]);
5705
- const markClosed = (0, import_react8.useCallback)(() => {
5994
+ const markClosed = (0, import_react10.useCallback)(() => {
5706
5995
  closedRef.current = true;
5707
5996
  }, []);
5708
5997
  return { markClosed };
5709
5998
  }
5710
- var import_react8;
5999
+ var import_react10;
5711
6000
  var init_usePaywallSession = __esm({
5712
6001
  "src/domains/paywall/usePaywallSession.ts"() {
5713
6002
  "use strict";
5714
- import_react8 = require("react");
6003
+ import_react10 = require("react");
5715
6004
  init_emitPaywallClosed();
5716
6005
  init_paywallHeartbeat();
5717
6006
  }
@@ -5719,9 +6008,9 @@ var init_usePaywallSession = __esm({
5719
6008
 
5720
6009
  // src/domains/paywall/usePaywallPresenter.ts
5721
6010
  function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
5722
- const [showingPreloadedWebView, setShowingPreloadedWebView] = (0, import_react9.useState)(false);
5723
- const resolverRef = (0, import_react9.useRef)(null);
5724
- const presentedAtRef = (0, import_react9.useRef)(Date.now());
6011
+ const [showingPreloadedWebView, setShowingPreloadedWebView] = (0, import_react11.useState)(false);
6012
+ const resolverRef = (0, import_react11.useRef)(null);
6013
+ const presentedAtRef = (0, import_react11.useRef)(Date.now());
5725
6014
  const sessionOpts = showingPreloadedWebView && preloadedWebView ? {
5726
6015
  placement: preloadedWebView.placement,
5727
6016
  paywallId: preloadedWebView.paywallId,
@@ -5731,7 +6020,7 @@ function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
5731
6020
  ...preloadedWebView.campaignId && { campaignId: preloadedWebView.campaignId }
5732
6021
  } : null;
5733
6022
  const { markClosed } = usePaywallSession(sessionOpts);
5734
- (0, import_react9.useEffect)(() => {
6023
+ (0, import_react11.useEffect)(() => {
5735
6024
  return () => {
5736
6025
  if (resolverRef.current) {
5737
6026
  resolverRef.current({ presented: false, purchased: false, cancelled: true, restored: false });
@@ -5740,7 +6029,7 @@ function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
5740
6029
  setShowingPreloadedWebView(false);
5741
6030
  };
5742
6031
  }, []);
5743
- const presentPreloadedWebView = (0, import_react9.useCallback)(() => {
6032
+ const presentPreloadedWebView = (0, import_react11.useCallback)(() => {
5744
6033
  return new Promise((resolve) => {
5745
6034
  resolverRef.current = resolve;
5746
6035
  presentedAtRef.current = Date.now();
@@ -5774,16 +6063,16 @@ function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
5774
6063
  }
5775
6064
  });
5776
6065
  }, [preloadedWebView]);
5777
- const resolvePreloadedPaywall = (0, import_react9.useCallback)((result) => {
6066
+ const resolvePreloadedPaywall = (0, import_react11.useCallback)((result) => {
5778
6067
  if (resolverRef.current) {
5779
6068
  resolverRef.current(result);
5780
6069
  resolverRef.current = null;
5781
6070
  }
5782
6071
  }, []);
5783
- const handlePreloadedWebViewReady = (0, import_react9.useCallback)(() => {
6072
+ const handlePreloadedWebViewReady = (0, import_react11.useCallback)(() => {
5784
6073
  setPreloadedWebView((prev) => prev ? { ...prev, loaded: true } : null);
5785
6074
  }, [setPreloadedWebView]);
5786
- const handlePreloadedClose = (0, import_react9.useCallback)(() => {
6075
+ const handlePreloadedClose = (0, import_react11.useCallback)(() => {
5787
6076
  if (preloadedWebView) {
5788
6077
  markClosed();
5789
6078
  void emitPaywallClosed({
@@ -5801,11 +6090,11 @@ function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
5801
6090
  }, [preloadedWebView, markClosed, resolvePreloadedPaywall]);
5802
6091
  return { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady, handlePreloadedClose, setShowingPreloadedWebView, presentedAtRef };
5803
6092
  }
5804
- var import_react9;
6093
+ var import_react11;
5805
6094
  var init_usePaywallPresenter = __esm({
5806
6095
  "src/domains/paywall/usePaywallPresenter.ts"() {
5807
6096
  "use strict";
5808
- import_react9 = require("react");
6097
+ import_react11 = require("react");
5809
6098
  init_PaywalloClient();
5810
6099
  init_emitPaywallClosed();
5811
6100
  init_usePaywallSession();
@@ -5820,7 +6109,9 @@ var init_paywall = __esm({
5820
6109
  init_PaywallError();
5821
6110
  init_PaywallErrorBoundary();
5822
6111
  init_PaywallModal();
6112
+ init_PaywallPreloadService();
5823
6113
  init_PaywallWebView();
6114
+ init_PaywallWebViewPrewarmer();
5824
6115
  init_usePaywallPresenter();
5825
6116
  init_usePaywallTracking();
5826
6117
  }
@@ -7146,7 +7437,7 @@ function buildV2Envelope(events, providerContext) {
7146
7437
  };
7147
7438
  });
7148
7439
  if (context.sdk_version === void 0) context.sdk_version = SDK_VERSION;
7149
- if (context.platform === void 0) context.platform = import_react_native21.Platform.OS;
7440
+ if (context.platform === void 0) context.platform = import_react_native23.Platform.OS;
7150
7441
  if (context.distinct_id === void 0 && events.length > 0) {
7151
7442
  context.distinct_id = events[0].distinctId;
7152
7443
  }
@@ -7186,11 +7477,11 @@ function splitPayloadAndContext(properties) {
7186
7477
  }
7187
7478
  return { payload, promoted };
7188
7479
  }
7189
- var import_react_native21, import_zod2, v2EventSchema, v2ContextSchema, v2EnvelopeSchema, CONTEXT_KEY_ALIASES;
7480
+ var import_react_native23, import_zod2, v2EventSchema, v2ContextSchema, v2EnvelopeSchema, CONTEXT_KEY_ALIASES;
7190
7481
  var init_eventEnvelopeV2 = __esm({
7191
7482
  "src/core/eventEnvelopeV2.ts"() {
7192
7483
  "use strict";
7193
- import_react_native21 = require("react-native");
7484
+ import_react_native23 = require("react-native");
7194
7485
  import_zod2 = require("zod");
7195
7486
  init_uuid();
7196
7487
  init_eventFamilies();
@@ -7260,11 +7551,11 @@ var init_events = __esm({
7260
7551
  });
7261
7552
 
7262
7553
  // src/core/EventBatcher.ts
7263
- var import_react_native22, BATCH_MAX_SIZE, BATCH_FLUSH_MS, EVENT_NAME_REGEX, V2_BATCH_ENDPOINT, EventBatcher;
7554
+ var import_react_native24, BATCH_MAX_SIZE, BATCH_FLUSH_MS, EVENT_NAME_REGEX, V2_BATCH_ENDPOINT, EventBatcher;
7264
7555
  var init_EventBatcher = __esm({
7265
7556
  "src/core/EventBatcher.ts"() {
7266
7557
  "use strict";
7267
- import_react_native22 = require("react-native");
7558
+ import_react_native24 = require("react-native");
7268
7559
  init_eventEnvelopeV2();
7269
7560
  init_events();
7270
7561
  BATCH_MAX_SIZE = 25;
@@ -7313,7 +7604,7 @@ var init_EventBatcher = __esm({
7313
7604
  const event = {
7314
7605
  eventName,
7315
7606
  distinctId,
7316
- properties: { ...properties, platform: import_react_native22.Platform.OS },
7607
+ properties: { ...properties, platform: import_react_native24.Platform.OS },
7317
7608
  timestamp: timestamp ?? Date.now(),
7318
7609
  environment: environment.toLowerCase()
7319
7610
  };
@@ -7639,11 +7930,11 @@ var init_http = __esm({
7639
7930
  });
7640
7931
 
7641
7932
  // src/core/ApiClient.ts
7642
- var import_react_native23, ApiClient;
7933
+ var import_react_native25, ApiClient;
7643
7934
  var init_ApiClient = __esm({
7644
7935
  "src/core/ApiClient.ts"() {
7645
7936
  "use strict";
7646
- import_react_native23 = require("react-native");
7937
+ import_react_native25 = require("react-native");
7647
7938
  init_apiPurchaseMethods();
7648
7939
  init_ApiCache();
7649
7940
  init_ApiClientFlags();
@@ -7745,7 +8036,7 @@ var init_ApiClient = __esm({
7745
8036
  buildSdkHeaders() {
7746
8037
  return {
7747
8038
  "x-sdk-version": SDK_VERSION,
7748
- "x-sdk-platform": import_react_native23.Platform.OS,
8039
+ "x-sdk-platform": import_react_native25.Platform.OS,
7749
8040
  "x-sdk-environment": this.environment
7750
8041
  };
7751
8042
  }
@@ -7775,7 +8066,7 @@ var init_ApiClient = __esm({
7775
8066
  message,
7776
8067
  context,
7777
8068
  sdkVersion: SDK_VERSION,
7778
- platform: import_react_native23.Platform.OS
8069
+ platform: import_react_native25.Platform.OS
7779
8070
  }, true);
7780
8071
  } catch (err) {
7781
8072
  if (this.debug) console.log("[Paywallo:ApiClient] reportError failed", err);
@@ -7793,7 +8084,7 @@ var init_ApiClient = __esm({
7793
8084
  async identify(distinctId, properties, email, deviceId, pii) {
7794
8085
  const rawTraits = {
7795
8086
  email: email || void 0,
7796
- platform: import_react_native23.Platform.OS,
8087
+ platform: import_react_native25.Platform.OS,
7797
8088
  name: properties?.name,
7798
8089
  country: properties?.country,
7799
8090
  locale: properties?.locale,
@@ -8089,12 +8380,12 @@ async function detectEnvironment() {
8089
8380
  return cached;
8090
8381
  }
8091
8382
  }
8092
- var import_react_native24, nativeModule, cached;
8383
+ var import_react_native26, nativeModule, cached;
8093
8384
  var init_detectEnvironment = __esm({
8094
8385
  "src/utils/detectEnvironment.ts"() {
8095
8386
  "use strict";
8096
- import_react_native24 = require("react-native");
8097
- nativeModule = import_react_native24.NativeModules.PaywalloEnv ?? null;
8387
+ import_react_native26 = require("react-native");
8388
+ nativeModule = import_react_native26.NativeModules.PaywalloEnv ?? null;
8098
8389
  cached = null;
8099
8390
  }
8100
8391
  });
@@ -8276,10 +8567,10 @@ async function doInit(state, config) {
8276
8567
  let screenHeight;
8277
8568
  let screenDensity;
8278
8569
  try {
8279
- const screen = import_react_native25.Dimensions.get("screen");
8570
+ const screen = import_react_native27.Dimensions.get("screen");
8280
8571
  screenWidth = Math.round(screen.width) || void 0;
8281
8572
  screenHeight = Math.round(screen.height) || void 0;
8282
- screenDensity = import_react_native25.PixelRatio.get() || void 0;
8573
+ screenDensity = import_react_native27.PixelRatio.get() || void 0;
8283
8574
  } catch {
8284
8575
  }
8285
8576
  return {
@@ -8346,11 +8637,11 @@ async function doInit(state, config) {
8346
8637
  setupAutoPreload(state, apiClient, config, distinctId);
8347
8638
  startBatchPreload(apiClient, config.debug);
8348
8639
  }
8349
- var import_react_native25;
8640
+ var import_react_native27;
8350
8641
  var init_PaywalloInitializer = __esm({
8351
8642
  "src/core/PaywalloInitializer.ts"() {
8352
8643
  "use strict";
8353
- import_react_native25 = require("react-native");
8644
+ import_react_native27 = require("react-native");
8354
8645
  init_identity();
8355
8646
  init_AdvertisingIdManager();
8356
8647
  init_DeepLinkAttributionCapture();
@@ -8393,6 +8684,7 @@ async function doFullReset(state) {
8393
8684
  offlineQueue.dispose();
8394
8685
  networkMonitor.dispose();
8395
8686
  campaignGateService.clearPreloaded();
8687
+ paywallPreloadService.clear();
8396
8688
  await notificationsManager.invalidateLocalToken().catch(() => void 0);
8397
8689
  notificationsManager.destroy();
8398
8690
  deepLinkAttributionCapture.stop();
@@ -8427,6 +8719,7 @@ var init_PaywalloLifecycle = __esm({
8427
8719
  init_identity();
8428
8720
  init_DeepLinkAttributionCapture();
8429
8721
  init_notifications();
8722
+ init_paywall();
8430
8723
  init_session();
8431
8724
  init_SubscriptionCache();
8432
8725
  init_network();
@@ -8730,6 +9023,15 @@ var init_PaywalloClient = __esm({
8730
9023
  isPreloaded(placement) {
8731
9024
  return campaignGateService.getPreloadedCampaign(placement) !== null;
8732
9025
  }
9026
+ async preloadPaywall(placement) {
9027
+ return paywallPreloadService.preload(this.getApiClientOrThrow(), placement);
9028
+ }
9029
+ async preloadPaywalls(placements) {
9030
+ return paywallPreloadService.preloadMany(this.getApiClientOrThrow(), placements);
9031
+ }
9032
+ isPaywallPreloaded(placement) {
9033
+ return paywallPreloadService.isPreloaded(placement);
9034
+ }
8733
9035
  getAutoPreloadedPlacement() {
8734
9036
  return this.state.autoPreloadedPlacement;
8735
9037
  }
@@ -8775,7 +9077,7 @@ __export(OfferingService_exports, {
8775
9077
  offeringService: () => offeringService
8776
9078
  });
8777
9079
  function resolveProductId(raw) {
8778
- const isIos = import_react_native26.Platform.OS === "ios";
9080
+ const isIos = import_react_native28.Platform.OS === "ios";
8779
9081
  const productId = isIos ? raw.apple_product_id : raw.google_product_id;
8780
9082
  return productId ?? "";
8781
9083
  }
@@ -8794,7 +9096,7 @@ function mapProduct2(raw) {
8794
9096
  };
8795
9097
  }
8796
9098
  function pickPlatformProduct(products) {
8797
- const isIos = import_react_native26.Platform.OS === "ios";
9099
+ const isIos = import_react_native28.Platform.OS === "ios";
8798
9100
  const match = products.find((p) => isIos ? p.apple_product_id : p.google_product_id);
8799
9101
  return match ?? products[0] ?? null;
8800
9102
  }
@@ -8810,11 +9112,11 @@ function mapOffering(raw) {
8810
9112
  product: mapProduct2(rawProduct)
8811
9113
  };
8812
9114
  }
8813
- var import_react_native26, OFFERINGS_CACHE_KEY, DEFAULT_TTL3, OfferingService, offeringService;
9115
+ var import_react_native28, OFFERINGS_CACHE_KEY, DEFAULT_TTL3, OfferingService, offeringService;
8814
9116
  var init_OfferingService = __esm({
8815
9117
  "src/domains/offering/OfferingService.ts"() {
8816
9118
  "use strict";
8817
- import_react_native26 = require("react-native");
9119
+ import_react_native28 = require("react-native");
8818
9120
  init_SecureStorage();
8819
9121
  OFFERINGS_CACHE_KEY = "@paywallo:offerings_cache";
8820
9122
  DEFAULT_TTL3 = 5 * 60 * 1e3;
@@ -8975,8 +9277,8 @@ module.exports = __toCommonJS(index_exports);
8975
9277
  init_PaywalloClient();
8976
9278
 
8977
9279
  // src/context/PaywalloContext.ts
8978
- var import_react10 = require("react");
8979
- var PaywalloContext = (0, import_react10.createContext)(null);
9280
+ var import_react12 = require("react");
9281
+ var PaywalloContext = (0, import_react12.createContext)(null);
8980
9282
 
8981
9283
  // src/index.ts
8982
9284
  init_ApiClient();
@@ -9021,7 +9323,7 @@ init_session();
9021
9323
  init_PaywalloError();
9022
9324
 
9023
9325
  // src/hooks/useOfferingPurchase.ts
9024
- var import_react11 = require("react");
9326
+ var import_react13 = require("react");
9025
9327
 
9026
9328
  // src/domains/plan/invalidateCache.ts
9027
9329
  async function invalidatePlanCache() {
@@ -9034,8 +9336,8 @@ async function invalidatePlanCache() {
9034
9336
 
9035
9337
  // src/hooks/useOfferingPurchase.ts
9036
9338
  function useOfferingPurchase() {
9037
- const [state, setState] = (0, import_react11.useState)("idle");
9038
- const purchase = (0, import_react11.useCallback)(async (productId) => {
9339
+ const [state, setState] = (0, import_react13.useState)("idle");
9340
+ const purchase = (0, import_react13.useCallback)(async (productId) => {
9039
9341
  setState("purchasing");
9040
9342
  try {
9041
9343
  const iapService = getIAPService();
@@ -9059,7 +9361,7 @@ function useOfferingPurchase() {
9059
9361
  }
9060
9362
 
9061
9363
  // src/hooks/useOfferings.ts
9062
- var import_react12 = require("react");
9364
+ var import_react14 = require("react");
9063
9365
  async function getOfferingService() {
9064
9366
  try {
9065
9367
  const mod = await Promise.resolve().then(() => (init_OfferingService(), OfferingService_exports));
@@ -9069,10 +9371,10 @@ async function getOfferingService() {
9069
9371
  }
9070
9372
  }
9071
9373
  function useOfferings(ids) {
9072
- const [offerings, setOfferings] = (0, import_react12.useState)([]);
9073
- const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
9374
+ const [offerings, setOfferings] = (0, import_react14.useState)([]);
9375
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
9074
9376
  const idsKey = ids?.join(",") ?? "";
9075
- const fetchOfferings = (0, import_react12.useCallback)(async () => {
9377
+ const fetchOfferings = (0, import_react14.useCallback)(async () => {
9076
9378
  setIsLoading(true);
9077
9379
  try {
9078
9380
  const service = await getOfferingService();
@@ -9088,25 +9390,25 @@ function useOfferings(ids) {
9088
9390
  setIsLoading(false);
9089
9391
  }
9090
9392
  }, [idsKey]);
9091
- (0, import_react12.useEffect)(() => {
9393
+ (0, import_react14.useEffect)(() => {
9092
9394
  void fetchOfferings();
9093
9395
  }, [fetchOfferings]);
9094
9396
  return { offerings, isLoading };
9095
9397
  }
9096
9398
 
9097
9399
  // src/hooks/useOnboarding.ts
9098
- var import_react13 = require("react");
9400
+ var import_react15 = require("react");
9099
9401
  init_onboarding();
9100
9402
  function useOnboarding() {
9101
- const step = (0, import_react13.useCallback)(
9403
+ const step = (0, import_react15.useCallback)(
9102
9404
  (stepName) => onboardingManager.step(stepName),
9103
9405
  []
9104
9406
  );
9105
- const complete = (0, import_react13.useCallback)(
9407
+ const complete = (0, import_react15.useCallback)(
9106
9408
  () => onboardingManager.complete(),
9107
9409
  []
9108
9410
  );
9109
- const drop = (0, import_react13.useCallback)(
9411
+ const drop = (0, import_react15.useCallback)(
9110
9412
  (stepName) => onboardingManager.drop(stepName),
9111
9413
  []
9112
9414
  );
@@ -9114,10 +9416,10 @@ function useOnboarding() {
9114
9416
  }
9115
9417
 
9116
9418
  // src/hooks/usePaywallo.ts
9117
- var import_react14 = require("react");
9419
+ var import_react16 = require("react");
9118
9420
  init_ClientError();
9119
9421
  function usePaywallo() {
9120
- const context = (0, import_react14.useContext)(PaywalloContext);
9422
+ const context = (0, import_react16.useContext)(PaywalloContext);
9121
9423
  if (!context) {
9122
9424
  throw new ClientError(CLIENT_ERROR_CODES.PROVIDER_MISSING, "usePaywallo must be used within a PaywalloProvider");
9123
9425
  }
@@ -9125,11 +9427,11 @@ function usePaywallo() {
9125
9427
  }
9126
9428
 
9127
9429
  // src/hooks/usePlanPurchase.ts
9128
- var import_react15 = require("react");
9430
+ var import_react17 = require("react");
9129
9431
  function usePlanPurchase() {
9130
- const [state, setState] = (0, import_react15.useState)("idle");
9131
- const [error, setError] = (0, import_react15.useState)(null);
9132
- const purchase = (0, import_react15.useCallback)(async (productId) => {
9432
+ const [state, setState] = (0, import_react17.useState)("idle");
9433
+ const [error, setError] = (0, import_react17.useState)(null);
9434
+ const purchase = (0, import_react17.useCallback)(async (productId) => {
9133
9435
  setState("purchasing");
9134
9436
  setError(null);
9135
9437
  try {
@@ -9152,7 +9454,7 @@ function usePlanPurchase() {
9152
9454
  return { success: false, error: purchaseError };
9153
9455
  }
9154
9456
  }, []);
9155
- const restore = (0, import_react15.useCallback)(async () => {
9457
+ const restore = (0, import_react17.useCallback)(async () => {
9156
9458
  setState("restoring");
9157
9459
  setError(null);
9158
9460
  try {
@@ -9181,7 +9483,7 @@ function usePlanPurchase() {
9181
9483
  }
9182
9484
 
9183
9485
  // src/hooks/usePlans.ts
9184
- var import_react16 = require("react");
9486
+ var import_react18 = require("react");
9185
9487
  async function getPlanService() {
9186
9488
  try {
9187
9489
  const mod = await Promise.resolve().then(() => (init_PlanService(), PlanService_exports));
@@ -9191,11 +9493,11 @@ async function getPlanService() {
9191
9493
  }
9192
9494
  }
9193
9495
  function usePlans() {
9194
- const [allPlans, setAllPlans] = (0, import_react16.useState)([]);
9195
- const [currentPlan, setCurrentPlan] = (0, import_react16.useState)(null);
9196
- const [isLoading, setIsLoading] = (0, import_react16.useState)(true);
9197
- const [error, setError] = (0, import_react16.useState)(null);
9198
- const fetchPlans = (0, import_react16.useCallback)(async (forceRefresh = false) => {
9496
+ const [allPlans, setAllPlans] = (0, import_react18.useState)([]);
9497
+ const [currentPlan, setCurrentPlan] = (0, import_react18.useState)(null);
9498
+ const [isLoading, setIsLoading] = (0, import_react18.useState)(true);
9499
+ const [error, setError] = (0, import_react18.useState)(null);
9500
+ const fetchPlans = (0, import_react18.useCallback)(async (forceRefresh = false) => {
9199
9501
  setIsLoading(true);
9200
9502
  setError(null);
9201
9503
  try {
@@ -9216,17 +9518,17 @@ function usePlans() {
9216
9518
  setIsLoading(false);
9217
9519
  }
9218
9520
  }, []);
9219
- const refresh = (0, import_react16.useCallback)(() => {
9521
+ const refresh = (0, import_react18.useCallback)(() => {
9220
9522
  return fetchPlans(true);
9221
9523
  }, [fetchPlans]);
9222
- (0, import_react16.useEffect)(() => {
9524
+ (0, import_react18.useEffect)(() => {
9223
9525
  void fetchPlans(false);
9224
9526
  }, [fetchPlans]);
9225
9527
  return { currentPlan, allPlans, isLoading, error, refresh };
9226
9528
  }
9227
9529
 
9228
9530
  // src/hooks/useProducts.ts
9229
- var import_react17 = require("react");
9531
+ var import_react19 = require("react");
9230
9532
 
9231
9533
  // src/utils/productMappers.ts
9232
9534
  function mapToIAPProduct(product) {
@@ -9265,11 +9567,11 @@ function mapToFormattedProduct(product) {
9265
9567
 
9266
9568
  // src/hooks/useProducts.ts
9267
9569
  function useProducts(initialProductIds) {
9268
- const [products, setProducts] = (0, import_react17.useState)([]);
9269
- const [formattedProducts, setFormattedProducts] = (0, import_react17.useState)([]);
9270
- const [isLoading, setIsLoading] = (0, import_react17.useState)(false);
9271
- const [error, setError] = (0, import_react17.useState)(null);
9272
- const loadProducts = (0, import_react17.useCallback)(async (productIds) => {
9570
+ const [products, setProducts] = (0, import_react19.useState)([]);
9571
+ const [formattedProducts, setFormattedProducts] = (0, import_react19.useState)([]);
9572
+ const [isLoading, setIsLoading] = (0, import_react19.useState)(false);
9573
+ const [error, setError] = (0, import_react19.useState)(null);
9574
+ const loadProducts = (0, import_react19.useCallback)(async (productIds) => {
9273
9575
  setIsLoading(true);
9274
9576
  setError(null);
9275
9577
  try {
@@ -9283,24 +9585,24 @@ function useProducts(initialProductIds) {
9283
9585
  setIsLoading(false);
9284
9586
  }
9285
9587
  }, []);
9286
- (0, import_react17.useEffect)(() => {
9588
+ (0, import_react19.useEffect)(() => {
9287
9589
  if (initialProductIds && initialProductIds.length > 0) {
9288
9590
  void loadProducts(initialProductIds);
9289
9591
  }
9290
9592
  }, []);
9291
- const getProduct = (0, import_react17.useCallback)(
9593
+ const getProduct = (0, import_react19.useCallback)(
9292
9594
  (productId) => {
9293
9595
  return products.find((p) => p.productId === productId);
9294
9596
  },
9295
9597
  [products]
9296
9598
  );
9297
- const getFormattedProduct = (0, import_react17.useCallback)(
9599
+ const getFormattedProduct = (0, import_react19.useCallback)(
9298
9600
  (productId) => {
9299
9601
  return formattedProducts.find((p) => p.productId === productId);
9300
9602
  },
9301
9603
  [formattedProducts]
9302
9604
  );
9303
- const getPriceInfo = (0, import_react17.useCallback)(
9605
+ const getPriceInfo = (0, import_react19.useCallback)(
9304
9606
  (productId) => {
9305
9607
  const p = formattedProducts.find((fp) => fp.productId === productId);
9306
9608
  if (!p) return void 0;
@@ -9329,14 +9631,14 @@ function useProducts(initialProductIds) {
9329
9631
  }
9330
9632
 
9331
9633
  // src/hooks/usePurchase.ts
9332
- var import_react18 = require("react");
9634
+ var import_react20 = require("react");
9333
9635
  function toIAPPurchase(p) {
9334
9636
  return { productId: p.productId, transactionId: p.transactionId, transactionDate: p.transactionDate, transactionReceipt: p.receipt };
9335
9637
  }
9336
9638
  function usePurchase() {
9337
- const [state, setState] = (0, import_react18.useState)("idle");
9338
- const [error, setError] = (0, import_react18.useState)(null);
9339
- const purchase = (0, import_react18.useCallback)(async (productId) => {
9639
+ const [state, setState] = (0, import_react20.useState)("idle");
9640
+ const [error, setError] = (0, import_react20.useState)(null);
9641
+ const purchase = (0, import_react20.useCallback)(async (productId) => {
9340
9642
  setState("purchasing");
9341
9643
  setError(null);
9342
9644
  try {
@@ -9364,7 +9666,7 @@ function usePurchase() {
9364
9666
  return { status: "failed", error: purchaseError };
9365
9667
  }
9366
9668
  }, []);
9367
- const restore = (0, import_react18.useCallback)(async () => {
9669
+ const restore = (0, import_react20.useCallback)(async () => {
9368
9670
  setState("restoring");
9369
9671
  setError(null);
9370
9672
  try {
@@ -9383,14 +9685,14 @@ function usePurchase() {
9383
9685
  }
9384
9686
 
9385
9687
  // src/hooks/useSubscription.ts
9386
- var import_react19 = require("react");
9688
+ var import_react21 = require("react");
9387
9689
  init_SubscriptionManager();
9388
9690
  var EMPTY_ENTITLEMENTS = [];
9389
9691
  function useSubscription() {
9390
- const [status, setStatus] = (0, import_react19.useState)(null);
9391
- const [isLoading, setIsLoading] = (0, import_react19.useState)(true);
9392
- const [error, setError] = (0, import_react19.useState)(null);
9393
- const refresh = (0, import_react19.useCallback)(async () => {
9692
+ const [status, setStatus] = (0, import_react21.useState)(null);
9693
+ const [isLoading, setIsLoading] = (0, import_react21.useState)(true);
9694
+ const [error, setError] = (0, import_react21.useState)(null);
9695
+ const refresh = (0, import_react21.useCallback)(async () => {
9394
9696
  setIsLoading(true);
9395
9697
  setError(null);
9396
9698
  try {
@@ -9403,7 +9705,7 @@ function useSubscription() {
9403
9705
  setIsLoading(false);
9404
9706
  }
9405
9707
  }, []);
9406
- const restore = (0, import_react19.useCallback)(async () => {
9708
+ const restore = (0, import_react21.useCallback)(async () => {
9407
9709
  setIsLoading(true);
9408
9710
  setError(null);
9409
9711
  try {
@@ -9416,7 +9718,7 @@ function useSubscription() {
9416
9718
  setIsLoading(false);
9417
9719
  }
9418
9720
  }, []);
9419
- (0, import_react19.useEffect)(() => {
9721
+ (0, import_react21.useEffect)(() => {
9420
9722
  void subscriptionManager.getSubscriptionStatus().then((s) => {
9421
9723
  setStatus(s);
9422
9724
  setIsLoading(false);
@@ -9444,63 +9746,257 @@ function useSubscription() {
9444
9746
  }
9445
9747
 
9446
9748
  // src/PaywalloProvider.tsx
9447
- var import_react25 = require("react");
9448
- var import_react_native27 = require("react-native");
9749
+ var import_react27 = require("react");
9750
+ var import_react_native29 = require("react-native");
9449
9751
  init_PaywalloClient();
9450
- init_SecureStorage();
9451
9752
  init_NativeStorage();
9753
+ init_SecureStorage();
9452
9754
  init_campaign();
9453
9755
  init_paywall();
9454
9756
  init_paywall();
9455
9757
  init_paywall();
9456
9758
  init_paywall();
9457
- init_session();
9458
- init_ClientError();
9759
+ init_paywall();
9760
+ init_paywall();
9459
9761
 
9460
- // src/hooks/useAppAutoEvents.ts
9461
- var import_react20 = require("react");
9762
+ // src/domains/paywall/SuperwallAutoBridge.ts
9763
+ init_PaywalloClient();
9462
9764
 
9463
- // src/utils/platformDetection.ts
9464
- async function detectPlatform3() {
9465
- try {
9466
- const rn = await import("react-native");
9467
- return rn.Platform?.OS === "ios" ? "ios" : "android";
9468
- } catch {
9469
- return "android";
9765
+ // src/core/utils/eventId.ts
9766
+ function hashString(input) {
9767
+ let h1 = 3735928559;
9768
+ let h2 = 1103547991;
9769
+ for (let i = 0; i < input.length; i++) {
9770
+ const ch = input.charCodeAt(i);
9771
+ h1 = Math.imul(h1 ^ ch, 2654435769);
9772
+ h2 = Math.imul(h2 ^ ch, 1598716949);
9470
9773
  }
9774
+ h1 = Math.imul(h1 ^ h1 >>> 16, 73244475);
9775
+ h1 ^= Math.imul(h2 ^ h2 >>> 13, 351452291);
9776
+ h2 = Math.imul(h2 ^ h2 >>> 16, 73244475);
9777
+ h2 ^= Math.imul(h1 ^ h1 >>> 13, 351452291);
9778
+ const toHex = (n) => (n >>> 0).toString(16).padStart(8, "0");
9779
+ return toHex(h1) + toHex(h2);
9471
9780
  }
9472
- async function detectOsVersion() {
9473
- try {
9474
- const rn = await import("react-native");
9475
- const version = rn.Platform?.Version;
9476
- return version != null ? String(version) : "unknown";
9477
- } catch {
9478
- return "unknown";
9479
- }
9781
+ function buildDeterministicEventId(parts) {
9782
+ const raw = hashString(parts.join("|"));
9783
+ const p1 = raw.slice(0, 8);
9784
+ const p2 = raw.slice(8, 12);
9785
+ const p3 = "4" + raw.slice(13, 16);
9786
+ const p4 = (parseInt(raw[16], 16) & 3 | 8).toString(16) + raw.slice(17, 20);
9787
+ const p5 = raw.padEnd(32, "0").slice(20, 32);
9788
+ return `${p1}-${p2}-${p3}-${p4}-${p5}`;
9480
9789
  }
9481
9790
 
9482
- // src/hooks/useAppAutoEvents.ts
9483
- var FIRST_SEEN_KEY = "@paywallo:firstSeen_v226";
9484
- var DEBOUNCE_MS = 1e3;
9485
- function useAppAutoEvents(config) {
9486
- const { isInitialized, getAppVersion: getAppVersion2, getSessionId, emitLifecycle, storageGet, storageSet, debug } = config;
9487
- const didRunRef = (0, import_react20.useRef)(false);
9488
- (0, import_react20.useEffect)(() => {
9489
- if (debug) console.log("[Paywallo EVENTS] useEffect fired", { isInitialized, didRunRefCurrent: didRunRef.current });
9490
- if (!isInitialized || didRunRef.current) return;
9491
- const timer = setTimeout(() => {
9492
- if (didRunRef.current) return;
9493
- didRunRef.current = true;
9494
- void (async () => {
9495
- const sessionId = getSessionId();
9496
- const appVersion = getAppVersion2();
9497
- const deviceType = await detectPlatform3();
9498
- const osVersion = await detectOsVersion();
9499
- if (debug) console.log("[Paywallo EVENTS] computed device context", { deviceType, osVersion, appVersion, sessionId, hasSessionId: !!sessionId });
9500
- try {
9501
- if (debug) console.log("[Paywallo EVENTS] checking install \u2014 about to read FIRST_SEEN_KEY");
9502
- const firstSeen = await storageGet(FIRST_SEEN_KEY);
9503
- if (!firstSeen) {
9791
+ // src/domains/paywall/SuperwallAutoBridge.ts
9792
+ var bridgeStarted = false;
9793
+ var unsubscribe = null;
9794
+ var debugEnabled = false;
9795
+ function log(msg, data) {
9796
+ if (!debugEnabled) return;
9797
+ if (data !== void 0) {
9798
+ console.log(`[Paywallo:SuperwallBridge] ${msg}`, data);
9799
+ } else {
9800
+ console.log(`[Paywallo:SuperwallBridge] ${msg}`);
9801
+ }
9802
+ }
9803
+ function minuteBucket(ts) {
9804
+ return String(Math.floor(ts / 6e4));
9805
+ }
9806
+ function buildEventId(type, identifier, ts) {
9807
+ return buildDeterministicEventId(["sw", type, identifier, minuteBucket(ts)]);
9808
+ }
9809
+ async function trackPaywallEvent(type, identifier, ts, extra) {
9810
+ const _eventId = buildEventId(type, identifier, ts);
9811
+ try {
9812
+ const cleanExtra = {};
9813
+ if (extra) {
9814
+ for (const [k, v] of Object.entries(extra)) if (v !== void 0) cleanExtra[k] = v;
9815
+ }
9816
+ await PaywalloClient.track("paywall", {
9817
+ priority: "critical",
9818
+ properties: {
9819
+ type,
9820
+ paywall_id: identifier,
9821
+ ...cleanExtra
9822
+ }
9823
+ });
9824
+ } catch (err) {
9825
+ log("track error", { type, identifier, eventId: _eventId, err: String(err) });
9826
+ }
9827
+ }
9828
+ function mapDismissToCloseReason(resultType) {
9829
+ switch (resultType) {
9830
+ case "purchased":
9831
+ return "purchased";
9832
+ case "restored":
9833
+ return "purchased";
9834
+ // restore counts as a purchase outcome for funnel
9835
+ case "declined":
9836
+ return "dismissed";
9837
+ default:
9838
+ return void 0;
9839
+ }
9840
+ }
9841
+ function buildCallbacks() {
9842
+ return {
9843
+ onPaywallPresent: (info) => {
9844
+ const ts = Date.now();
9845
+ log("onPaywallPresent", { identifier: info.identifier });
9846
+ void trackPaywallEvent("viewed", info.identifier, ts, {
9847
+ placement: info.presentedByEventWithName,
9848
+ variant_id: info.experiment?.variant?.id,
9849
+ campaign_id: info.experiment?.id
9850
+ });
9851
+ },
9852
+ onPaywallDismiss: (info, result) => {
9853
+ const ts = Date.now();
9854
+ log("onPaywallDismiss", { identifier: info.identifier, result: result.type });
9855
+ void trackPaywallEvent("closed", info.identifier, ts, {
9856
+ placement: info.presentedByEventWithName,
9857
+ variant_id: info.experiment?.variant?.id,
9858
+ campaign_id: info.experiment?.id,
9859
+ close_reason: mapDismissToCloseReason(result.type),
9860
+ closed_at: new Date(ts).toISOString()
9861
+ });
9862
+ },
9863
+ onPurchase: (params) => {
9864
+ const ts = Date.now();
9865
+ const safe = params !== null && typeof params === "object" ? params : {};
9866
+ const productId = "productId" in safe && typeof safe.productId === "string" ? safe.productId : "unknown";
9867
+ const identifier = "paywallIdentifier" in safe && typeof safe.paywallIdentifier === "string" ? safe.paywallIdentifier : productId;
9868
+ log("onPurchase", { productId, identifier });
9869
+ void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
9870
+ },
9871
+ // Global Superwall event listener — used specifically for `transactionComplete`
9872
+ // which is the only event that exposes the full product (currency/price) +
9873
+ // transaction (storeTransactionId) shape needed to create a server-side
9874
+ // `transactions` row. Other events are skipped (paywallOpen/Dismiss already
9875
+ // handled above with their typed callbacks).
9876
+ onSuperwallEvent: (info) => {
9877
+ const ev = info?.event;
9878
+ if (!ev || ev.event !== "transactionComplete") return;
9879
+ const ts = Date.now();
9880
+ const product = ev.product;
9881
+ const transaction = ev.transaction;
9882
+ if (!product) {
9883
+ log("transactionComplete missing product \u2014 skipping transaction track");
9884
+ return;
9885
+ }
9886
+ const productId = product.productIdentifier ?? product.id ?? "unknown";
9887
+ const currency = product.currencyCode ?? "USD";
9888
+ const price = product.price ?? 0;
9889
+ const transactionId = transaction?.storeTransactionId ?? transaction?.originalTransactionIdentifier ?? `${productId}_${String(ts)}`;
9890
+ const isTrial = product.hasFreeTrial === true || (product.trialPeriodPrice ?? 0) > 0;
9891
+ const paywallIdentifier = ev.paywallInfo?.identifier;
9892
+ log("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
9893
+ void (async () => {
9894
+ try {
9895
+ await PaywalloClient.track("transaction", {
9896
+ priority: "critical",
9897
+ properties: {
9898
+ type: isTrial ? "trial_started" : "completed",
9899
+ // Server's IngestTranslator looks for `transaction_id`/`product_id`
9900
+ // /`amount`/`currency`/`paywall_id` keys (not `tx_id`). PurchaseHandler
9901
+ // then resolves type=trial/new/renewal based on amount being 0 vs >0.
9902
+ transaction_id: transactionId,
9903
+ product_id: productId,
9904
+ amount: price,
9905
+ currency,
9906
+ ...paywallIdentifier ? { paywall_id: paywallIdentifier } : {}
9907
+ }
9908
+ });
9909
+ } catch (err) {
9910
+ log("transactionComplete track error", { err: String(err) });
9911
+ }
9912
+ })();
9913
+ }
9914
+ };
9915
+ }
9916
+ function startSuperwallAutoBridge(debug = false) {
9917
+ debugEnabled = debug;
9918
+ if (bridgeStarted) {
9919
+ log("already started, skipping");
9920
+ return;
9921
+ }
9922
+ void detectAndStart();
9923
+ }
9924
+ async function detectAndStart() {
9925
+ let subscribeFn;
9926
+ try {
9927
+ const mod = await import("expo-superwall");
9928
+ if (!mod || typeof mod.subscribeToSuperwallEvents !== "function") {
9929
+ const internal = await import("expo-superwall/build/src/internal/superwallEventBridge");
9930
+ if (typeof internal.subscribeToSuperwallEvents !== "function") {
9931
+ log("expo-superwall subscribeToSuperwallEvents not found \u2014 bridge inactive");
9932
+ return;
9933
+ }
9934
+ subscribeFn = internal.subscribeToSuperwallEvents;
9935
+ } else {
9936
+ subscribeFn = mod.subscribeToSuperwallEvents;
9937
+ }
9938
+ } catch {
9939
+ log("expo-superwall not installed \u2014 bridge inactive");
9940
+ return;
9941
+ }
9942
+ try {
9943
+ const callbacks = buildCallbacks();
9944
+ unsubscribe = subscribeFn(void 0, () => callbacks);
9945
+ bridgeStarted = true;
9946
+ log("bridge started");
9947
+ } catch (err) {
9948
+ log("subscribe failed", { err: String(err) });
9949
+ }
9950
+ }
9951
+
9952
+ // src/PaywalloProvider.tsx
9953
+ init_session();
9954
+ init_ClientError();
9955
+
9956
+ // src/hooks/useAppAutoEvents.ts
9957
+ var import_react22 = require("react");
9958
+
9959
+ // src/utils/platformDetection.ts
9960
+ async function detectPlatform3() {
9961
+ try {
9962
+ const rn = await import("react-native");
9963
+ return rn.Platform?.OS === "ios" ? "ios" : "android";
9964
+ } catch {
9965
+ return "android";
9966
+ }
9967
+ }
9968
+ async function detectOsVersion() {
9969
+ try {
9970
+ const rn = await import("react-native");
9971
+ const version = rn.Platform?.Version;
9972
+ return version != null ? String(version) : "unknown";
9973
+ } catch {
9974
+ return "unknown";
9975
+ }
9976
+ }
9977
+
9978
+ // src/hooks/useAppAutoEvents.ts
9979
+ var FIRST_SEEN_KEY = "@paywallo:firstSeen_v226";
9980
+ var DEBOUNCE_MS = 1e3;
9981
+ function useAppAutoEvents(config) {
9982
+ const { isInitialized, getAppVersion: getAppVersion2, getSessionId, emitLifecycle, storageGet, storageSet, debug } = config;
9983
+ const didRunRef = (0, import_react22.useRef)(false);
9984
+ (0, import_react22.useEffect)(() => {
9985
+ if (debug) console.log("[Paywallo EVENTS] useEffect fired", { isInitialized, didRunRefCurrent: didRunRef.current });
9986
+ if (!isInitialized || didRunRef.current) return;
9987
+ const timer = setTimeout(() => {
9988
+ if (didRunRef.current) return;
9989
+ didRunRef.current = true;
9990
+ void (async () => {
9991
+ const sessionId = getSessionId();
9992
+ const appVersion = getAppVersion2();
9993
+ const deviceType = await detectPlatform3();
9994
+ const osVersion = await detectOsVersion();
9995
+ if (debug) console.log("[Paywallo EVENTS] computed device context", { deviceType, osVersion, appVersion, sessionId, hasSessionId: !!sessionId });
9996
+ try {
9997
+ if (debug) console.log("[Paywallo EVENTS] checking install \u2014 about to read FIRST_SEEN_KEY");
9998
+ const firstSeen = await storageGet(FIRST_SEEN_KEY);
9999
+ if (!firstSeen) {
9504
10000
  await emitLifecycle({ type: "install", device_type: deviceType, os_version: osVersion, app_version: appVersion, ...sessionId ? { session_id: sessionId } : {} }, "critical");
9505
10001
  await storageSet(FIRST_SEEN_KEY, (/* @__PURE__ */ new Date()).toISOString());
9506
10002
  if (debug) console.log("[Paywallo EVENTS] install event emitted (first time)");
@@ -9525,7 +10021,7 @@ function useAppAutoEvents(config) {
9525
10021
  }
9526
10022
 
9527
10023
  // src/hooks/useCampaignPreload.ts
9528
- var import_react21 = require("react");
10024
+ var import_react23 = require("react");
9529
10025
  init_PaywalloClient();
9530
10026
  init_CampaignError();
9531
10027
 
@@ -9596,17 +10092,17 @@ function getCacheEntry(cache, placement) {
9596
10092
 
9597
10093
  // src/hooks/useCampaignPreload.ts
9598
10094
  function useCampaignPreload() {
9599
- const [preloadedWebView, setPreloadedWebView] = (0, import_react21.useState)(null);
9600
- const preloadedCampaignsRef = (0, import_react21.useRef)(/* @__PURE__ */ new Map());
9601
- const isPreloadValid = (0, import_react21.useCallback)(
10095
+ const [preloadedWebView, setPreloadedWebView] = (0, import_react23.useState)(null);
10096
+ const preloadedCampaignsRef = (0, import_react23.useRef)(/* @__PURE__ */ new Map());
10097
+ const isPreloadValid = (0, import_react23.useCallback)(
9602
10098
  (placement) => isCacheEntryValid(preloadedCampaignsRef.current.get(placement)),
9603
10099
  []
9604
10100
  );
9605
- const consumePreloadedCampaign = (0, import_react21.useCallback)(
10101
+ const consumePreloadedCampaign = (0, import_react23.useCallback)(
9606
10102
  (placement) => getCacheEntry(preloadedCampaignsRef.current, placement),
9607
10103
  []
9608
10104
  );
9609
- const preloadCampaign = (0, import_react21.useCallback)(
10105
+ const preloadCampaign = (0, import_react23.useCallback)(
9610
10106
  async (placement, context) => {
9611
10107
  try {
9612
10108
  const campaign = await PaywalloClient.getCampaign(placement, context);
@@ -9675,11 +10171,11 @@ function useCampaignPreload() {
9675
10171
  }
9676
10172
 
9677
10173
  // src/hooks/useProductLoader.ts
9678
- var import_react22 = require("react");
10174
+ var import_react24 = require("react");
9679
10175
  function useProductLoader() {
9680
- const [products, setProducts] = (0, import_react22.useState)(/* @__PURE__ */ new Map());
9681
- const [isLoadingProducts, setIsLoadingProducts] = (0, import_react22.useState)(false);
9682
- const refreshProducts = (0, import_react22.useCallback)(async (productIds) => {
10176
+ const [products, setProducts] = (0, import_react24.useState)(/* @__PURE__ */ new Map());
10177
+ const [isLoadingProducts, setIsLoadingProducts] = (0, import_react24.useState)(false);
10178
+ const refreshProducts = (0, import_react24.useCallback)(async (productIds) => {
9683
10179
  setIsLoadingProducts(true);
9684
10180
  try {
9685
10181
  const iapService = getIAPService();
@@ -9697,11 +10193,11 @@ function useProductLoader() {
9697
10193
  }
9698
10194
 
9699
10195
  // src/hooks/usePurchaseOrchestrator.ts
9700
- var import_react23 = require("react");
10196
+ var import_react25 = require("react");
9701
10197
  init_paywall();
9702
10198
  function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, invalidateSubscriptionCache) {
9703
10199
  const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
9704
- const handlePreloadedPurchase = (0, import_react23.useCallback)(
10200
+ const handlePreloadedPurchase = (0, import_react25.useCallback)(
9705
10201
  async (productId) => {
9706
10202
  try {
9707
10203
  if (preloadedWebView) {
@@ -9739,7 +10235,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
9739
10235
  },
9740
10236
  [preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed, trackProductSelected, trackPurchased, invalidateSubscriptionCache]
9741
10237
  );
9742
- const handlePreloadedRestore = (0, import_react23.useCallback)(async () => {
10238
+ const handlePreloadedRestore = (0, import_react25.useCallback)(async () => {
9743
10239
  try {
9744
10240
  const transactions = await getIAPService().restore();
9745
10241
  if (transactions.length === 0) return;
@@ -9762,7 +10258,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
9762
10258
  }
9763
10259
 
9764
10260
  // src/hooks/useSubscriptionSync.ts
9765
- var import_react24 = require("react");
10261
+ var import_react26 = require("react");
9766
10262
  init_PaywalloClient();
9767
10263
  init_SubscriptionCache();
9768
10264
 
@@ -9872,18 +10368,18 @@ async function checkPersistedCache(distinctId, onHit) {
9872
10368
 
9873
10369
  // src/hooks/useSubscriptionSync.ts
9874
10370
  function useSubscriptionSync() {
9875
- const [subscription, setSubscription] = (0, import_react24.useState)(null);
9876
- const cacheRef = (0, import_react24.useRef)(null);
9877
- const setCacheAndState = (0, import_react24.useCallback)((sub) => {
10371
+ const [subscription, setSubscription] = (0, import_react26.useState)(null);
10372
+ const cacheRef = (0, import_react26.useRef)(null);
10373
+ const setCacheAndState = (0, import_react26.useCallback)((sub) => {
9878
10374
  cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
9879
10375
  setSubscription(sub);
9880
10376
  }, []);
9881
- const invalidateCache = (0, import_react24.useCallback)(() => {
10377
+ const invalidateCache = (0, import_react26.useCallback)(() => {
9882
10378
  cacheRef.current = null;
9883
10379
  const distinctId = PaywalloClient.getDistinctId();
9884
10380
  if (distinctId) void subscriptionCache.invalidate(distinctId);
9885
10381
  }, []);
9886
- const fetchAndCache = (0, import_react24.useCallback)(async () => {
10382
+ const fetchAndCache = (0, import_react26.useCallback)(async () => {
9887
10383
  const apiClient = PaywalloClient.getApiClient();
9888
10384
  const distinctId = PaywalloClient.getDistinctId();
9889
10385
  if (!apiClient || !distinctId) return null;
@@ -9893,7 +10389,7 @@ function useSubscriptionSync() {
9893
10389
  void subscriptionCache.set(distinctId, toDomainResponse(status));
9894
10390
  return sub;
9895
10391
  }, [setCacheAndState]);
9896
- const hasActiveSubscription = (0, import_react24.useCallback)(async () => {
10392
+ const hasActiveSubscription = (0, import_react26.useCallback)(async () => {
9897
10393
  const apiClient = PaywalloClient.getApiClient();
9898
10394
  const distinctId = PaywalloClient.getDistinctId();
9899
10395
  if (!apiClient || !distinctId) return false;
@@ -9913,7 +10409,7 @@ function useSubscriptionSync() {
9913
10409
  return resolveOfflineFromCache(distinctId);
9914
10410
  }
9915
10411
  }, [setCacheAndState]);
9916
- const getSubscription = (0, import_react24.useCallback)(async () => {
10412
+ const getSubscription = (0, import_react26.useCallback)(async () => {
9917
10413
  const apiClient = PaywalloClient.getApiClient();
9918
10414
  const distinctId = PaywalloClient.getDistinctId();
9919
10415
  if (!apiClient || !distinctId) return null;
@@ -9934,214 +10430,24 @@ function useSubscriptionSync() {
9934
10430
  return { subscription, hasActiveSubscription, getSubscription, invalidateCache };
9935
10431
  }
9936
10432
 
9937
- // src/domains/paywall/SuperwallAutoBridge.ts
9938
- init_PaywalloClient();
9939
-
9940
- // src/core/utils/eventId.ts
9941
- function hashString(input) {
9942
- let h1 = 3735928559;
9943
- let h2 = 1103547991;
9944
- for (let i = 0; i < input.length; i++) {
9945
- const ch = input.charCodeAt(i);
9946
- h1 = Math.imul(h1 ^ ch, 2654435769);
9947
- h2 = Math.imul(h2 ^ ch, 1598716949);
9948
- }
9949
- h1 = Math.imul(h1 ^ h1 >>> 16, 73244475);
9950
- h1 ^= Math.imul(h2 ^ h2 >>> 13, 351452291);
9951
- h2 = Math.imul(h2 ^ h2 >>> 16, 73244475);
9952
- h2 ^= Math.imul(h1 ^ h1 >>> 13, 351452291);
9953
- const toHex = (n) => (n >>> 0).toString(16).padStart(8, "0");
9954
- return toHex(h1) + toHex(h2);
9955
- }
9956
- function buildDeterministicEventId(parts) {
9957
- const raw = hashString(parts.join("|"));
9958
- const p1 = raw.slice(0, 8);
9959
- const p2 = raw.slice(8, 12);
9960
- const p3 = "4" + raw.slice(13, 16);
9961
- const p4 = (parseInt(raw[16], 16) & 3 | 8).toString(16) + raw.slice(17, 20);
9962
- const p5 = raw.padEnd(32, "0").slice(20, 32);
9963
- return `${p1}-${p2}-${p3}-${p4}-${p5}`;
9964
- }
9965
-
9966
- // src/domains/paywall/SuperwallAutoBridge.ts
9967
- var bridgeStarted = false;
9968
- var unsubscribe = null;
9969
- var debugEnabled = false;
9970
- function log(msg, data) {
9971
- if (!debugEnabled) return;
9972
- if (data !== void 0) {
9973
- console.log(`[Paywallo:SuperwallBridge] ${msg}`, data);
9974
- } else {
9975
- console.log(`[Paywallo:SuperwallBridge] ${msg}`);
9976
- }
9977
- }
9978
- function minuteBucket(ts) {
9979
- return String(Math.floor(ts / 6e4));
9980
- }
9981
- function buildEventId(type, identifier, ts) {
9982
- return buildDeterministicEventId(["sw", type, identifier, minuteBucket(ts)]);
9983
- }
9984
- async function trackPaywallEvent(type, identifier, ts, extra) {
9985
- const _eventId = buildEventId(type, identifier, ts);
9986
- try {
9987
- const cleanExtra = {};
9988
- if (extra) {
9989
- for (const [k, v] of Object.entries(extra)) if (v !== void 0) cleanExtra[k] = v;
9990
- }
9991
- await PaywalloClient.track("paywall", {
9992
- priority: "critical",
9993
- properties: {
9994
- type,
9995
- paywall_id: identifier,
9996
- ...cleanExtra
9997
- }
9998
- });
9999
- } catch (err) {
10000
- log("track error", { type, identifier, eventId: _eventId, err: String(err) });
10001
- }
10002
- }
10003
- function mapDismissToCloseReason(resultType) {
10004
- switch (resultType) {
10005
- case "purchased":
10006
- return "purchased";
10007
- case "restored":
10008
- return "purchased";
10009
- // restore counts as a purchase outcome for funnel
10010
- case "declined":
10011
- return "dismissed";
10012
- default:
10013
- return void 0;
10014
- }
10015
- }
10016
- function buildCallbacks() {
10017
- return {
10018
- onPaywallPresent: (info) => {
10019
- const ts = Date.now();
10020
- log("onPaywallPresent", { identifier: info.identifier });
10021
- void trackPaywallEvent("viewed", info.identifier, ts, {
10022
- placement: info.presentedByEventWithName,
10023
- variant_id: info.experiment?.variant?.id,
10024
- campaign_id: info.experiment?.id
10025
- });
10026
- },
10027
- onPaywallDismiss: (info, result) => {
10028
- const ts = Date.now();
10029
- log("onPaywallDismiss", { identifier: info.identifier, result: result.type });
10030
- void trackPaywallEvent("closed", info.identifier, ts, {
10031
- placement: info.presentedByEventWithName,
10032
- variant_id: info.experiment?.variant?.id,
10033
- campaign_id: info.experiment?.id,
10034
- close_reason: mapDismissToCloseReason(result.type),
10035
- closed_at: new Date(ts).toISOString()
10036
- });
10037
- },
10038
- onPurchase: (params) => {
10039
- const ts = Date.now();
10040
- const safe = params !== null && typeof params === "object" ? params : {};
10041
- const productId = "productId" in safe && typeof safe.productId === "string" ? safe.productId : "unknown";
10042
- const identifier = "paywallIdentifier" in safe && typeof safe.paywallIdentifier === "string" ? safe.paywallIdentifier : productId;
10043
- log("onPurchase", { productId, identifier });
10044
- void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
10045
- },
10046
- // Global Superwall event listener — used specifically for `transactionComplete`
10047
- // which is the only event that exposes the full product (currency/price) +
10048
- // transaction (storeTransactionId) shape needed to create a server-side
10049
- // `transactions` row. Other events are skipped (paywallOpen/Dismiss already
10050
- // handled above with their typed callbacks).
10051
- onSuperwallEvent: (info) => {
10052
- const ev = info?.event;
10053
- if (!ev || ev.event !== "transactionComplete") return;
10054
- const ts = Date.now();
10055
- const product = ev.product;
10056
- const transaction = ev.transaction;
10057
- if (!product) {
10058
- log("transactionComplete missing product \u2014 skipping transaction track");
10059
- return;
10060
- }
10061
- const productId = product.productIdentifier ?? product.id ?? "unknown";
10062
- const currency = product.currencyCode ?? "USD";
10063
- const price = product.price ?? 0;
10064
- const transactionId = transaction?.storeTransactionId ?? transaction?.originalTransactionIdentifier ?? `${productId}_${String(ts)}`;
10065
- const isTrial = product.hasFreeTrial === true || (product.trialPeriodPrice ?? 0) > 0;
10066
- const paywallIdentifier = ev.paywallInfo?.identifier;
10067
- log("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
10068
- void (async () => {
10069
- try {
10070
- await PaywalloClient.track("transaction", {
10071
- priority: "critical",
10072
- properties: {
10073
- type: isTrial ? "trial_started" : "completed",
10074
- // Server's IngestTranslator looks for `transaction_id`/`product_id`
10075
- // /`amount`/`currency`/`paywall_id` keys (not `tx_id`). PurchaseHandler
10076
- // then resolves type=trial/new/renewal based on amount being 0 vs >0.
10077
- transaction_id: transactionId,
10078
- product_id: productId,
10079
- amount: price,
10080
- currency,
10081
- ...paywallIdentifier ? { paywall_id: paywallIdentifier } : {}
10082
- }
10083
- });
10084
- } catch (err) {
10085
- log("transactionComplete track error", { err: String(err) });
10086
- }
10087
- })();
10088
- }
10089
- };
10090
- }
10091
- function startSuperwallAutoBridge(debug = false) {
10092
- debugEnabled = debug;
10093
- if (bridgeStarted) {
10094
- log("already started, skipping");
10095
- return;
10096
- }
10097
- void detectAndStart();
10098
- }
10099
- async function detectAndStart() {
10100
- let subscribeFn;
10101
- try {
10102
- const mod = await import("expo-superwall");
10103
- if (!mod || typeof mod.subscribeToSuperwallEvents !== "function") {
10104
- const internal = await import("expo-superwall/build/src/internal/superwallEventBridge");
10105
- if (typeof internal.subscribeToSuperwallEvents !== "function") {
10106
- log("expo-superwall subscribeToSuperwallEvents not found \u2014 bridge inactive");
10107
- return;
10108
- }
10109
- subscribeFn = internal.subscribeToSuperwallEvents;
10110
- } else {
10111
- subscribeFn = mod.subscribeToSuperwallEvents;
10112
- }
10113
- } catch {
10114
- log("expo-superwall not installed \u2014 bridge inactive");
10115
- return;
10116
- }
10117
- try {
10118
- const callbacks = buildCallbacks();
10119
- unsubscribe = subscribeFn(void 0, () => callbacks);
10120
- bridgeStarted = true;
10121
- log("bridge started");
10122
- } catch (err) {
10123
- log("subscribe failed", { err: String(err) });
10124
- }
10125
- }
10126
-
10127
10433
  // src/PaywalloProvider.tsx
10128
10434
  init_localization();
10129
- var import_jsx_runtime3 = require("react/jsx-runtime");
10435
+ var import_jsx_runtime5 = require("react/jsx-runtime");
10130
10436
  function PaywalloProvider({ children, config }) {
10131
- const [isInitialized, setIsInitialized] = (0, import_react25.useState)(false);
10132
- const [initError, setInitError] = (0, import_react25.useState)(null);
10133
- const [distinctId, setDistinctId] = (0, import_react25.useState)(null);
10134
- const [activePaywall, setActivePaywall] = (0, import_react25.useState)(null);
10437
+ const [isInitialized, setIsInitialized] = (0, import_react27.useState)(false);
10438
+ const [initError, setInitError] = (0, import_react27.useState)(null);
10439
+ const [distinctId, setDistinctId] = (0, import_react27.useState)(null);
10440
+ const [activePaywall, setActivePaywall] = (0, import_react27.useState)(null);
10135
10441
  const { products, isLoadingProducts, refreshProducts } = useProductLoader();
10136
10442
  const { subscription, hasActiveSubscription, getSubscription, invalidateCache } = useSubscriptionSync();
10137
10443
  const { preloadedWebView, setPreloadedWebView, preloadCampaign, consumePreloadedCampaign, isPreloadValid } = useCampaignPreload();
10138
- const clearPreloadedWebView = (0, import_react25.useCallback)(() => setPreloadedWebView(null), [setPreloadedWebView]);
10139
- const autoPreloadTriggeredRef = (0, import_react25.useRef)(false);
10140
- const preloadedWebViewRef = (0, import_react25.useRef)(preloadedWebView);
10141
- const pollTimerRef = (0, import_react25.useRef)(null);
10142
- const pollCancelledRef = (0, import_react25.useRef)(false);
10143
- const secureStorageRef = (0, import_react25.useRef)(new SecureStorage(config.debug));
10144
- (0, import_react25.useEffect)(() => {
10444
+ const clearPreloadedWebView = (0, import_react27.useCallback)(() => setPreloadedWebView(null), [setPreloadedWebView]);
10445
+ const autoPreloadTriggeredRef = (0, import_react27.useRef)(false);
10446
+ const preloadedWebViewRef = (0, import_react27.useRef)(preloadedWebView);
10447
+ const pollTimerRef = (0, import_react27.useRef)(null);
10448
+ const pollCancelledRef = (0, import_react27.useRef)(false);
10449
+ const secureStorageRef = (0, import_react27.useRef)(new SecureStorage(config.debug));
10450
+ (0, import_react27.useEffect)(() => {
10145
10451
  preloadedWebViewRef.current = preloadedWebView;
10146
10452
  }, [preloadedWebView]);
10147
10453
  useAppAutoEvents({
@@ -10167,22 +10473,22 @@ function PaywalloProvider({ children, config }) {
10167
10473
  },
10168
10474
  debug: config.debug
10169
10475
  });
10170
- const triggerRepreload = (0, import_react25.useCallback)((placement) => {
10476
+ const triggerRepreload = (0, import_react27.useCallback)((placement) => {
10171
10477
  void PaywalloClient.preloadCampaign(placement).catch(() => {
10172
10478
  });
10173
10479
  }, []);
10174
10480
  const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady: baseHandlePreloadedWebViewReady, handlePreloadedClose: baseHandlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, setPreloadedWebView);
10175
- const SCREEN_HEIGHT = (0, import_react25.useMemo)(() => import_react_native27.Dimensions.get("window").height, []);
10176
- const slideAnim = (0, import_react25.useRef)(new import_react_native27.Animated.Value(SCREEN_HEIGHT)).current;
10177
- const handlePreloadedWebViewReady = (0, import_react25.useCallback)(() => {
10481
+ const SCREEN_HEIGHT = (0, import_react27.useMemo)(() => import_react_native29.Dimensions.get("window").height, []);
10482
+ const slideAnim = (0, import_react27.useRef)(new import_react_native29.Animated.Value(SCREEN_HEIGHT)).current;
10483
+ const handlePreloadedWebViewReady = (0, import_react27.useCallback)(() => {
10178
10484
  if (PaywalloClient.getConfig()?.debug) {
10179
10485
  console.log(`[Paywallo WEBVIEW] loaded \u2014 placement: ${preloadedWebView?.placement}`);
10180
10486
  }
10181
10487
  baseHandlePreloadedWebViewReady();
10182
10488
  }, [preloadedWebView?.placement, baseHandlePreloadedWebViewReady]);
10183
- const handlePreloadedClose = (0, import_react25.useCallback)(() => {
10489
+ const handlePreloadedClose = (0, import_react27.useCallback)(() => {
10184
10490
  const placement = preloadedWebView?.placement;
10185
- import_react_native27.Animated.timing(slideAnim, { toValue: SCREEN_HEIGHT, duration: 200, easing: import_react_native27.Easing.in(import_react_native27.Easing.cubic), useNativeDriver: true }).start(() => {
10491
+ import_react_native29.Animated.timing(slideAnim, { toValue: SCREEN_HEIGHT, duration: 200, easing: import_react_native29.Easing.in(import_react_native29.Easing.cubic), useNativeDriver: true }).start(() => {
10186
10492
  baseHandlePreloadedClose();
10187
10493
  if (placement) triggerRepreload(placement);
10188
10494
  });
@@ -10195,8 +10501,8 @@ function PaywalloProvider({ children, config }) {
10195
10501
  presentedAtRef,
10196
10502
  invalidateCache
10197
10503
  );
10198
- const [isPreloadPurchasing, setIsPreloadPurchasing] = (0, import_react25.useState)(false);
10199
- const handlePreloadedPurchase = (0, import_react25.useCallback)(async (productId) => {
10504
+ const [isPreloadPurchasing, setIsPreloadPurchasing] = (0, import_react27.useState)(false);
10505
+ const handlePreloadedPurchase = (0, import_react27.useCallback)(async (productId) => {
10200
10506
  setIsPreloadPurchasing(true);
10201
10507
  try {
10202
10508
  await rawPreloadedPurchase(productId);
@@ -10204,8 +10510,8 @@ function PaywalloProvider({ children, config }) {
10204
10510
  setIsPreloadPurchasing(false);
10205
10511
  }
10206
10512
  }, [rawPreloadedPurchase]);
10207
- const configRef = (0, import_react25.useRef)(config);
10208
- (0, import_react25.useEffect)(() => {
10513
+ const configRef = (0, import_react27.useRef)(config);
10514
+ (0, import_react27.useEffect)(() => {
10209
10515
  let mounted = true;
10210
10516
  initLocalization({ detectDevice: true });
10211
10517
  PaywalloClient.init(configRef.current).then(async () => {
@@ -10218,6 +10524,11 @@ function PaywalloProvider({ children, config }) {
10218
10524
  const placement = await PaywalloClient.waitForAutoPreloadedPlacement();
10219
10525
  if (mounted && placement) void preloadCampaign(placement).catch(() => {
10220
10526
  });
10527
+ const preloadPlacements = configRef.current.preloadPaywalls;
10528
+ if (mounted && preloadPlacements && preloadPlacements.length > 0) {
10529
+ void PaywalloClient.preloadPaywalls(preloadPlacements).catch(() => {
10530
+ });
10531
+ }
10221
10532
  }
10222
10533
  }).catch((err) => {
10223
10534
  if (!mounted) return;
@@ -10227,14 +10538,28 @@ function PaywalloProvider({ children, config }) {
10227
10538
  mounted = false;
10228
10539
  };
10229
10540
  }, [preloadCampaign]);
10230
- const getPaywallConfig = (0, import_react25.useCallback)(async (p) => {
10541
+ const getPaywallConfig = (0, import_react27.useCallback)(async (p) => {
10231
10542
  const api = PaywalloClient.getApiClient();
10232
10543
  return api ? api.getPaywall(p) : null;
10233
10544
  }, []);
10234
- const presentPaywall = (0, import_react25.useCallback)((placement) => {
10235
- return new Promise((resolve) => setActivePaywall({ placement, resolver: resolve }));
10545
+ const presentPaywall = (0, import_react27.useCallback)((placement) => {
10546
+ const preloaded = paywallPreloadService.getPreloaded(placement);
10547
+ return new Promise((resolve) => setActivePaywall({
10548
+ placement,
10549
+ resolver: resolve,
10550
+ ...preloaded && {
10551
+ paywallConfig: {
10552
+ id: preloaded.config.id,
10553
+ placement: preloaded.config.placement,
10554
+ config: preloaded.config.config,
10555
+ primaryProductId: preloaded.config.primaryProductId,
10556
+ secondaryProductId: preloaded.config.secondaryProductId
10557
+ },
10558
+ preloadedProducts: preloaded.products
10559
+ }
10560
+ }));
10236
10561
  }, []);
10237
- const trackCampaignImpression = (0, import_react25.useCallback)((placement, variantKey, campaignId) => {
10562
+ const trackCampaignImpression = (0, import_react27.useCallback)((placement, variantKey, campaignId) => {
10238
10563
  const api = PaywalloClient.getApiClient();
10239
10564
  if (!api) return;
10240
10565
  const sessionId = PaywalloClient.getSessionId();
@@ -10248,7 +10573,7 @@ function PaywalloProvider({ children, config }) {
10248
10573
  }).catch(() => {
10249
10574
  });
10250
10575
  }, []);
10251
- const presentCampaign = (0, import_react25.useCallback)(
10576
+ const presentCampaign = (0, import_react27.useCallback)(
10252
10577
  async (placement, context) => {
10253
10578
  try {
10254
10579
  if (preloadedWebView?.placement === placement && preloadedWebView.loaded && isPreloadValid(placement)) {
@@ -10322,7 +10647,7 @@ function PaywalloProvider({ children, config }) {
10322
10647
  },
10323
10648
  [preloadedWebView, isPreloadValid, consumePreloadedCampaign, presentPreloadedWebView, trackCampaignImpression]
10324
10649
  );
10325
- const restorePurchases = (0, import_react25.useCallback)(async () => {
10650
+ const restorePurchases = (0, import_react27.useCallback)(async () => {
10326
10651
  invalidateCache();
10327
10652
  try {
10328
10653
  const txs = await getIAPService().restore();
@@ -10331,13 +10656,13 @@ function PaywalloProvider({ children, config }) {
10331
10656
  return { success: false, restoredProducts: [], error: error instanceof Error ? error : new PurchaseError(PURCHASE_ERROR_CODES.RESTORE_FAILED, String(error)) };
10332
10657
  }
10333
10658
  }, [invalidateCache]);
10334
- const handlePaywallResult = (0, import_react25.useCallback)((result) => {
10659
+ const handlePaywallResult = (0, import_react27.useCallback)((result) => {
10335
10660
  if (activePaywall) {
10336
10661
  activePaywall.resolver(result);
10337
10662
  setActivePaywall(null);
10338
10663
  }
10339
10664
  }, [activePaywall]);
10340
- const bridgePaywallPresenter = (0, import_react25.useCallback)((placement) => {
10665
+ const bridgePaywallPresenter = (0, import_react27.useCallback)((placement) => {
10341
10666
  if (PaywalloClient.getConfig()?.debug) {
10342
10667
  console.log(`[Paywallo PRESENT] called \u2014 preloaded: ${preloadedWebViewRef.current?.loaded === true} placement: ${placement}`);
10343
10668
  }
@@ -10366,7 +10691,7 @@ function PaywalloProvider({ children, config }) {
10366
10691
  }
10367
10692
  return presentCampaign(placement);
10368
10693
  }, [presentPreloadedWebView, presentCampaign]);
10369
- (0, import_react25.useEffect)(() => {
10694
+ (0, import_react27.useEffect)(() => {
10370
10695
  if (!isInitialized) return;
10371
10696
  const onEmergency = async (id) => {
10372
10697
  try {
@@ -10393,9 +10718,9 @@ function PaywalloProvider({ children, config }) {
10393
10718
  };
10394
10719
  }, [isInitialized, bridgePaywallPresenter, getSubscription, hasActiveSubscription, restorePurchases]);
10395
10720
  const isReady = isInitialized && !isLoadingProducts;
10396
- const noOp = (0, import_react25.useCallback)(() => {
10721
+ const noOp = (0, import_react27.useCallback)(() => {
10397
10722
  }, []);
10398
- const contextValue = (0, import_react25.useMemo)(() => ({
10723
+ const contextValue = (0, import_react27.useMemo)(() => ({
10399
10724
  config: PaywalloClient.getConfig(),
10400
10725
  isInitialized,
10401
10726
  isReady,
@@ -10429,20 +10754,20 @@ function PaywalloProvider({ children, config }) {
10429
10754
  getPaywallConfig,
10430
10755
  refreshProducts
10431
10756
  ]);
10432
- (0, import_react25.useLayoutEffect)(() => {
10757
+ (0, import_react27.useLayoutEffect)(() => {
10433
10758
  if (showingPreloadedWebView) {
10434
10759
  slideAnim.setValue(SCREEN_HEIGHT);
10435
- import_react_native27.Animated.timing(slideAnim, { toValue: 0, duration: 200, easing: import_react_native27.Easing.out(import_react_native27.Easing.cubic), useNativeDriver: true }).start();
10760
+ import_react_native29.Animated.timing(slideAnim, { toValue: 0, duration: 200, easing: import_react_native29.Easing.out(import_react_native29.Easing.cubic), useNativeDriver: true }).start();
10436
10761
  }
10437
10762
  }, [showingPreloadedWebView, slideAnim, SCREEN_HEIGHT]);
10438
10763
  const preloadStyle = [
10439
- styles3.preloadOverlay,
10440
- showingPreloadedWebView ? styles3.visible : styles3.hidden,
10764
+ styles5.preloadOverlay,
10765
+ showingPreloadedWebView ? styles5.visible : styles5.hidden,
10441
10766
  showingPreloadedWebView ? { transform: [{ translateY: slideAnim }] } : void 0
10442
10767
  ];
10443
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(PaywalloContext.Provider, { value: contextValue, children: [
10768
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(PaywalloContext.Provider, { value: contextValue, children: [
10444
10769
  children,
10445
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PaywallErrorBoundary, { children: !!preloadedWebView?.craftData && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native27.Animated.View, { style: preloadStyle, pointerEvents: showingPreloadedWebView ? "auto" : "none", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
10770
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PaywallErrorBoundary, { children: !!preloadedWebView?.craftData && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native29.Animated.View, { style: preloadStyle, pointerEvents: showingPreloadedWebView ? "auto" : "none", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
10446
10771
  PaywallWebView,
10447
10772
  {
10448
10773
  paywallId: preloadedWebView.paywallId,
@@ -10458,7 +10783,7 @@ function PaywalloProvider({ children, config }) {
10458
10783
  onRestore: showingPreloadedWebView ? handlePreloadedRestore : noOp
10459
10784
  }
10460
10785
  ) }) }),
10461
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PaywallErrorBoundary, { children: activePaywall && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
10786
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PaywallErrorBoundary, { children: activePaywall && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
10462
10787
  PaywallModal,
10463
10788
  {
10464
10789
  placement: activePaywall.placement,
@@ -10470,10 +10795,11 @@ function PaywalloProvider({ children, config }) {
10470
10795
  campaignId: activePaywall.campaignId,
10471
10796
  variantId: activePaywall.variantId
10472
10797
  }
10473
- ) })
10798
+ ) }),
10799
+ isInitialized && (config.preloadPaywalls?.length ?? 0) > 0 && PaywalloClient.getWebUrl() !== null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PaywallWebViewPrewarmer, { webUrl: PaywalloClient.getWebUrl() })
10474
10800
  ] });
10475
10801
  }
10476
- var styles3 = import_react_native27.StyleSheet.create({
10802
+ var styles5 = import_react_native29.StyleSheet.create({
10477
10803
  preloadOverlay: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#ffffff" },
10478
10804
  hidden: { zIndex: -1, opacity: 0, width: 1, height: 1, overflow: "hidden" },
10479
10805
  visible: { zIndex: 9999, opacity: 1 }