@virex-tech/paywallo-sdk 2.2.6 → 2.3.0
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.d.mts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +851 -511
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +743 -403
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2370,8 +2370,8 @@ var init_NotificationHandlerSetup = __esm({
|
|
|
2370
2370
|
// src/core/version.ts
|
|
2371
2371
|
function resolveVersion() {
|
|
2372
2372
|
try {
|
|
2373
|
-
if ("2.
|
|
2374
|
-
return "2.
|
|
2373
|
+
if ("2.3.0") {
|
|
2374
|
+
return "2.3.0";
|
|
2375
2375
|
}
|
|
2376
2376
|
} catch {
|
|
2377
2377
|
}
|
|
@@ -3041,6 +3041,41 @@ var init_emitPaywallClosed = __esm({
|
|
|
3041
3041
|
}
|
|
3042
3042
|
});
|
|
3043
3043
|
|
|
3044
|
+
// src/domains/paywall/extractFirstPageBackground.ts
|
|
3045
|
+
function extractFirstPageBackground(craftData) {
|
|
3046
|
+
if (!craftData) return DEFAULT_BACKGROUND;
|
|
3047
|
+
try {
|
|
3048
|
+
const data = JSON.parse(craftData);
|
|
3049
|
+
const nodes = { ...data.nodes, ...data.sharedNodes };
|
|
3050
|
+
const rootId = data.rootId;
|
|
3051
|
+
if (!rootId || !nodes[rootId]) return DEFAULT_BACKGROUND;
|
|
3052
|
+
const firstPageId = nodes[rootId].children?.[0];
|
|
3053
|
+
if (!firstPageId) return DEFAULT_BACKGROUND;
|
|
3054
|
+
const bg = nodes[firstPageId]?.props?.backgroundColor;
|
|
3055
|
+
return typeof bg === "string" && bg.length > 0 && bg !== "transparent" ? bg : DEFAULT_BACKGROUND;
|
|
3056
|
+
} catch {
|
|
3057
|
+
return DEFAULT_BACKGROUND;
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
function isColorDark(color) {
|
|
3061
|
+
const hex = color.replace("#", "").trim();
|
|
3062
|
+
const normalized = hex.length === 3 ? hex.split("").map((c) => c + c).join("") : hex;
|
|
3063
|
+
if (normalized.length !== 6) return false;
|
|
3064
|
+
const r = parseInt(normalized.slice(0, 2), 16);
|
|
3065
|
+
const g = parseInt(normalized.slice(2, 4), 16);
|
|
3066
|
+
const b = parseInt(normalized.slice(4, 6), 16);
|
|
3067
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return false;
|
|
3068
|
+
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
3069
|
+
return luminance < 0.5;
|
|
3070
|
+
}
|
|
3071
|
+
var DEFAULT_BACKGROUND;
|
|
3072
|
+
var init_extractFirstPageBackground = __esm({
|
|
3073
|
+
"src/domains/paywall/extractFirstPageBackground.ts"() {
|
|
3074
|
+
"use strict";
|
|
3075
|
+
DEFAULT_BACKGROUND = "#FFFFFF";
|
|
3076
|
+
}
|
|
3077
|
+
});
|
|
3078
|
+
|
|
3044
3079
|
// src/domains/paywall/parseWebViewMessage.ts
|
|
3045
3080
|
function deriveMessageId(message) {
|
|
3046
3081
|
if (message.id) return message.id;
|
|
@@ -3283,6 +3318,66 @@ var init_localization = __esm({
|
|
|
3283
3318
|
}
|
|
3284
3319
|
});
|
|
3285
3320
|
|
|
3321
|
+
// src/domains/paywall/PaywallSkeleton.tsx
|
|
3322
|
+
import { useEffect, useRef } from "react";
|
|
3323
|
+
import { Animated, StyleSheet, View } from "react-native";
|
|
3324
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3325
|
+
function SkeletonBlock({ style, color, opacity }) {
|
|
3326
|
+
return /* @__PURE__ */ jsx(Animated.View, { style: [style, { backgroundColor: color, opacity }] });
|
|
3327
|
+
}
|
|
3328
|
+
function PaywallSkeleton({ backgroundColor = "#FFFFFF" }) {
|
|
3329
|
+
const pulse = useRef(new Animated.Value(0.4)).current;
|
|
3330
|
+
useEffect(() => {
|
|
3331
|
+
const loop = Animated.loop(
|
|
3332
|
+
Animated.sequence([
|
|
3333
|
+
Animated.timing(pulse, { toValue: 1, duration: 700, useNativeDriver: true }),
|
|
3334
|
+
Animated.timing(pulse, { toValue: 0.4, duration: 700, useNativeDriver: true })
|
|
3335
|
+
])
|
|
3336
|
+
);
|
|
3337
|
+
loop.start();
|
|
3338
|
+
return () => loop.stop();
|
|
3339
|
+
}, [pulse]);
|
|
3340
|
+
const placeholder = isColorDark(backgroundColor) ? "rgba(255,255,255,0.13)" : "rgba(0,0,0,0.07)";
|
|
3341
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.container, { backgroundColor }], pointerEvents: "none", children: [
|
|
3342
|
+
/* @__PURE__ */ jsx(View, { style: styles.header, children: /* @__PURE__ */ jsx(SkeletonBlock, { style: styles.logo, color: placeholder, opacity: pulse }) }),
|
|
3343
|
+
/* @__PURE__ */ jsx(SkeletonBlock, { style: styles.hero, color: placeholder, opacity: pulse }),
|
|
3344
|
+
/* @__PURE__ */ jsxs(View, { style: styles.titleGroup, children: [
|
|
3345
|
+
/* @__PURE__ */ jsx(SkeletonBlock, { style: styles.titleLine, color: placeholder, opacity: pulse }),
|
|
3346
|
+
/* @__PURE__ */ jsx(SkeletonBlock, { style: styles.titleLineShort, color: placeholder, opacity: pulse })
|
|
3347
|
+
] }),
|
|
3348
|
+
/* @__PURE__ */ jsxs(View, { style: styles.cardsRow, children: [
|
|
3349
|
+
/* @__PURE__ */ jsx(SkeletonBlock, { style: styles.card, color: placeholder, opacity: pulse }),
|
|
3350
|
+
/* @__PURE__ */ jsx(SkeletonBlock, { style: styles.card, color: placeholder, opacity: pulse })
|
|
3351
|
+
] }),
|
|
3352
|
+
/* @__PURE__ */ jsxs(View, { style: styles.footerGroup, children: [
|
|
3353
|
+
/* @__PURE__ */ jsx(SkeletonBlock, { style: styles.cta, color: placeholder, opacity: pulse }),
|
|
3354
|
+
/* @__PURE__ */ jsx(SkeletonBlock, { style: styles.footer, color: placeholder, opacity: pulse })
|
|
3355
|
+
] })
|
|
3356
|
+
] });
|
|
3357
|
+
}
|
|
3358
|
+
var RADIUS, styles;
|
|
3359
|
+
var init_PaywallSkeleton = __esm({
|
|
3360
|
+
"src/domains/paywall/PaywallSkeleton.tsx"() {
|
|
3361
|
+
"use strict";
|
|
3362
|
+
init_extractFirstPageBackground();
|
|
3363
|
+
RADIUS = 14;
|
|
3364
|
+
styles = StyleSheet.create({
|
|
3365
|
+
card: { borderRadius: RADIUS, flex: 1, height: 120 },
|
|
3366
|
+
cardsRow: { flexDirection: "row", gap: 12, marginTop: "auto", paddingHorizontal: 24 },
|
|
3367
|
+
container: { flex: 1, paddingBottom: 40, paddingTop: 64 },
|
|
3368
|
+
cta: { borderRadius: 30, height: 58, marginHorizontal: 24 },
|
|
3369
|
+
footer: { alignSelf: "center", borderRadius: 6, height: 12, marginTop: 20, width: 180 },
|
|
3370
|
+
footerGroup: { marginTop: 28 },
|
|
3371
|
+
header: { alignItems: "center", paddingVertical: 12 },
|
|
3372
|
+
hero: { alignSelf: "center", borderRadius: 20, height: 220, marginTop: 16, width: "70%" },
|
|
3373
|
+
logo: { borderRadius: 6, height: 22, width: 110 },
|
|
3374
|
+
titleGroup: { alignItems: "center", gap: 12, marginTop: 28, paddingHorizontal: 24 },
|
|
3375
|
+
titleLine: { borderRadius: 8, height: 26, width: "85%" },
|
|
3376
|
+
titleLineShort: { borderRadius: 8, height: 26, width: "60%" }
|
|
3377
|
+
});
|
|
3378
|
+
}
|
|
3379
|
+
});
|
|
3380
|
+
|
|
3286
3381
|
// src/domains/paywall/paywallScripts.ts
|
|
3287
3382
|
function buildPaywallInjectionScript(data) {
|
|
3288
3383
|
const paywallData = {
|
|
@@ -3292,6 +3387,7 @@ function buildPaywallInjectionScript(data) {
|
|
|
3292
3387
|
products: data.products,
|
|
3293
3388
|
primaryProductId: data.primaryProductId,
|
|
3294
3389
|
secondaryProductId: data.secondaryProductId,
|
|
3390
|
+
tertiaryProductId: data.tertiaryProductId,
|
|
3295
3391
|
currentLanguage: getCurrentLanguage(),
|
|
3296
3392
|
defaultLanguage: getDefaultLanguage()
|
|
3297
3393
|
}
|
|
@@ -3327,7 +3423,7 @@ var init_paywallScripts = __esm({
|
|
|
3327
3423
|
});
|
|
3328
3424
|
|
|
3329
3425
|
// src/domains/paywall/PaywallWebView.tsx
|
|
3330
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3426
|
+
import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState } from "react";
|
|
3331
3427
|
import {
|
|
3332
3428
|
BackHandler,
|
|
3333
3429
|
findNodeHandle,
|
|
@@ -3336,11 +3432,11 @@ import {
|
|
|
3336
3432
|
NativeModules as NativeModules6,
|
|
3337
3433
|
Platform as Platform9,
|
|
3338
3434
|
requireNativeComponent,
|
|
3339
|
-
StyleSheet,
|
|
3435
|
+
StyleSheet as StyleSheet2,
|
|
3340
3436
|
UIManager,
|
|
3341
|
-
View
|
|
3437
|
+
View as View2
|
|
3342
3438
|
} from "react-native";
|
|
3343
|
-
import { jsx } from "react/jsx-runtime";
|
|
3439
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
3344
3440
|
function getNativeWebView() {
|
|
3345
3441
|
if (NativeWebViewComponent) return NativeWebViewComponent;
|
|
3346
3442
|
if (nativeWebViewLookupFailed) return null;
|
|
@@ -3390,6 +3486,7 @@ function PaywallWebView({
|
|
|
3390
3486
|
products,
|
|
3391
3487
|
primaryProductId,
|
|
3392
3488
|
secondaryProductId,
|
|
3489
|
+
tertiaryProductId,
|
|
3393
3490
|
isPurchasing,
|
|
3394
3491
|
webUrl: webUrlProp,
|
|
3395
3492
|
onPurchase,
|
|
@@ -3398,16 +3495,16 @@ function PaywallWebView({
|
|
|
3398
3495
|
onReady,
|
|
3399
3496
|
onError
|
|
3400
3497
|
}) {
|
|
3401
|
-
const paywallDataSent =
|
|
3402
|
-
const loadEndReceived =
|
|
3498
|
+
const paywallDataSent = useRef2(false);
|
|
3499
|
+
const loadEndReceived = useRef2(false);
|
|
3403
3500
|
const [scriptToInject, setScriptToInject] = useState(void 0);
|
|
3404
|
-
const webViewRef =
|
|
3405
|
-
const onErrorRef =
|
|
3501
|
+
const webViewRef = useRef2(null);
|
|
3502
|
+
const onErrorRef = useRef2(onError);
|
|
3406
3503
|
onErrorRef.current = onError;
|
|
3407
|
-
const onReadyRef =
|
|
3504
|
+
const onReadyRef = useRef2(onReady);
|
|
3408
3505
|
onReadyRef.current = onReady;
|
|
3409
3506
|
const NativeWebView = useMemo(() => getNativeWebView(), []);
|
|
3410
|
-
|
|
3507
|
+
useEffect2(() => {
|
|
3411
3508
|
if (!NativeWebView) {
|
|
3412
3509
|
onErrorRef.current?.({
|
|
3413
3510
|
code: -1,
|
|
@@ -3418,7 +3515,7 @@ function PaywallWebView({
|
|
|
3418
3515
|
const [resolvedWebUrl, setResolvedWebUrl] = useState(
|
|
3419
3516
|
() => webUrlProp ?? PaywalloClient.getWebUrl()
|
|
3420
3517
|
);
|
|
3421
|
-
|
|
3518
|
+
useEffect2(() => {
|
|
3422
3519
|
if (webUrlProp !== void 0) {
|
|
3423
3520
|
setResolvedWebUrl(webUrlProp ?? null);
|
|
3424
3521
|
return;
|
|
@@ -3437,7 +3534,7 @@ function PaywallWebView({
|
|
|
3437
3534
|
}, 200);
|
|
3438
3535
|
return () => clearInterval(interval);
|
|
3439
3536
|
}, [webUrlProp]);
|
|
3440
|
-
|
|
3537
|
+
useEffect2(() => {
|
|
3441
3538
|
if (!resolvedWebUrl) {
|
|
3442
3539
|
onErrorRef.current?.({
|
|
3443
3540
|
code: -1,
|
|
@@ -3461,13 +3558,14 @@ function PaywallWebView({
|
|
|
3461
3558
|
craftData,
|
|
3462
3559
|
products: Array.from(products.values()),
|
|
3463
3560
|
primaryProductId,
|
|
3464
|
-
secondaryProductId
|
|
3561
|
+
secondaryProductId,
|
|
3562
|
+
tertiaryProductId
|
|
3465
3563
|
});
|
|
3466
3564
|
injectScript(script);
|
|
3467
|
-
}, [craftData, products, primaryProductId, secondaryProductId, injectScript]);
|
|
3468
|
-
const processedIdsRef =
|
|
3469
|
-
const dedupeTimersRef =
|
|
3470
|
-
|
|
3565
|
+
}, [craftData, products, primaryProductId, secondaryProductId, tertiaryProductId, injectScript]);
|
|
3566
|
+
const processedIdsRef = useRef2(/* @__PURE__ */ new Set());
|
|
3567
|
+
const dedupeTimersRef = useRef2(/* @__PURE__ */ new Map());
|
|
3568
|
+
useEffect2(() => {
|
|
3471
3569
|
return () => {
|
|
3472
3570
|
for (const timerId of dedupeTimersRef.current.values()) clearTimeout(timerId);
|
|
3473
3571
|
dedupeTimersRef.current.clear();
|
|
@@ -3519,7 +3617,7 @@ function PaywallWebView({
|
|
|
3519
3617
|
},
|
|
3520
3618
|
[buildAndSetInjectionScript, onPurchase, onClose, onRestore]
|
|
3521
3619
|
);
|
|
3522
|
-
|
|
3620
|
+
useEffect2(() => {
|
|
3523
3621
|
const emitter = getWebViewEmitter();
|
|
3524
3622
|
if (!emitter) return;
|
|
3525
3623
|
const loadEndSub = emitter.addListener("PaywalloWebViewLoadEnd", () => {
|
|
@@ -3544,7 +3642,7 @@ function PaywallWebView({
|
|
|
3544
3642
|
errorSub.remove();
|
|
3545
3643
|
};
|
|
3546
3644
|
}, [buildAndSetInjectionScript, handleParsedMessage]);
|
|
3547
|
-
|
|
3645
|
+
useEffect2(() => {
|
|
3548
3646
|
const timer = setTimeout(() => {
|
|
3549
3647
|
if (!paywallDataSent.current) {
|
|
3550
3648
|
onErrorRef.current?.({
|
|
@@ -3584,11 +3682,11 @@ function PaywallWebView({
|
|
|
3584
3682
|
},
|
|
3585
3683
|
[handleParsedMessage]
|
|
3586
3684
|
);
|
|
3587
|
-
|
|
3685
|
+
useEffect2(() => {
|
|
3588
3686
|
if (!paywallDataSent.current) return;
|
|
3589
3687
|
injectScript(buildPurchaseStateScript(isPurchasing));
|
|
3590
3688
|
}, [isPurchasing, injectScript]);
|
|
3591
|
-
|
|
3689
|
+
useEffect2(() => {
|
|
3592
3690
|
if (Platform9.OS !== "android") return;
|
|
3593
3691
|
const onBackPress = () => {
|
|
3594
3692
|
if (!paywallDataSent.current) return false;
|
|
@@ -3600,7 +3698,7 @@ function PaywallWebView({
|
|
|
3600
3698
|
}, [injectScript]);
|
|
3601
3699
|
if (!resolvedWebUrl || !NativeWebView) return null;
|
|
3602
3700
|
const paywallUrl = `${resolvedWebUrl}/paywall/${paywallId}`;
|
|
3603
|
-
return /* @__PURE__ */
|
|
3701
|
+
return /* @__PURE__ */ jsx2(View2, { style: styles2.container, children: /* @__PURE__ */ jsx2(
|
|
3604
3702
|
NativeWebView,
|
|
3605
3703
|
{
|
|
3606
3704
|
ref: webViewRef,
|
|
@@ -3609,11 +3707,11 @@ function PaywallWebView({
|
|
|
3609
3707
|
onMessage: handleMessage,
|
|
3610
3708
|
onLoadEnd: handleLoadEnd,
|
|
3611
3709
|
onError: handleNativeError,
|
|
3612
|
-
style:
|
|
3710
|
+
style: styles2.webview
|
|
3613
3711
|
}
|
|
3614
3712
|
) });
|
|
3615
3713
|
}
|
|
3616
|
-
var NativeWebViewComponent, nativeWebViewLookupFailed, webViewEmitter,
|
|
3714
|
+
var NativeWebViewComponent, nativeWebViewLookupFailed, webViewEmitter, styles2;
|
|
3617
3715
|
var init_PaywallWebView = __esm({
|
|
3618
3716
|
"src/domains/paywall/PaywallWebView.tsx"() {
|
|
3619
3717
|
"use strict";
|
|
@@ -3623,7 +3721,7 @@ var init_PaywallWebView = __esm({
|
|
|
3623
3721
|
NativeWebViewComponent = null;
|
|
3624
3722
|
nativeWebViewLookupFailed = false;
|
|
3625
3723
|
webViewEmitter = null;
|
|
3626
|
-
|
|
3724
|
+
styles2 = StyleSheet2.create({
|
|
3627
3725
|
container: { flex: 1 },
|
|
3628
3726
|
webview: { flex: 1, backgroundColor: "transparent" }
|
|
3629
3727
|
});
|
|
@@ -5264,21 +5362,21 @@ var init_usePaywallTracking = __esm({
|
|
|
5264
5362
|
});
|
|
5265
5363
|
|
|
5266
5364
|
// src/domains/paywall/usePaywallActions.ts
|
|
5267
|
-
import { useCallback as useCallback3, useEffect as
|
|
5268
|
-
import { Animated, AppState as AppState3, Dimensions as Dimensions2 } from "react-native";
|
|
5365
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState2 } from "react";
|
|
5366
|
+
import { Animated as Animated2, AppState as AppState3, Dimensions as Dimensions2 } from "react-native";
|
|
5269
5367
|
function usePaywallActions(placement, paywallConfig, onResult, variantKey, campaignId, openAnim, variantId) {
|
|
5270
5368
|
const [isPurchasing, setIsPurchasing] = useState2(false);
|
|
5271
|
-
const isPurchasingRef =
|
|
5369
|
+
const isPurchasingRef = useRef3(false);
|
|
5272
5370
|
const [isClosing, setIsClosing] = useState2(false);
|
|
5273
|
-
const slideAnim =
|
|
5274
|
-
const presentedAtRef =
|
|
5275
|
-
const closedRef =
|
|
5371
|
+
const slideAnim = useRef3(new Animated2.Value(0)).current;
|
|
5372
|
+
const presentedAtRef = useRef3(Date.now());
|
|
5373
|
+
const closedRef = useRef3(false);
|
|
5276
5374
|
const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
|
|
5277
5375
|
const closeWithReason = useCallback3((reason, resultOverride) => {
|
|
5278
5376
|
if (closedRef.current) return;
|
|
5279
5377
|
closedRef.current = true;
|
|
5280
5378
|
const animTarget = openAnim ?? slideAnim;
|
|
5281
|
-
|
|
5379
|
+
Animated2.timing(animTarget, {
|
|
5282
5380
|
toValue: Dimensions2.get("window").height,
|
|
5283
5381
|
duration: 350,
|
|
5284
5382
|
useNativeDriver: true
|
|
@@ -5298,7 +5396,7 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
|
|
|
5298
5396
|
setIsClosing(true);
|
|
5299
5397
|
closeWithReason("dismissed");
|
|
5300
5398
|
}, [isClosing, closeWithReason]);
|
|
5301
|
-
|
|
5399
|
+
useEffect3(() => {
|
|
5302
5400
|
const sub = AppState3.addEventListener("change", (nextState) => {
|
|
5303
5401
|
if (isPurchasingRef.current) return;
|
|
5304
5402
|
if (nextState === "background" || nextState === "inactive") {
|
|
@@ -5307,7 +5405,7 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
|
|
|
5307
5405
|
});
|
|
5308
5406
|
return () => sub.remove();
|
|
5309
5407
|
}, [closeWithReason]);
|
|
5310
|
-
|
|
5408
|
+
useEffect3(() => {
|
|
5311
5409
|
const timer = setTimeout(() => {
|
|
5312
5410
|
closeWithReason("timeout");
|
|
5313
5411
|
}, PAYWALL_TIMEOUT_MS);
|
|
@@ -5378,17 +5476,17 @@ var init_usePaywallActions = __esm({
|
|
|
5378
5476
|
});
|
|
5379
5477
|
|
|
5380
5478
|
// src/domains/paywall/usePaywallLoader.ts
|
|
5381
|
-
import { useEffect as
|
|
5479
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState3 } from "react";
|
|
5382
5480
|
function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts, variantKey, campaignId, variantId) {
|
|
5383
5481
|
const [error, setError] = useState3(null);
|
|
5384
5482
|
const [paywallConfig, setPaywallConfig] = useState3(null);
|
|
5385
5483
|
const [products, setProducts] = useState3(/* @__PURE__ */ new Map());
|
|
5386
|
-
const preloadedConfigRef =
|
|
5484
|
+
const preloadedConfigRef = useRef4(preloadedConfig);
|
|
5387
5485
|
preloadedConfigRef.current = preloadedConfig;
|
|
5388
|
-
const preloadedProductsRef =
|
|
5486
|
+
const preloadedProductsRef = useRef4(preloadedProducts);
|
|
5389
5487
|
preloadedProductsRef.current = preloadedProducts;
|
|
5390
|
-
const trackingDoneRef =
|
|
5391
|
-
|
|
5488
|
+
const trackingDoneRef = useRef4(false);
|
|
5489
|
+
useEffect4(() => {
|
|
5392
5490
|
if (!visible) {
|
|
5393
5491
|
trackingDoneRef.current = false;
|
|
5394
5492
|
return;
|
|
@@ -5407,7 +5505,8 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
|
|
|
5407
5505
|
placement: snap.placement,
|
|
5408
5506
|
config: snap.config,
|
|
5409
5507
|
primaryProductId: snap.primaryProductId,
|
|
5410
|
-
secondaryProductId: snap.secondaryProductId
|
|
5508
|
+
secondaryProductId: snap.secondaryProductId,
|
|
5509
|
+
tertiaryProductId: snap.tertiaryProductId
|
|
5411
5510
|
};
|
|
5412
5511
|
} else {
|
|
5413
5512
|
const fetched = await apiClient.getPaywall(placement);
|
|
@@ -5419,6 +5518,7 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
|
|
|
5419
5518
|
const productIds = [];
|
|
5420
5519
|
if (config.primaryProductId) productIds.push(config.primaryProductId);
|
|
5421
5520
|
if (config.secondaryProductId) productIds.push(config.secondaryProductId);
|
|
5521
|
+
if (config.tertiaryProductId) productIds.push(config.tertiaryProductId);
|
|
5422
5522
|
const snapProducts = preloadedProductsRef.current;
|
|
5423
5523
|
if (snapProducts && snapProducts.size > 0) {
|
|
5424
5524
|
if (!cancelled) setProducts(snapProducts);
|
|
@@ -5483,28 +5583,28 @@ var init_usePaywallLoader = __esm({
|
|
|
5483
5583
|
});
|
|
5484
5584
|
|
|
5485
5585
|
// src/domains/paywall/PaywallModal.tsx
|
|
5486
|
-
import { useCallback as useCallback4, useEffect as
|
|
5586
|
+
import { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef5, useState as useState4 } from "react";
|
|
5487
5587
|
import {
|
|
5488
|
-
Animated as
|
|
5588
|
+
Animated as Animated3,
|
|
5489
5589
|
Dimensions as Dimensions3,
|
|
5490
5590
|
Easing,
|
|
5491
5591
|
Modal,
|
|
5492
5592
|
Pressable,
|
|
5493
|
-
StyleSheet as
|
|
5593
|
+
StyleSheet as StyleSheet3,
|
|
5494
5594
|
Text,
|
|
5495
|
-
View as
|
|
5595
|
+
View as View3
|
|
5496
5596
|
} from "react-native";
|
|
5497
|
-
import { Fragment, jsx as
|
|
5597
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
5498
5598
|
function ErrorFallback({ description, onRetry, onClose }) {
|
|
5499
5599
|
const overrides = PaywalloClient.getConfig()?.errorStrings;
|
|
5500
5600
|
const title = overrides?.title ?? getSdkString("paywallErrorTitle");
|
|
5501
5601
|
const retryLabel = overrides?.retry ?? getSdkString("paywallRetryButton");
|
|
5502
5602
|
const closeLabel = overrides?.close ?? getSdkString("paywallCloseButton");
|
|
5503
|
-
return /* @__PURE__ */
|
|
5504
|
-
/* @__PURE__ */
|
|
5505
|
-
/* @__PURE__ */
|
|
5506
|
-
/* @__PURE__ */
|
|
5507
|
-
/* @__PURE__ */
|
|
5603
|
+
return /* @__PURE__ */ jsx3(View3, { style: styles3.fallbackContainer, children: /* @__PURE__ */ jsxs2(View3, { style: styles3.fallbackCard, children: [
|
|
5604
|
+
/* @__PURE__ */ jsx3(Text, { style: styles3.fallbackTitle, children: title }),
|
|
5605
|
+
/* @__PURE__ */ jsx3(Text, { style: styles3.fallbackDescription, children: description }),
|
|
5606
|
+
/* @__PURE__ */ jsx3(Pressable, { style: styles3.retryButton, onPress: onRetry, children: /* @__PURE__ */ jsx3(Text, { style: styles3.retryButtonText, children: retryLabel }) }),
|
|
5607
|
+
/* @__PURE__ */ jsx3(Pressable, { style: styles3.closeButton, onPress: onClose, children: /* @__PURE__ */ jsx3(Text, { style: styles3.closeButtonText, children: closeLabel }) })
|
|
5508
5608
|
] }) });
|
|
5509
5609
|
}
|
|
5510
5610
|
function PaywallModal({
|
|
@@ -5527,7 +5627,7 @@ function PaywallModal({
|
|
|
5527
5627
|
variantId
|
|
5528
5628
|
);
|
|
5529
5629
|
const screenHeight = Dimensions3.get("window").height;
|
|
5530
|
-
const openAnim =
|
|
5630
|
+
const openAnim = useRef5(new Animated3.Value(screenHeight)).current;
|
|
5531
5631
|
const [modalVisible, setModalVisible] = useState4(false);
|
|
5532
5632
|
const handleResult = useCallback4((result) => {
|
|
5533
5633
|
setModalVisible(false);
|
|
@@ -5536,7 +5636,9 @@ function PaywallModal({
|
|
|
5536
5636
|
const { isPurchasing, slideAnim, handleClose, handlePurchase, handleRestore } = usePaywallActions(placement, paywallConfig, handleResult, variantKey, campaignId, openAnim, variantId);
|
|
5537
5637
|
const [webViewError, setWebViewError] = useState4(null);
|
|
5538
5638
|
const [retryCount, setRetryCount] = useState4(0);
|
|
5539
|
-
const
|
|
5639
|
+
const [showSkeleton, setShowSkeleton] = useState4(true);
|
|
5640
|
+
const skeletonOpacity = useRef5(new Animated3.Value(1)).current;
|
|
5641
|
+
const retryCountRef = useRef5(0);
|
|
5540
5642
|
const handleWebViewError = useCallback4(
|
|
5541
5643
|
(err) => {
|
|
5542
5644
|
if (retryCountRef.current >= 1) {
|
|
@@ -5552,21 +5654,39 @@ function PaywallModal({
|
|
|
5552
5654
|
retryCountRef.current = 0;
|
|
5553
5655
|
setWebViewError(null);
|
|
5554
5656
|
setRetryCount(0);
|
|
5555
|
-
|
|
5556
|
-
|
|
5657
|
+
setShowSkeleton(true);
|
|
5658
|
+
skeletonOpacity.setValue(1);
|
|
5659
|
+
}, [skeletonOpacity]);
|
|
5660
|
+
const handleWebViewReady = useCallback4(() => {
|
|
5661
|
+
Animated3.timing(skeletonOpacity, {
|
|
5662
|
+
toValue: 0,
|
|
5663
|
+
duration: 250,
|
|
5664
|
+
easing: Easing.out(Easing.cubic),
|
|
5665
|
+
useNativeDriver: true
|
|
5666
|
+
}).start(() => setShowSkeleton(false));
|
|
5667
|
+
}, [skeletonOpacity]);
|
|
5668
|
+
useEffect5(() => {
|
|
5669
|
+
if (!visible || !showSkeleton) return;
|
|
5670
|
+
const timer = setTimeout(handleWebViewReady, 8e3);
|
|
5671
|
+
return () => clearTimeout(timer);
|
|
5672
|
+
}, [visible, showSkeleton, handleWebViewReady]);
|
|
5673
|
+
useEffect5(() => {
|
|
5557
5674
|
if (visible) {
|
|
5558
5675
|
setModalVisible(true);
|
|
5676
|
+
setShowSkeleton(true);
|
|
5677
|
+
skeletonOpacity.setValue(1);
|
|
5559
5678
|
openAnim.setValue(Dimensions3.get("window").height);
|
|
5560
|
-
|
|
5679
|
+
Animated3.timing(openAnim, {
|
|
5561
5680
|
toValue: 0,
|
|
5562
5681
|
duration: 550,
|
|
5563
5682
|
easing: Easing.out(Easing.cubic),
|
|
5564
5683
|
useNativeDriver: true
|
|
5565
5684
|
}).start();
|
|
5566
5685
|
}
|
|
5567
|
-
}, [visible, openAnim]);
|
|
5686
|
+
}, [visible, openAnim, skeletonOpacity]);
|
|
5568
5687
|
if (!modalVisible && !visible) return null;
|
|
5569
|
-
|
|
5688
|
+
const skeletonBackground = paywallConfig && "craftData" in paywallConfig.config ? extractFirstPageBackground(paywallConfig.config.craftData) : "#FFFFFF";
|
|
5689
|
+
return /* @__PURE__ */ jsx3(
|
|
5570
5690
|
Modal,
|
|
5571
5691
|
{
|
|
5572
5692
|
visible: modalVisible,
|
|
@@ -5575,16 +5695,16 @@ function PaywallModal({
|
|
|
5575
5695
|
onRequestClose: handleClose,
|
|
5576
5696
|
transparent: true,
|
|
5577
5697
|
statusBarTranslucent: true,
|
|
5578
|
-
children: /* @__PURE__ */
|
|
5579
|
-
error && /* @__PURE__ */
|
|
5580
|
-
!error && paywallConfig && "craftData" in paywallConfig.config && /* @__PURE__ */
|
|
5698
|
+
children: /* @__PURE__ */ jsx3(View3, { style: styles3.overlay, children: /* @__PURE__ */ jsx3(Animated3.View, { style: [styles3.animatedContainer, { transform: [{ translateY: openAnim }] }], children: /* @__PURE__ */ jsxs2(View3, { style: styles3.container, children: [
|
|
5699
|
+
error && /* @__PURE__ */ jsx3(View3, { style: styles3.errorContainer, children: /* @__PURE__ */ jsx3(Text, { style: styles3.errorText, children: error.message }) }),
|
|
5700
|
+
!error && paywallConfig && "craftData" in paywallConfig.config && /* @__PURE__ */ jsx3(Fragment, { children: webViewError ? /* @__PURE__ */ jsx3(
|
|
5581
5701
|
ErrorFallback,
|
|
5582
5702
|
{
|
|
5583
5703
|
description: webViewError.description,
|
|
5584
5704
|
onRetry: handleRetry,
|
|
5585
5705
|
onClose: handleClose
|
|
5586
5706
|
}
|
|
5587
|
-
) : /* @__PURE__ */
|
|
5707
|
+
) : /* @__PURE__ */ jsx3(
|
|
5588
5708
|
PaywallWebView,
|
|
5589
5709
|
{
|
|
5590
5710
|
paywallId: paywallConfig.id,
|
|
@@ -5592,28 +5712,40 @@ function PaywallModal({
|
|
|
5592
5712
|
products,
|
|
5593
5713
|
primaryProductId: paywallConfig.primaryProductId,
|
|
5594
5714
|
secondaryProductId: paywallConfig.secondaryProductId,
|
|
5715
|
+
tertiaryProductId: paywallConfig.tertiaryProductId,
|
|
5595
5716
|
isPurchasing,
|
|
5717
|
+
onReady: handleWebViewReady,
|
|
5596
5718
|
onClose: handleClose,
|
|
5597
5719
|
onPurchase: handlePurchase,
|
|
5598
5720
|
onRestore: handleRestore,
|
|
5599
5721
|
onError: handleWebViewError
|
|
5600
5722
|
},
|
|
5601
5723
|
retryCount
|
|
5602
|
-
) })
|
|
5724
|
+
) }),
|
|
5725
|
+
showSkeleton && !error && !webViewError && /* @__PURE__ */ jsx3(
|
|
5726
|
+
Animated3.View,
|
|
5727
|
+
{
|
|
5728
|
+
style: [StyleSheet3.absoluteFill, { opacity: skeletonOpacity }],
|
|
5729
|
+
pointerEvents: "none",
|
|
5730
|
+
children: /* @__PURE__ */ jsx3(PaywallSkeleton, { backgroundColor: skeletonBackground })
|
|
5731
|
+
}
|
|
5732
|
+
)
|
|
5603
5733
|
] }) }) })
|
|
5604
5734
|
}
|
|
5605
5735
|
);
|
|
5606
5736
|
}
|
|
5607
|
-
var
|
|
5737
|
+
var styles3;
|
|
5608
5738
|
var init_PaywallModal = __esm({
|
|
5609
5739
|
"src/domains/paywall/PaywallModal.tsx"() {
|
|
5610
5740
|
"use strict";
|
|
5611
5741
|
init_PaywalloClient();
|
|
5612
5742
|
init_localization();
|
|
5743
|
+
init_extractFirstPageBackground();
|
|
5744
|
+
init_PaywallSkeleton();
|
|
5613
5745
|
init_PaywallWebView();
|
|
5614
5746
|
init_usePaywallActions();
|
|
5615
5747
|
init_usePaywallLoader();
|
|
5616
|
-
|
|
5748
|
+
styles3 = StyleSheet3.create({
|
|
5617
5749
|
overlay: { flex: 1, backgroundColor: "rgba(0,0,0,0.3)" },
|
|
5618
5750
|
animatedContainer: { flex: 1, backgroundColor: "#fff" },
|
|
5619
5751
|
container: { flex: 1, backgroundColor: "#fff" },
|
|
@@ -5669,13 +5801,177 @@ var init_PaywallModal = __esm({
|
|
|
5669
5801
|
}
|
|
5670
5802
|
});
|
|
5671
5803
|
|
|
5804
|
+
// src/domains/paywall/PaywallPreloadService.ts
|
|
5805
|
+
var PRELOAD_TTL_MS, STALE_THRESHOLD, PaywallPreloadService, paywallPreloadService;
|
|
5806
|
+
var init_PaywallPreloadService = __esm({
|
|
5807
|
+
"src/domains/paywall/PaywallPreloadService.ts"() {
|
|
5808
|
+
"use strict";
|
|
5809
|
+
init_PaywalloClient();
|
|
5810
|
+
init_IAPService();
|
|
5811
|
+
init_PaywallError();
|
|
5812
|
+
PRELOAD_TTL_MS = 5 * 60 * 1e3;
|
|
5813
|
+
STALE_THRESHOLD = 0.75;
|
|
5814
|
+
PaywallPreloadService = class {
|
|
5815
|
+
constructor() {
|
|
5816
|
+
this.preloaded = /* @__PURE__ */ new Map();
|
|
5817
|
+
this.activePreloads = /* @__PURE__ */ new Map();
|
|
5818
|
+
this.apiClientRef = null;
|
|
5819
|
+
}
|
|
5820
|
+
async preload(apiClient, placement) {
|
|
5821
|
+
this.apiClientRef = apiClient;
|
|
5822
|
+
const existing = this.activePreloads.get(placement);
|
|
5823
|
+
if (existing) return existing;
|
|
5824
|
+
const promise = this.doPreload(apiClient, placement);
|
|
5825
|
+
this.activePreloads.set(placement, promise);
|
|
5826
|
+
try {
|
|
5827
|
+
return await promise;
|
|
5828
|
+
} finally {
|
|
5829
|
+
this.activePreloads.delete(placement);
|
|
5830
|
+
}
|
|
5831
|
+
}
|
|
5832
|
+
async preloadMany(apiClient, placements) {
|
|
5833
|
+
this.apiClientRef = apiClient;
|
|
5834
|
+
if (placements.length === 0) return;
|
|
5835
|
+
const debug = PaywalloClient.getConfig()?.debug;
|
|
5836
|
+
if (debug) console.log(`[Paywallo PRELOAD] paywalls: ${placements.length} placements`);
|
|
5837
|
+
for (let i = 0; i < placements.length; i++) {
|
|
5838
|
+
if (i > 0) await new Promise((r) => setTimeout(r, 200));
|
|
5839
|
+
void this.preload(apiClient, placements[i]).catch((err) => {
|
|
5840
|
+
if (debug) console.log(`[Paywallo PRELOAD] paywall failed ${placements[i]}:`, err);
|
|
5841
|
+
});
|
|
5842
|
+
}
|
|
5843
|
+
}
|
|
5844
|
+
getPreloaded(placement) {
|
|
5845
|
+
const cached2 = this.preloaded.get(placement);
|
|
5846
|
+
if (!cached2) return null;
|
|
5847
|
+
const age = Date.now() - cached2.preloadedAt;
|
|
5848
|
+
if (age >= PRELOAD_TTL_MS) {
|
|
5849
|
+
this.preloaded.delete(placement);
|
|
5850
|
+
return null;
|
|
5851
|
+
}
|
|
5852
|
+
if (age >= PRELOAD_TTL_MS * STALE_THRESHOLD && this.apiClientRef) {
|
|
5853
|
+
void this.preload(this.apiClientRef, placement).catch(() => {
|
|
5854
|
+
});
|
|
5855
|
+
}
|
|
5856
|
+
return { config: cached2.config, products: new Map(cached2.products) };
|
|
5857
|
+
}
|
|
5858
|
+
isPreloaded(placement) {
|
|
5859
|
+
return this.getPreloaded(placement) !== null;
|
|
5860
|
+
}
|
|
5861
|
+
clear() {
|
|
5862
|
+
this.preloaded.clear();
|
|
5863
|
+
this.activePreloads.clear();
|
|
5864
|
+
}
|
|
5865
|
+
async doPreload(apiClient, placement) {
|
|
5866
|
+
try {
|
|
5867
|
+
const config = await apiClient.getPaywall(placement);
|
|
5868
|
+
if (!config) {
|
|
5869
|
+
return {
|
|
5870
|
+
success: false,
|
|
5871
|
+
error: new PaywallError(
|
|
5872
|
+
PAYWALL_ERROR_CODES.NOT_FOUND,
|
|
5873
|
+
`Paywall not found for placement: ${placement}`
|
|
5874
|
+
)
|
|
5875
|
+
};
|
|
5876
|
+
}
|
|
5877
|
+
const products = await this.loadProductsFor(config);
|
|
5878
|
+
this.preloaded.set(placement, { config, products, preloadedAt: Date.now() });
|
|
5879
|
+
if (PaywalloClient.getConfig()?.debug) {
|
|
5880
|
+
console.log(`[Paywallo PRELOAD] paywall cached \u2014 placement: ${placement}`);
|
|
5881
|
+
}
|
|
5882
|
+
return { success: true };
|
|
5883
|
+
} catch (error) {
|
|
5884
|
+
return {
|
|
5885
|
+
success: false,
|
|
5886
|
+
error: error instanceof Error ? error : new PaywallError(PAYWALL_ERROR_CODES.LOAD_FAILED, String(error))
|
|
5887
|
+
};
|
|
5888
|
+
}
|
|
5889
|
+
}
|
|
5890
|
+
async loadProductsFor(config) {
|
|
5891
|
+
const productIds = [];
|
|
5892
|
+
if (config.primaryProductId) productIds.push(config.primaryProductId);
|
|
5893
|
+
if (config.secondaryProductId) productIds.push(config.secondaryProductId);
|
|
5894
|
+
if (config.tertiaryProductId) productIds.push(config.tertiaryProductId);
|
|
5895
|
+
if (productIds.length === 0) return /* @__PURE__ */ new Map();
|
|
5896
|
+
try {
|
|
5897
|
+
const loaded = await getIAPService().loadProducts(productIds);
|
|
5898
|
+
const map = /* @__PURE__ */ new Map();
|
|
5899
|
+
for (const p of loaded) map.set(p.productId, p);
|
|
5900
|
+
return map;
|
|
5901
|
+
} catch (err) {
|
|
5902
|
+
if (PaywalloClient.getConfig()?.debug) {
|
|
5903
|
+
console.log("[Paywallo PRELOAD] products failed, caching config only:", err);
|
|
5904
|
+
}
|
|
5905
|
+
return /* @__PURE__ */ new Map();
|
|
5906
|
+
}
|
|
5907
|
+
}
|
|
5908
|
+
};
|
|
5909
|
+
paywallPreloadService = new PaywallPreloadService();
|
|
5910
|
+
}
|
|
5911
|
+
});
|
|
5912
|
+
|
|
5913
|
+
// src/domains/paywall/PaywallWebViewPrewarmer.tsx
|
|
5914
|
+
import { useEffect as useEffect6, useMemo as useMemo2, useState as useState5 } from "react";
|
|
5915
|
+
import { requireNativeComponent as requireNativeComponent2, StyleSheet as StyleSheet4, View as View4 } from "react-native";
|
|
5916
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
5917
|
+
function getNativeWebView2() {
|
|
5918
|
+
if (NativeWebViewComponent2) return NativeWebViewComponent2;
|
|
5919
|
+
if (nativeLookupFailed) return null;
|
|
5920
|
+
try {
|
|
5921
|
+
NativeWebViewComponent2 = requireNativeComponent2("PaywalloWebView");
|
|
5922
|
+
return NativeWebViewComponent2;
|
|
5923
|
+
} catch {
|
|
5924
|
+
nativeLookupFailed = true;
|
|
5925
|
+
return null;
|
|
5926
|
+
}
|
|
5927
|
+
}
|
|
5928
|
+
function PaywallWebViewPrewarmer({
|
|
5929
|
+
webUrl,
|
|
5930
|
+
durationMs = DEFAULT_DURATION_MS
|
|
5931
|
+
}) {
|
|
5932
|
+
const NativeWebView = useMemo2(() => getNativeWebView2(), []);
|
|
5933
|
+
const [mounted, setMounted] = useState5(true);
|
|
5934
|
+
useEffect6(() => {
|
|
5935
|
+
if (!NativeWebView) return;
|
|
5936
|
+
const timer = setTimeout(() => setMounted(false), durationMs);
|
|
5937
|
+
return () => clearTimeout(timer);
|
|
5938
|
+
}, [NativeWebView, durationMs]);
|
|
5939
|
+
if (!NativeWebView || !mounted) return null;
|
|
5940
|
+
const preheatUrl = `${webUrl}/paywall/preheat`;
|
|
5941
|
+
if (PaywalloClient.getConfig()?.debug) {
|
|
5942
|
+
console.log(`[Paywallo PREWARM] mounting hidden WebView: ${preheatUrl}`);
|
|
5943
|
+
}
|
|
5944
|
+
return /* @__PURE__ */ jsx4(View4, { pointerEvents: "none", style: styles4.hidden, children: /* @__PURE__ */ jsx4(NativeWebView, { sourceUrl: preheatUrl, style: styles4.fill }) });
|
|
5945
|
+
}
|
|
5946
|
+
var NativeWebViewComponent2, nativeLookupFailed, DEFAULT_DURATION_MS, styles4;
|
|
5947
|
+
var init_PaywallWebViewPrewarmer = __esm({
|
|
5948
|
+
"src/domains/paywall/PaywallWebViewPrewarmer.tsx"() {
|
|
5949
|
+
"use strict";
|
|
5950
|
+
init_PaywalloClient();
|
|
5951
|
+
NativeWebViewComponent2 = null;
|
|
5952
|
+
nativeLookupFailed = false;
|
|
5953
|
+
DEFAULT_DURATION_MS = 60 * 1e3;
|
|
5954
|
+
styles4 = StyleSheet4.create({
|
|
5955
|
+
hidden: {
|
|
5956
|
+
height: 1,
|
|
5957
|
+
left: -9999,
|
|
5958
|
+
opacity: 0,
|
|
5959
|
+
position: "absolute",
|
|
5960
|
+
top: -9999,
|
|
5961
|
+
width: 1
|
|
5962
|
+
},
|
|
5963
|
+
fill: { flex: 1 }
|
|
5964
|
+
});
|
|
5965
|
+
}
|
|
5966
|
+
});
|
|
5967
|
+
|
|
5672
5968
|
// src/domains/paywall/usePaywallSession.ts
|
|
5673
|
-
import { useCallback as useCallback5, useEffect as
|
|
5969
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef6 } from "react";
|
|
5674
5970
|
function usePaywallSession(options) {
|
|
5675
|
-
const closedRef =
|
|
5676
|
-
const optsRef =
|
|
5971
|
+
const closedRef = useRef6(false);
|
|
5972
|
+
const optsRef = useRef6(options);
|
|
5677
5973
|
optsRef.current = options;
|
|
5678
|
-
|
|
5974
|
+
useEffect7(() => {
|
|
5679
5975
|
if (!options) return;
|
|
5680
5976
|
closedRef.current = false;
|
|
5681
5977
|
startHeartbeat({
|
|
@@ -5719,11 +6015,11 @@ var init_usePaywallSession = __esm({
|
|
|
5719
6015
|
});
|
|
5720
6016
|
|
|
5721
6017
|
// src/domains/paywall/usePaywallPresenter.ts
|
|
5722
|
-
import { useCallback as useCallback6, useEffect as
|
|
6018
|
+
import { useCallback as useCallback6, useEffect as useEffect8, useRef as useRef7, useState as useState6 } from "react";
|
|
5723
6019
|
function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
|
|
5724
|
-
const [showingPreloadedWebView, setShowingPreloadedWebView] =
|
|
5725
|
-
const resolverRef =
|
|
5726
|
-
const presentedAtRef =
|
|
6020
|
+
const [showingPreloadedWebView, setShowingPreloadedWebView] = useState6(false);
|
|
6021
|
+
const resolverRef = useRef7(null);
|
|
6022
|
+
const presentedAtRef = useRef7(Date.now());
|
|
5727
6023
|
const sessionOpts = showingPreloadedWebView && preloadedWebView ? {
|
|
5728
6024
|
placement: preloadedWebView.placement,
|
|
5729
6025
|
paywallId: preloadedWebView.paywallId,
|
|
@@ -5733,7 +6029,7 @@ function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
|
|
|
5733
6029
|
...preloadedWebView.campaignId && { campaignId: preloadedWebView.campaignId }
|
|
5734
6030
|
} : null;
|
|
5735
6031
|
const { markClosed } = usePaywallSession(sessionOpts);
|
|
5736
|
-
|
|
6032
|
+
useEffect8(() => {
|
|
5737
6033
|
return () => {
|
|
5738
6034
|
if (resolverRef.current) {
|
|
5739
6035
|
resolverRef.current({ presented: false, purchased: false, cancelled: true, restored: false });
|
|
@@ -5820,7 +6116,9 @@ var init_paywall = __esm({
|
|
|
5820
6116
|
init_PaywallError();
|
|
5821
6117
|
init_PaywallErrorBoundary();
|
|
5822
6118
|
init_PaywallModal();
|
|
6119
|
+
init_PaywallPreloadService();
|
|
5823
6120
|
init_PaywallWebView();
|
|
6121
|
+
init_PaywallWebViewPrewarmer();
|
|
5824
6122
|
init_usePaywallPresenter();
|
|
5825
6123
|
init_usePaywallTracking();
|
|
5826
6124
|
}
|
|
@@ -7811,6 +8109,8 @@ var init_ApiClient = __esm({
|
|
|
7811
8109
|
}
|
|
7812
8110
|
}
|
|
7813
8111
|
const attribution = Object.keys(rawAttribution).length > 0 ? rawAttribution : void 0;
|
|
8112
|
+
const rawUserId = properties?.["userId"];
|
|
8113
|
+
const externalUserId = typeof rawUserId === "string" && rawUserId.length > 0 ? rawUserId : void 0;
|
|
7814
8114
|
await postWithQueue(
|
|
7815
8115
|
{
|
|
7816
8116
|
isOfflineQueueEnabled: () => this.offlineQueueEnabled,
|
|
@@ -7821,6 +8121,7 @@ var init_ApiClient = __esm({
|
|
|
7821
8121
|
"/sdk/identity/identify",
|
|
7822
8122
|
{
|
|
7823
8123
|
distinct_id: distinctId,
|
|
8124
|
+
...externalUserId && { external_user_id: externalUserId },
|
|
7824
8125
|
traits,
|
|
7825
8126
|
...attribution && { attribution },
|
|
7826
8127
|
...deviceId && { deviceId },
|
|
@@ -8392,6 +8693,7 @@ async function doFullReset(state) {
|
|
|
8392
8693
|
offlineQueue.dispose();
|
|
8393
8694
|
networkMonitor.dispose();
|
|
8394
8695
|
campaignGateService.clearPreloaded();
|
|
8696
|
+
paywallPreloadService.clear();
|
|
8395
8697
|
await notificationsManager.invalidateLocalToken().catch(() => void 0);
|
|
8396
8698
|
notificationsManager.destroy();
|
|
8397
8699
|
deepLinkAttributionCapture.stop();
|
|
@@ -8426,6 +8728,7 @@ var init_PaywalloLifecycle = __esm({
|
|
|
8426
8728
|
init_identity();
|
|
8427
8729
|
init_DeepLinkAttributionCapture();
|
|
8428
8730
|
init_notifications();
|
|
8731
|
+
init_paywall();
|
|
8429
8732
|
init_session();
|
|
8430
8733
|
init_SubscriptionCache();
|
|
8431
8734
|
init_network();
|
|
@@ -8729,6 +9032,15 @@ var init_PaywalloClient = __esm({
|
|
|
8729
9032
|
isPreloaded(placement) {
|
|
8730
9033
|
return campaignGateService.getPreloadedCampaign(placement) !== null;
|
|
8731
9034
|
}
|
|
9035
|
+
async preloadPaywall(placement) {
|
|
9036
|
+
return paywallPreloadService.preload(this.getApiClientOrThrow(), placement);
|
|
9037
|
+
}
|
|
9038
|
+
async preloadPaywalls(placements) {
|
|
9039
|
+
return paywallPreloadService.preloadMany(this.getApiClientOrThrow(), placements);
|
|
9040
|
+
}
|
|
9041
|
+
isPaywallPreloaded(placement) {
|
|
9042
|
+
return paywallPreloadService.isPreloaded(placement);
|
|
9043
|
+
}
|
|
8732
9044
|
getAutoPreloadedPlacement() {
|
|
8733
9045
|
return this.state.autoPreloadedPlacement;
|
|
8734
9046
|
}
|
|
@@ -8943,7 +9255,7 @@ init_session();
|
|
|
8943
9255
|
init_PaywalloError();
|
|
8944
9256
|
|
|
8945
9257
|
// src/hooks/useOfferingPurchase.ts
|
|
8946
|
-
import { useCallback as useCallback7, useState as
|
|
9258
|
+
import { useCallback as useCallback7, useState as useState7 } from "react";
|
|
8947
9259
|
|
|
8948
9260
|
// src/domains/plan/invalidateCache.ts
|
|
8949
9261
|
async function invalidatePlanCache() {
|
|
@@ -8956,7 +9268,7 @@ async function invalidatePlanCache() {
|
|
|
8956
9268
|
|
|
8957
9269
|
// src/hooks/useOfferingPurchase.ts
|
|
8958
9270
|
function useOfferingPurchase() {
|
|
8959
|
-
const [state, setState] =
|
|
9271
|
+
const [state, setState] = useState7("idle");
|
|
8960
9272
|
const purchase = useCallback7(async (productId) => {
|
|
8961
9273
|
setState("purchasing");
|
|
8962
9274
|
try {
|
|
@@ -8981,7 +9293,7 @@ function useOfferingPurchase() {
|
|
|
8981
9293
|
}
|
|
8982
9294
|
|
|
8983
9295
|
// src/hooks/useOfferings.ts
|
|
8984
|
-
import { useCallback as useCallback8, useEffect as
|
|
9296
|
+
import { useCallback as useCallback8, useEffect as useEffect9, useState as useState8 } from "react";
|
|
8985
9297
|
async function getOfferingService() {
|
|
8986
9298
|
try {
|
|
8987
9299
|
const mod = await Promise.resolve().then(() => (init_OfferingService(), OfferingService_exports));
|
|
@@ -8991,8 +9303,8 @@ async function getOfferingService() {
|
|
|
8991
9303
|
}
|
|
8992
9304
|
}
|
|
8993
9305
|
function useOfferings(ids) {
|
|
8994
|
-
const [offerings, setOfferings] =
|
|
8995
|
-
const [isLoading, setIsLoading] =
|
|
9306
|
+
const [offerings, setOfferings] = useState8([]);
|
|
9307
|
+
const [isLoading, setIsLoading] = useState8(true);
|
|
8996
9308
|
const idsKey = ids?.join(",") ?? "";
|
|
8997
9309
|
const fetchOfferings = useCallback8(async () => {
|
|
8998
9310
|
setIsLoading(true);
|
|
@@ -9010,7 +9322,7 @@ function useOfferings(ids) {
|
|
|
9010
9322
|
setIsLoading(false);
|
|
9011
9323
|
}
|
|
9012
9324
|
}, [idsKey]);
|
|
9013
|
-
|
|
9325
|
+
useEffect9(() => {
|
|
9014
9326
|
void fetchOfferings();
|
|
9015
9327
|
}, [fetchOfferings]);
|
|
9016
9328
|
return { offerings, isLoading };
|
|
@@ -9047,10 +9359,10 @@ function usePaywallo() {
|
|
|
9047
9359
|
}
|
|
9048
9360
|
|
|
9049
9361
|
// src/hooks/usePlanPurchase.ts
|
|
9050
|
-
import { useCallback as useCallback10, useState as
|
|
9362
|
+
import { useCallback as useCallback10, useState as useState9 } from "react";
|
|
9051
9363
|
function usePlanPurchase() {
|
|
9052
|
-
const [state, setState] =
|
|
9053
|
-
const [error, setError] =
|
|
9364
|
+
const [state, setState] = useState9("idle");
|
|
9365
|
+
const [error, setError] = useState9(null);
|
|
9054
9366
|
const purchase = useCallback10(async (productId) => {
|
|
9055
9367
|
setState("purchasing");
|
|
9056
9368
|
setError(null);
|
|
@@ -9103,7 +9415,7 @@ function usePlanPurchase() {
|
|
|
9103
9415
|
}
|
|
9104
9416
|
|
|
9105
9417
|
// src/hooks/usePlans.ts
|
|
9106
|
-
import { useCallback as useCallback11, useEffect as
|
|
9418
|
+
import { useCallback as useCallback11, useEffect as useEffect10, useState as useState10 } from "react";
|
|
9107
9419
|
async function getPlanService() {
|
|
9108
9420
|
try {
|
|
9109
9421
|
const mod = await Promise.resolve().then(() => (init_PlanService(), PlanService_exports));
|
|
@@ -9113,10 +9425,10 @@ async function getPlanService() {
|
|
|
9113
9425
|
}
|
|
9114
9426
|
}
|
|
9115
9427
|
function usePlans() {
|
|
9116
|
-
const [allPlans, setAllPlans] =
|
|
9117
|
-
const [currentPlan, setCurrentPlan] =
|
|
9118
|
-
const [isLoading, setIsLoading] =
|
|
9119
|
-
const [error, setError] =
|
|
9428
|
+
const [allPlans, setAllPlans] = useState10([]);
|
|
9429
|
+
const [currentPlan, setCurrentPlan] = useState10(null);
|
|
9430
|
+
const [isLoading, setIsLoading] = useState10(true);
|
|
9431
|
+
const [error, setError] = useState10(null);
|
|
9120
9432
|
const fetchPlans = useCallback11(async (forceRefresh = false) => {
|
|
9121
9433
|
setIsLoading(true);
|
|
9122
9434
|
setError(null);
|
|
@@ -9141,14 +9453,14 @@ function usePlans() {
|
|
|
9141
9453
|
const refresh = useCallback11(() => {
|
|
9142
9454
|
return fetchPlans(true);
|
|
9143
9455
|
}, [fetchPlans]);
|
|
9144
|
-
|
|
9456
|
+
useEffect10(() => {
|
|
9145
9457
|
void fetchPlans(false);
|
|
9146
9458
|
}, [fetchPlans]);
|
|
9147
9459
|
return { currentPlan, allPlans, isLoading, error, refresh };
|
|
9148
9460
|
}
|
|
9149
9461
|
|
|
9150
9462
|
// src/hooks/useProducts.ts
|
|
9151
|
-
import { useCallback as useCallback12, useEffect as
|
|
9463
|
+
import { useCallback as useCallback12, useEffect as useEffect11, useState as useState11 } from "react";
|
|
9152
9464
|
|
|
9153
9465
|
// src/utils/productMappers.ts
|
|
9154
9466
|
function mapToIAPProduct(product) {
|
|
@@ -9187,10 +9499,10 @@ function mapToFormattedProduct(product) {
|
|
|
9187
9499
|
|
|
9188
9500
|
// src/hooks/useProducts.ts
|
|
9189
9501
|
function useProducts(initialProductIds) {
|
|
9190
|
-
const [products, setProducts] =
|
|
9191
|
-
const [formattedProducts, setFormattedProducts] =
|
|
9192
|
-
const [isLoading, setIsLoading] =
|
|
9193
|
-
const [error, setError] =
|
|
9502
|
+
const [products, setProducts] = useState11([]);
|
|
9503
|
+
const [formattedProducts, setFormattedProducts] = useState11([]);
|
|
9504
|
+
const [isLoading, setIsLoading] = useState11(false);
|
|
9505
|
+
const [error, setError] = useState11(null);
|
|
9194
9506
|
const loadProducts = useCallback12(async (productIds) => {
|
|
9195
9507
|
setIsLoading(true);
|
|
9196
9508
|
setError(null);
|
|
@@ -9205,7 +9517,7 @@ function useProducts(initialProductIds) {
|
|
|
9205
9517
|
setIsLoading(false);
|
|
9206
9518
|
}
|
|
9207
9519
|
}, []);
|
|
9208
|
-
|
|
9520
|
+
useEffect11(() => {
|
|
9209
9521
|
if (initialProductIds && initialProductIds.length > 0) {
|
|
9210
9522
|
void loadProducts(initialProductIds);
|
|
9211
9523
|
}
|
|
@@ -9251,13 +9563,13 @@ function useProducts(initialProductIds) {
|
|
|
9251
9563
|
}
|
|
9252
9564
|
|
|
9253
9565
|
// src/hooks/usePurchase.ts
|
|
9254
|
-
import { useCallback as useCallback13, useState as
|
|
9566
|
+
import { useCallback as useCallback13, useState as useState12 } from "react";
|
|
9255
9567
|
function toIAPPurchase(p) {
|
|
9256
9568
|
return { productId: p.productId, transactionId: p.transactionId, transactionDate: p.transactionDate, transactionReceipt: p.receipt };
|
|
9257
9569
|
}
|
|
9258
9570
|
function usePurchase() {
|
|
9259
|
-
const [state, setState] =
|
|
9260
|
-
const [error, setError] =
|
|
9571
|
+
const [state, setState] = useState12("idle");
|
|
9572
|
+
const [error, setError] = useState12(null);
|
|
9261
9573
|
const purchase = useCallback13(async (productId) => {
|
|
9262
9574
|
setState("purchasing");
|
|
9263
9575
|
setError(null);
|
|
@@ -9305,13 +9617,13 @@ function usePurchase() {
|
|
|
9305
9617
|
}
|
|
9306
9618
|
|
|
9307
9619
|
// src/hooks/useSubscription.ts
|
|
9308
|
-
import { useCallback as useCallback14, useEffect as
|
|
9620
|
+
import { useCallback as useCallback14, useEffect as useEffect12, useState as useState13 } from "react";
|
|
9309
9621
|
init_SubscriptionManager();
|
|
9310
9622
|
var EMPTY_ENTITLEMENTS = [];
|
|
9311
9623
|
function useSubscription() {
|
|
9312
|
-
const [status, setStatus] =
|
|
9313
|
-
const [isLoading, setIsLoading] =
|
|
9314
|
-
const [error, setError] =
|
|
9624
|
+
const [status, setStatus] = useState13(null);
|
|
9625
|
+
const [isLoading, setIsLoading] = useState13(true);
|
|
9626
|
+
const [error, setError] = useState13(null);
|
|
9315
9627
|
const refresh = useCallback14(async () => {
|
|
9316
9628
|
setIsLoading(true);
|
|
9317
9629
|
setError(null);
|
|
@@ -9338,7 +9650,7 @@ function useSubscription() {
|
|
|
9338
9650
|
setIsLoading(false);
|
|
9339
9651
|
}
|
|
9340
9652
|
}, []);
|
|
9341
|
-
|
|
9653
|
+
useEffect12(() => {
|
|
9342
9654
|
void subscriptionManager.getSubscriptionStatus().then((s) => {
|
|
9343
9655
|
setStatus(s);
|
|
9344
9656
|
setIsLoading(false);
|
|
@@ -9366,94 +9678,288 @@ function useSubscription() {
|
|
|
9366
9678
|
}
|
|
9367
9679
|
|
|
9368
9680
|
// src/PaywalloProvider.tsx
|
|
9369
|
-
import { useCallback as useCallback19, useEffect as
|
|
9370
|
-
import { Animated as
|
|
9681
|
+
import { useCallback as useCallback19, useEffect as useEffect14, useLayoutEffect, useMemo as useMemo3, useRef as useRef11, useState as useState17 } from "react";
|
|
9682
|
+
import { Animated as Animated4, Dimensions as Dimensions5, Easing as Easing2, StyleSheet as StyleSheet5 } from "react-native";
|
|
9371
9683
|
init_PaywalloClient();
|
|
9372
|
-
init_SecureStorage();
|
|
9373
9684
|
init_NativeStorage();
|
|
9685
|
+
init_SecureStorage();
|
|
9374
9686
|
init_campaign();
|
|
9375
9687
|
init_paywall();
|
|
9376
9688
|
init_paywall();
|
|
9377
9689
|
init_paywall();
|
|
9378
9690
|
init_paywall();
|
|
9379
|
-
|
|
9380
|
-
|
|
9691
|
+
init_paywall();
|
|
9692
|
+
init_paywall();
|
|
9381
9693
|
|
|
9382
|
-
// src/
|
|
9383
|
-
|
|
9694
|
+
// src/domains/paywall/SuperwallAutoBridge.ts
|
|
9695
|
+
init_PaywalloClient();
|
|
9384
9696
|
|
|
9385
|
-
// src/utils/
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9697
|
+
// src/core/utils/eventId.ts
|
|
9698
|
+
function hashString(input) {
|
|
9699
|
+
let h1 = 3735928559;
|
|
9700
|
+
let h2 = 1103547991;
|
|
9701
|
+
for (let i = 0; i < input.length; i++) {
|
|
9702
|
+
const ch = input.charCodeAt(i);
|
|
9703
|
+
h1 = Math.imul(h1 ^ ch, 2654435769);
|
|
9704
|
+
h2 = Math.imul(h2 ^ ch, 1598716949);
|
|
9392
9705
|
}
|
|
9706
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 73244475);
|
|
9707
|
+
h1 ^= Math.imul(h2 ^ h2 >>> 13, 351452291);
|
|
9708
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 73244475);
|
|
9709
|
+
h2 ^= Math.imul(h1 ^ h1 >>> 13, 351452291);
|
|
9710
|
+
const toHex = (n) => (n >>> 0).toString(16).padStart(8, "0");
|
|
9711
|
+
return toHex(h1) + toHex(h2);
|
|
9393
9712
|
}
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
}
|
|
9713
|
+
function buildDeterministicEventId(parts) {
|
|
9714
|
+
const raw = hashString(parts.join("|"));
|
|
9715
|
+
const p1 = raw.slice(0, 8);
|
|
9716
|
+
const p2 = raw.slice(8, 12);
|
|
9717
|
+
const p3 = "4" + raw.slice(13, 16);
|
|
9718
|
+
const p4 = (parseInt(raw[16], 16) & 3 | 8).toString(16) + raw.slice(17, 20);
|
|
9719
|
+
const p5 = raw.padEnd(32, "0").slice(20, 32);
|
|
9720
|
+
return `${p1}-${p2}-${p3}-${p4}-${p5}`;
|
|
9402
9721
|
}
|
|
9403
9722
|
|
|
9404
|
-
// src/
|
|
9405
|
-
var
|
|
9406
|
-
var
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
didRunRef.current = true;
|
|
9416
|
-
void (async () => {
|
|
9417
|
-
const sessionId = getSessionId();
|
|
9418
|
-
const appVersion = getAppVersion2();
|
|
9419
|
-
const deviceType = await detectPlatform3();
|
|
9420
|
-
const osVersion = await detectOsVersion();
|
|
9421
|
-
if (debug) console.log("[Paywallo EVENTS] computed device context", { deviceType, osVersion, appVersion, sessionId, hasSessionId: !!sessionId });
|
|
9422
|
-
try {
|
|
9423
|
-
if (debug) console.log("[Paywallo EVENTS] checking install \u2014 about to read FIRST_SEEN_KEY");
|
|
9424
|
-
const firstSeen = await storageGet(FIRST_SEEN_KEY);
|
|
9425
|
-
if (!firstSeen) {
|
|
9426
|
-
await emitLifecycle({ type: "install", device_type: deviceType, os_version: osVersion, app_version: appVersion, ...sessionId ? { session_id: sessionId } : {} }, "critical");
|
|
9427
|
-
await storageSet(FIRST_SEEN_KEY, (/* @__PURE__ */ new Date()).toISOString());
|
|
9428
|
-
if (debug) console.log("[Paywallo EVENTS] install event emitted (first time)");
|
|
9429
|
-
if (debug) console.log("[Paywallo EVENTS] lifecycle install emitted");
|
|
9430
|
-
} else {
|
|
9431
|
-
if (debug) console.log("[Paywallo EVENTS] install skipped \u2014 firstSeen already exists", { firstSeen });
|
|
9432
|
-
}
|
|
9433
|
-
} catch (err) {
|
|
9434
|
-
if (debug) console.log("[Paywallo EVENTS] lifecycle install check failed", err);
|
|
9435
|
-
}
|
|
9436
|
-
try {
|
|
9437
|
-
if (debug) console.log("[Paywallo EVENTS] emitting cold_start");
|
|
9438
|
-
await emitLifecycle({ type: "cold_start", app_version: appVersion, device_type: deviceType, os_version: osVersion, ...sessionId ? { session_id: sessionId } : {} }, "normal");
|
|
9439
|
-
if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start emitted");
|
|
9440
|
-
} catch (err) {
|
|
9441
|
-
if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start failed", err);
|
|
9442
|
-
}
|
|
9443
|
-
})();
|
|
9444
|
-
}, DEBOUNCE_MS);
|
|
9445
|
-
return () => clearTimeout(timer);
|
|
9446
|
-
}, [isInitialized]);
|
|
9723
|
+
// src/domains/paywall/SuperwallAutoBridge.ts
|
|
9724
|
+
var bridgeStarted = false;
|
|
9725
|
+
var unsubscribe = null;
|
|
9726
|
+
var debugEnabled = false;
|
|
9727
|
+
function log(msg, data) {
|
|
9728
|
+
if (!debugEnabled) return;
|
|
9729
|
+
if (data !== void 0) {
|
|
9730
|
+
console.log(`[Paywallo:SuperwallBridge] ${msg}`, data);
|
|
9731
|
+
} else {
|
|
9732
|
+
console.log(`[Paywallo:SuperwallBridge] ${msg}`);
|
|
9733
|
+
}
|
|
9447
9734
|
}
|
|
9735
|
+
function minuteBucket(ts) {
|
|
9736
|
+
return String(Math.floor(ts / 6e4));
|
|
9737
|
+
}
|
|
9738
|
+
function buildEventId(type, identifier, ts) {
|
|
9739
|
+
return buildDeterministicEventId(["sw", type, identifier, minuteBucket(ts)]);
|
|
9740
|
+
}
|
|
9741
|
+
async function trackPaywallEvent(type, identifier, ts, extra) {
|
|
9742
|
+
const _eventId = buildEventId(type, identifier, ts);
|
|
9743
|
+
try {
|
|
9744
|
+
const cleanExtra = {};
|
|
9745
|
+
if (extra) {
|
|
9746
|
+
for (const [k, v] of Object.entries(extra)) if (v !== void 0) cleanExtra[k] = v;
|
|
9747
|
+
}
|
|
9748
|
+
await PaywalloClient.track("paywall", {
|
|
9749
|
+
priority: "critical",
|
|
9750
|
+
properties: {
|
|
9751
|
+
type,
|
|
9752
|
+
paywall_id: identifier,
|
|
9753
|
+
...cleanExtra
|
|
9754
|
+
}
|
|
9755
|
+
});
|
|
9756
|
+
} catch (err) {
|
|
9757
|
+
log("track error", { type, identifier, eventId: _eventId, err: String(err) });
|
|
9758
|
+
}
|
|
9759
|
+
}
|
|
9760
|
+
function mapDismissToCloseReason(resultType) {
|
|
9761
|
+
switch (resultType) {
|
|
9762
|
+
case "purchased":
|
|
9763
|
+
return "purchased";
|
|
9764
|
+
case "restored":
|
|
9765
|
+
return "purchased";
|
|
9766
|
+
// restore counts as a purchase outcome for funnel
|
|
9767
|
+
case "declined":
|
|
9768
|
+
return "dismissed";
|
|
9769
|
+
default:
|
|
9770
|
+
return void 0;
|
|
9771
|
+
}
|
|
9772
|
+
}
|
|
9773
|
+
function buildCallbacks() {
|
|
9774
|
+
return {
|
|
9775
|
+
onPaywallPresent: (info) => {
|
|
9776
|
+
const ts = Date.now();
|
|
9777
|
+
log("onPaywallPresent", { identifier: info.identifier });
|
|
9778
|
+
void trackPaywallEvent("viewed", info.identifier, ts, {
|
|
9779
|
+
placement: info.presentedByEventWithName,
|
|
9780
|
+
variant_id: info.experiment?.variant?.id,
|
|
9781
|
+
campaign_id: info.experiment?.id
|
|
9782
|
+
});
|
|
9783
|
+
},
|
|
9784
|
+
onPaywallDismiss: (info, result) => {
|
|
9785
|
+
const ts = Date.now();
|
|
9786
|
+
log("onPaywallDismiss", { identifier: info.identifier, result: result.type });
|
|
9787
|
+
void trackPaywallEvent("closed", info.identifier, ts, {
|
|
9788
|
+
placement: info.presentedByEventWithName,
|
|
9789
|
+
variant_id: info.experiment?.variant?.id,
|
|
9790
|
+
campaign_id: info.experiment?.id,
|
|
9791
|
+
close_reason: mapDismissToCloseReason(result.type),
|
|
9792
|
+
closed_at: new Date(ts).toISOString()
|
|
9793
|
+
});
|
|
9794
|
+
},
|
|
9795
|
+
onPurchase: (params) => {
|
|
9796
|
+
const ts = Date.now();
|
|
9797
|
+
const safe = params !== null && typeof params === "object" ? params : {};
|
|
9798
|
+
const productId = "productId" in safe && typeof safe.productId === "string" ? safe.productId : "unknown";
|
|
9799
|
+
const identifier = "paywallIdentifier" in safe && typeof safe.paywallIdentifier === "string" ? safe.paywallIdentifier : productId;
|
|
9800
|
+
log("onPurchase", { productId, identifier });
|
|
9801
|
+
void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
|
|
9802
|
+
},
|
|
9803
|
+
// Global Superwall event listener — used specifically for `transactionComplete`
|
|
9804
|
+
// which is the only event that exposes the full product (currency/price) +
|
|
9805
|
+
// transaction (storeTransactionId) shape needed to create a server-side
|
|
9806
|
+
// `transactions` row. Other events are skipped (paywallOpen/Dismiss already
|
|
9807
|
+
// handled above with their typed callbacks).
|
|
9808
|
+
onSuperwallEvent: (info) => {
|
|
9809
|
+
const ev = info?.event;
|
|
9810
|
+
if (!ev || ev.event !== "transactionComplete") return;
|
|
9811
|
+
const ts = Date.now();
|
|
9812
|
+
const product = ev.product;
|
|
9813
|
+
const transaction = ev.transaction;
|
|
9814
|
+
if (!product) {
|
|
9815
|
+
log("transactionComplete missing product \u2014 skipping transaction track");
|
|
9816
|
+
return;
|
|
9817
|
+
}
|
|
9818
|
+
const productId = product.productIdentifier ?? product.id ?? "unknown";
|
|
9819
|
+
const currency = product.currencyCode ?? "USD";
|
|
9820
|
+
const price = product.price ?? 0;
|
|
9821
|
+
const transactionId = transaction?.storeTransactionId ?? transaction?.originalTransactionIdentifier ?? `${productId}_${String(ts)}`;
|
|
9822
|
+
const isTrial = product.hasFreeTrial === true || (product.trialPeriodPrice ?? 0) > 0;
|
|
9823
|
+
const paywallIdentifier = ev.paywallInfo?.identifier;
|
|
9824
|
+
log("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
|
|
9825
|
+
void (async () => {
|
|
9826
|
+
try {
|
|
9827
|
+
await PaywalloClient.track("transaction", {
|
|
9828
|
+
priority: "critical",
|
|
9829
|
+
properties: {
|
|
9830
|
+
type: isTrial ? "trial_started" : "completed",
|
|
9831
|
+
// Server's IngestTranslator looks for `transaction_id`/`product_id`
|
|
9832
|
+
// /`amount`/`currency`/`paywall_id` keys (not `tx_id`). PurchaseHandler
|
|
9833
|
+
// then resolves type=trial/new/renewal based on amount being 0 vs >0.
|
|
9834
|
+
transaction_id: transactionId,
|
|
9835
|
+
product_id: productId,
|
|
9836
|
+
amount: price,
|
|
9837
|
+
currency,
|
|
9838
|
+
...paywallIdentifier ? { paywall_id: paywallIdentifier } : {}
|
|
9839
|
+
}
|
|
9840
|
+
});
|
|
9841
|
+
} catch (err) {
|
|
9842
|
+
log("transactionComplete track error", { err: String(err) });
|
|
9843
|
+
}
|
|
9844
|
+
})();
|
|
9845
|
+
}
|
|
9846
|
+
};
|
|
9847
|
+
}
|
|
9848
|
+
function startSuperwallAutoBridge(debug = false) {
|
|
9849
|
+
debugEnabled = debug;
|
|
9850
|
+
if (bridgeStarted) {
|
|
9851
|
+
log("already started, skipping");
|
|
9852
|
+
return;
|
|
9853
|
+
}
|
|
9854
|
+
void detectAndStart();
|
|
9855
|
+
}
|
|
9856
|
+
async function detectAndStart() {
|
|
9857
|
+
let subscribeFn;
|
|
9858
|
+
try {
|
|
9859
|
+
const mod = await import("expo-superwall");
|
|
9860
|
+
if (!mod || typeof mod.subscribeToSuperwallEvents !== "function") {
|
|
9861
|
+
const internal = await import("expo-superwall/build/src/internal/superwallEventBridge");
|
|
9862
|
+
if (typeof internal.subscribeToSuperwallEvents !== "function") {
|
|
9863
|
+
log("expo-superwall subscribeToSuperwallEvents not found \u2014 bridge inactive");
|
|
9864
|
+
return;
|
|
9865
|
+
}
|
|
9866
|
+
subscribeFn = internal.subscribeToSuperwallEvents;
|
|
9867
|
+
} else {
|
|
9868
|
+
subscribeFn = mod.subscribeToSuperwallEvents;
|
|
9869
|
+
}
|
|
9870
|
+
} catch {
|
|
9871
|
+
log("expo-superwall not installed \u2014 bridge inactive");
|
|
9872
|
+
return;
|
|
9873
|
+
}
|
|
9874
|
+
try {
|
|
9875
|
+
const callbacks = buildCallbacks();
|
|
9876
|
+
unsubscribe = subscribeFn(void 0, () => callbacks);
|
|
9877
|
+
bridgeStarted = true;
|
|
9878
|
+
log("bridge started");
|
|
9879
|
+
} catch (err) {
|
|
9880
|
+
log("subscribe failed", { err: String(err) });
|
|
9881
|
+
}
|
|
9882
|
+
}
|
|
9883
|
+
|
|
9884
|
+
// src/PaywalloProvider.tsx
|
|
9885
|
+
init_session();
|
|
9886
|
+
init_ClientError();
|
|
9887
|
+
|
|
9888
|
+
// src/hooks/useAppAutoEvents.ts
|
|
9889
|
+
import { useEffect as useEffect13, useRef as useRef8 } from "react";
|
|
9890
|
+
|
|
9891
|
+
// src/utils/platformDetection.ts
|
|
9892
|
+
async function detectPlatform3() {
|
|
9893
|
+
try {
|
|
9894
|
+
const rn = await import("react-native");
|
|
9895
|
+
return rn.Platform?.OS === "ios" ? "ios" : "android";
|
|
9896
|
+
} catch {
|
|
9897
|
+
return "android";
|
|
9898
|
+
}
|
|
9899
|
+
}
|
|
9900
|
+
async function detectOsVersion() {
|
|
9901
|
+
try {
|
|
9902
|
+
const rn = await import("react-native");
|
|
9903
|
+
const version = rn.Platform?.Version;
|
|
9904
|
+
return version != null ? String(version) : "unknown";
|
|
9905
|
+
} catch {
|
|
9906
|
+
return "unknown";
|
|
9907
|
+
}
|
|
9908
|
+
}
|
|
9909
|
+
|
|
9910
|
+
// src/hooks/useAppAutoEvents.ts
|
|
9911
|
+
var FIRST_SEEN_KEY = "@paywallo:firstSeen_v226";
|
|
9912
|
+
var DEBOUNCE_MS = 1e3;
|
|
9913
|
+
function useAppAutoEvents(config) {
|
|
9914
|
+
const { isInitialized, getAppVersion: getAppVersion2, getSessionId, emitLifecycle, storageGet, storageSet, debug } = config;
|
|
9915
|
+
const didRunRef = useRef8(false);
|
|
9916
|
+
useEffect13(() => {
|
|
9917
|
+
if (debug) console.log("[Paywallo EVENTS] useEffect fired", { isInitialized, didRunRefCurrent: didRunRef.current });
|
|
9918
|
+
if (!isInitialized || didRunRef.current) return;
|
|
9919
|
+
const timer = setTimeout(() => {
|
|
9920
|
+
if (didRunRef.current) return;
|
|
9921
|
+
didRunRef.current = true;
|
|
9922
|
+
void (async () => {
|
|
9923
|
+
const sessionId = getSessionId();
|
|
9924
|
+
const appVersion = getAppVersion2();
|
|
9925
|
+
const deviceType = await detectPlatform3();
|
|
9926
|
+
const osVersion = await detectOsVersion();
|
|
9927
|
+
if (debug) console.log("[Paywallo EVENTS] computed device context", { deviceType, osVersion, appVersion, sessionId, hasSessionId: !!sessionId });
|
|
9928
|
+
try {
|
|
9929
|
+
if (debug) console.log("[Paywallo EVENTS] checking install \u2014 about to read FIRST_SEEN_KEY");
|
|
9930
|
+
const firstSeen = await storageGet(FIRST_SEEN_KEY);
|
|
9931
|
+
if (!firstSeen) {
|
|
9932
|
+
await emitLifecycle({ type: "install", device_type: deviceType, os_version: osVersion, app_version: appVersion, ...sessionId ? { session_id: sessionId } : {} }, "critical");
|
|
9933
|
+
await storageSet(FIRST_SEEN_KEY, (/* @__PURE__ */ new Date()).toISOString());
|
|
9934
|
+
if (debug) console.log("[Paywallo EVENTS] install event emitted (first time)");
|
|
9935
|
+
if (debug) console.log("[Paywallo EVENTS] lifecycle install emitted");
|
|
9936
|
+
} else {
|
|
9937
|
+
if (debug) console.log("[Paywallo EVENTS] install skipped \u2014 firstSeen already exists", { firstSeen });
|
|
9938
|
+
}
|
|
9939
|
+
} catch (err) {
|
|
9940
|
+
if (debug) console.log("[Paywallo EVENTS] lifecycle install check failed", err);
|
|
9941
|
+
}
|
|
9942
|
+
try {
|
|
9943
|
+
if (debug) console.log("[Paywallo EVENTS] emitting cold_start");
|
|
9944
|
+
await emitLifecycle({ type: "cold_start", app_version: appVersion, device_type: deviceType, os_version: osVersion, ...sessionId ? { session_id: sessionId } : {} }, "normal");
|
|
9945
|
+
if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start emitted");
|
|
9946
|
+
} catch (err) {
|
|
9947
|
+
if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start failed", err);
|
|
9948
|
+
}
|
|
9949
|
+
})();
|
|
9950
|
+
}, DEBOUNCE_MS);
|
|
9951
|
+
return () => clearTimeout(timer);
|
|
9952
|
+
}, [isInitialized]);
|
|
9953
|
+
}
|
|
9954
|
+
|
|
9955
|
+
// src/hooks/useCampaignPreload.ts
|
|
9956
|
+
init_PaywalloClient();
|
|
9957
|
+
init_CampaignError();
|
|
9958
|
+
import { useCallback as useCallback15, useRef as useRef9, useState as useState14 } from "react";
|
|
9959
|
+
|
|
9960
|
+
// src/utils/loadCampaignProducts.ts
|
|
9961
|
+
init_IAPService();
|
|
9448
9962
|
|
|
9449
|
-
// src/hooks/useCampaignPreload.ts
|
|
9450
|
-
init_PaywalloClient();
|
|
9451
|
-
init_CampaignError();
|
|
9452
|
-
import { useCallback as useCallback15, useRef as useRef8, useState as useState13 } from "react";
|
|
9453
|
-
|
|
9454
|
-
// src/utils/loadCampaignProducts.ts
|
|
9455
|
-
init_IAPService();
|
|
9456
|
-
|
|
9457
9963
|
// src/utils/serverProduct.ts
|
|
9458
9964
|
var PERIOD_MAP = {
|
|
9459
9965
|
P1Y: "ano",
|
|
@@ -9518,8 +10024,8 @@ function getCacheEntry(cache, placement) {
|
|
|
9518
10024
|
|
|
9519
10025
|
// src/hooks/useCampaignPreload.ts
|
|
9520
10026
|
function useCampaignPreload() {
|
|
9521
|
-
const [preloadedWebView, setPreloadedWebView] =
|
|
9522
|
-
const preloadedCampaignsRef =
|
|
10027
|
+
const [preloadedWebView, setPreloadedWebView] = useState14(null);
|
|
10028
|
+
const preloadedCampaignsRef = useRef9(/* @__PURE__ */ new Map());
|
|
9523
10029
|
const isPreloadValid = useCallback15(
|
|
9524
10030
|
(placement) => isCacheEntryValid(preloadedCampaignsRef.current.get(placement)),
|
|
9525
10031
|
[]
|
|
@@ -9597,10 +10103,10 @@ function useCampaignPreload() {
|
|
|
9597
10103
|
}
|
|
9598
10104
|
|
|
9599
10105
|
// src/hooks/useProductLoader.ts
|
|
9600
|
-
import { useCallback as useCallback16, useState as
|
|
10106
|
+
import { useCallback as useCallback16, useState as useState15 } from "react";
|
|
9601
10107
|
function useProductLoader() {
|
|
9602
|
-
const [products, setProducts] =
|
|
9603
|
-
const [isLoadingProducts, setIsLoadingProducts] =
|
|
10108
|
+
const [products, setProducts] = useState15(/* @__PURE__ */ new Map());
|
|
10109
|
+
const [isLoadingProducts, setIsLoadingProducts] = useState15(false);
|
|
9604
10110
|
const refreshProducts = useCallback16(async (productIds) => {
|
|
9605
10111
|
setIsLoadingProducts(true);
|
|
9606
10112
|
try {
|
|
@@ -9686,7 +10192,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
9686
10192
|
// src/hooks/useSubscriptionSync.ts
|
|
9687
10193
|
init_PaywalloClient();
|
|
9688
10194
|
init_SubscriptionCache();
|
|
9689
|
-
import { useCallback as useCallback18, useRef as
|
|
10195
|
+
import { useCallback as useCallback18, useRef as useRef10, useState as useState16 } from "react";
|
|
9690
10196
|
|
|
9691
10197
|
// src/hooks/subscriptionSyncHelpers.ts
|
|
9692
10198
|
init_SubscriptionCache();
|
|
@@ -9794,8 +10300,8 @@ async function checkPersistedCache(distinctId, onHit) {
|
|
|
9794
10300
|
|
|
9795
10301
|
// src/hooks/useSubscriptionSync.ts
|
|
9796
10302
|
function useSubscriptionSync() {
|
|
9797
|
-
const [subscription, setSubscription] =
|
|
9798
|
-
const cacheRef =
|
|
10303
|
+
const [subscription, setSubscription] = useState16(null);
|
|
10304
|
+
const cacheRef = useRef10(null);
|
|
9799
10305
|
const setCacheAndState = useCallback18((sub) => {
|
|
9800
10306
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
9801
10307
|
setSubscription(sub);
|
|
@@ -9856,214 +10362,24 @@ function useSubscriptionSync() {
|
|
|
9856
10362
|
return { subscription, hasActiveSubscription, getSubscription, invalidateCache };
|
|
9857
10363
|
}
|
|
9858
10364
|
|
|
9859
|
-
// src/domains/paywall/SuperwallAutoBridge.ts
|
|
9860
|
-
init_PaywalloClient();
|
|
9861
|
-
|
|
9862
|
-
// src/core/utils/eventId.ts
|
|
9863
|
-
function hashString(input) {
|
|
9864
|
-
let h1 = 3735928559;
|
|
9865
|
-
let h2 = 1103547991;
|
|
9866
|
-
for (let i = 0; i < input.length; i++) {
|
|
9867
|
-
const ch = input.charCodeAt(i);
|
|
9868
|
-
h1 = Math.imul(h1 ^ ch, 2654435769);
|
|
9869
|
-
h2 = Math.imul(h2 ^ ch, 1598716949);
|
|
9870
|
-
}
|
|
9871
|
-
h1 = Math.imul(h1 ^ h1 >>> 16, 73244475);
|
|
9872
|
-
h1 ^= Math.imul(h2 ^ h2 >>> 13, 351452291);
|
|
9873
|
-
h2 = Math.imul(h2 ^ h2 >>> 16, 73244475);
|
|
9874
|
-
h2 ^= Math.imul(h1 ^ h1 >>> 13, 351452291);
|
|
9875
|
-
const toHex = (n) => (n >>> 0).toString(16).padStart(8, "0");
|
|
9876
|
-
return toHex(h1) + toHex(h2);
|
|
9877
|
-
}
|
|
9878
|
-
function buildDeterministicEventId(parts) {
|
|
9879
|
-
const raw = hashString(parts.join("|"));
|
|
9880
|
-
const p1 = raw.slice(0, 8);
|
|
9881
|
-
const p2 = raw.slice(8, 12);
|
|
9882
|
-
const p3 = "4" + raw.slice(13, 16);
|
|
9883
|
-
const p4 = (parseInt(raw[16], 16) & 3 | 8).toString(16) + raw.slice(17, 20);
|
|
9884
|
-
const p5 = raw.padEnd(32, "0").slice(20, 32);
|
|
9885
|
-
return `${p1}-${p2}-${p3}-${p4}-${p5}`;
|
|
9886
|
-
}
|
|
9887
|
-
|
|
9888
|
-
// src/domains/paywall/SuperwallAutoBridge.ts
|
|
9889
|
-
var bridgeStarted = false;
|
|
9890
|
-
var unsubscribe = null;
|
|
9891
|
-
var debugEnabled = false;
|
|
9892
|
-
function log(msg, data) {
|
|
9893
|
-
if (!debugEnabled) return;
|
|
9894
|
-
if (data !== void 0) {
|
|
9895
|
-
console.log(`[Paywallo:SuperwallBridge] ${msg}`, data);
|
|
9896
|
-
} else {
|
|
9897
|
-
console.log(`[Paywallo:SuperwallBridge] ${msg}`);
|
|
9898
|
-
}
|
|
9899
|
-
}
|
|
9900
|
-
function minuteBucket(ts) {
|
|
9901
|
-
return String(Math.floor(ts / 6e4));
|
|
9902
|
-
}
|
|
9903
|
-
function buildEventId(type, identifier, ts) {
|
|
9904
|
-
return buildDeterministicEventId(["sw", type, identifier, minuteBucket(ts)]);
|
|
9905
|
-
}
|
|
9906
|
-
async function trackPaywallEvent(type, identifier, ts, extra) {
|
|
9907
|
-
const _eventId = buildEventId(type, identifier, ts);
|
|
9908
|
-
try {
|
|
9909
|
-
const cleanExtra = {};
|
|
9910
|
-
if (extra) {
|
|
9911
|
-
for (const [k, v] of Object.entries(extra)) if (v !== void 0) cleanExtra[k] = v;
|
|
9912
|
-
}
|
|
9913
|
-
await PaywalloClient.track("paywall", {
|
|
9914
|
-
priority: "critical",
|
|
9915
|
-
properties: {
|
|
9916
|
-
type,
|
|
9917
|
-
paywall_id: identifier,
|
|
9918
|
-
...cleanExtra
|
|
9919
|
-
}
|
|
9920
|
-
});
|
|
9921
|
-
} catch (err) {
|
|
9922
|
-
log("track error", { type, identifier, eventId: _eventId, err: String(err) });
|
|
9923
|
-
}
|
|
9924
|
-
}
|
|
9925
|
-
function mapDismissToCloseReason(resultType) {
|
|
9926
|
-
switch (resultType) {
|
|
9927
|
-
case "purchased":
|
|
9928
|
-
return "purchased";
|
|
9929
|
-
case "restored":
|
|
9930
|
-
return "purchased";
|
|
9931
|
-
// restore counts as a purchase outcome for funnel
|
|
9932
|
-
case "declined":
|
|
9933
|
-
return "dismissed";
|
|
9934
|
-
default:
|
|
9935
|
-
return void 0;
|
|
9936
|
-
}
|
|
9937
|
-
}
|
|
9938
|
-
function buildCallbacks() {
|
|
9939
|
-
return {
|
|
9940
|
-
onPaywallPresent: (info) => {
|
|
9941
|
-
const ts = Date.now();
|
|
9942
|
-
log("onPaywallPresent", { identifier: info.identifier });
|
|
9943
|
-
void trackPaywallEvent("viewed", info.identifier, ts, {
|
|
9944
|
-
placement: info.presentedByEventWithName,
|
|
9945
|
-
variant_id: info.experiment?.variant?.id,
|
|
9946
|
-
campaign_id: info.experiment?.id
|
|
9947
|
-
});
|
|
9948
|
-
},
|
|
9949
|
-
onPaywallDismiss: (info, result) => {
|
|
9950
|
-
const ts = Date.now();
|
|
9951
|
-
log("onPaywallDismiss", { identifier: info.identifier, result: result.type });
|
|
9952
|
-
void trackPaywallEvent("closed", info.identifier, ts, {
|
|
9953
|
-
placement: info.presentedByEventWithName,
|
|
9954
|
-
variant_id: info.experiment?.variant?.id,
|
|
9955
|
-
campaign_id: info.experiment?.id,
|
|
9956
|
-
close_reason: mapDismissToCloseReason(result.type),
|
|
9957
|
-
closed_at: new Date(ts).toISOString()
|
|
9958
|
-
});
|
|
9959
|
-
},
|
|
9960
|
-
onPurchase: (params) => {
|
|
9961
|
-
const ts = Date.now();
|
|
9962
|
-
const safe = params !== null && typeof params === "object" ? params : {};
|
|
9963
|
-
const productId = "productId" in safe && typeof safe.productId === "string" ? safe.productId : "unknown";
|
|
9964
|
-
const identifier = "paywallIdentifier" in safe && typeof safe.paywallIdentifier === "string" ? safe.paywallIdentifier : productId;
|
|
9965
|
-
log("onPurchase", { productId, identifier });
|
|
9966
|
-
void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
|
|
9967
|
-
},
|
|
9968
|
-
// Global Superwall event listener — used specifically for `transactionComplete`
|
|
9969
|
-
// which is the only event that exposes the full product (currency/price) +
|
|
9970
|
-
// transaction (storeTransactionId) shape needed to create a server-side
|
|
9971
|
-
// `transactions` row. Other events are skipped (paywallOpen/Dismiss already
|
|
9972
|
-
// handled above with their typed callbacks).
|
|
9973
|
-
onSuperwallEvent: (info) => {
|
|
9974
|
-
const ev = info?.event;
|
|
9975
|
-
if (!ev || ev.event !== "transactionComplete") return;
|
|
9976
|
-
const ts = Date.now();
|
|
9977
|
-
const product = ev.product;
|
|
9978
|
-
const transaction = ev.transaction;
|
|
9979
|
-
if (!product) {
|
|
9980
|
-
log("transactionComplete missing product \u2014 skipping transaction track");
|
|
9981
|
-
return;
|
|
9982
|
-
}
|
|
9983
|
-
const productId = product.productIdentifier ?? product.id ?? "unknown";
|
|
9984
|
-
const currency = product.currencyCode ?? "USD";
|
|
9985
|
-
const price = product.price ?? 0;
|
|
9986
|
-
const transactionId = transaction?.storeTransactionId ?? transaction?.originalTransactionIdentifier ?? `${productId}_${String(ts)}`;
|
|
9987
|
-
const isTrial = product.hasFreeTrial === true || (product.trialPeriodPrice ?? 0) > 0;
|
|
9988
|
-
const paywallIdentifier = ev.paywallInfo?.identifier;
|
|
9989
|
-
log("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
|
|
9990
|
-
void (async () => {
|
|
9991
|
-
try {
|
|
9992
|
-
await PaywalloClient.track("transaction", {
|
|
9993
|
-
priority: "critical",
|
|
9994
|
-
properties: {
|
|
9995
|
-
type: isTrial ? "trial_started" : "completed",
|
|
9996
|
-
// Server's IngestTranslator looks for `transaction_id`/`product_id`
|
|
9997
|
-
// /`amount`/`currency`/`paywall_id` keys (not `tx_id`). PurchaseHandler
|
|
9998
|
-
// then resolves type=trial/new/renewal based on amount being 0 vs >0.
|
|
9999
|
-
transaction_id: transactionId,
|
|
10000
|
-
product_id: productId,
|
|
10001
|
-
amount: price,
|
|
10002
|
-
currency,
|
|
10003
|
-
...paywallIdentifier ? { paywall_id: paywallIdentifier } : {}
|
|
10004
|
-
}
|
|
10005
|
-
});
|
|
10006
|
-
} catch (err) {
|
|
10007
|
-
log("transactionComplete track error", { err: String(err) });
|
|
10008
|
-
}
|
|
10009
|
-
})();
|
|
10010
|
-
}
|
|
10011
|
-
};
|
|
10012
|
-
}
|
|
10013
|
-
function startSuperwallAutoBridge(debug = false) {
|
|
10014
|
-
debugEnabled = debug;
|
|
10015
|
-
if (bridgeStarted) {
|
|
10016
|
-
log("already started, skipping");
|
|
10017
|
-
return;
|
|
10018
|
-
}
|
|
10019
|
-
void detectAndStart();
|
|
10020
|
-
}
|
|
10021
|
-
async function detectAndStart() {
|
|
10022
|
-
let subscribeFn;
|
|
10023
|
-
try {
|
|
10024
|
-
const mod = await import("expo-superwall");
|
|
10025
|
-
if (!mod || typeof mod.subscribeToSuperwallEvents !== "function") {
|
|
10026
|
-
const internal = await import("expo-superwall/build/src/internal/superwallEventBridge");
|
|
10027
|
-
if (typeof internal.subscribeToSuperwallEvents !== "function") {
|
|
10028
|
-
log("expo-superwall subscribeToSuperwallEvents not found \u2014 bridge inactive");
|
|
10029
|
-
return;
|
|
10030
|
-
}
|
|
10031
|
-
subscribeFn = internal.subscribeToSuperwallEvents;
|
|
10032
|
-
} else {
|
|
10033
|
-
subscribeFn = mod.subscribeToSuperwallEvents;
|
|
10034
|
-
}
|
|
10035
|
-
} catch {
|
|
10036
|
-
log("expo-superwall not installed \u2014 bridge inactive");
|
|
10037
|
-
return;
|
|
10038
|
-
}
|
|
10039
|
-
try {
|
|
10040
|
-
const callbacks = buildCallbacks();
|
|
10041
|
-
unsubscribe = subscribeFn(void 0, () => callbacks);
|
|
10042
|
-
bridgeStarted = true;
|
|
10043
|
-
log("bridge started");
|
|
10044
|
-
} catch (err) {
|
|
10045
|
-
log("subscribe failed", { err: String(err) });
|
|
10046
|
-
}
|
|
10047
|
-
}
|
|
10048
|
-
|
|
10049
10365
|
// src/PaywalloProvider.tsx
|
|
10050
10366
|
init_localization();
|
|
10051
|
-
import { jsx as
|
|
10367
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
10052
10368
|
function PaywalloProvider({ children, config }) {
|
|
10053
|
-
const [isInitialized, setIsInitialized] =
|
|
10054
|
-
const [initError, setInitError] =
|
|
10055
|
-
const [distinctId, setDistinctId] =
|
|
10056
|
-
const [activePaywall, setActivePaywall] =
|
|
10369
|
+
const [isInitialized, setIsInitialized] = useState17(false);
|
|
10370
|
+
const [initError, setInitError] = useState17(null);
|
|
10371
|
+
const [distinctId, setDistinctId] = useState17(null);
|
|
10372
|
+
const [activePaywall, setActivePaywall] = useState17(null);
|
|
10057
10373
|
const { products, isLoadingProducts, refreshProducts } = useProductLoader();
|
|
10058
10374
|
const { subscription, hasActiveSubscription, getSubscription, invalidateCache } = useSubscriptionSync();
|
|
10059
10375
|
const { preloadedWebView, setPreloadedWebView, preloadCampaign, consumePreloadedCampaign, isPreloadValid } = useCampaignPreload();
|
|
10060
10376
|
const clearPreloadedWebView = useCallback19(() => setPreloadedWebView(null), [setPreloadedWebView]);
|
|
10061
|
-
const autoPreloadTriggeredRef =
|
|
10062
|
-
const preloadedWebViewRef =
|
|
10063
|
-
const pollTimerRef =
|
|
10064
|
-
const pollCancelledRef =
|
|
10065
|
-
const secureStorageRef =
|
|
10066
|
-
|
|
10377
|
+
const autoPreloadTriggeredRef = useRef11(false);
|
|
10378
|
+
const preloadedWebViewRef = useRef11(preloadedWebView);
|
|
10379
|
+
const pollTimerRef = useRef11(null);
|
|
10380
|
+
const pollCancelledRef = useRef11(false);
|
|
10381
|
+
const secureStorageRef = useRef11(new SecureStorage(config.debug));
|
|
10382
|
+
useEffect14(() => {
|
|
10067
10383
|
preloadedWebViewRef.current = preloadedWebView;
|
|
10068
10384
|
}, [preloadedWebView]);
|
|
10069
10385
|
useAppAutoEvents({
|
|
@@ -10094,8 +10410,8 @@ function PaywalloProvider({ children, config }) {
|
|
|
10094
10410
|
});
|
|
10095
10411
|
}, []);
|
|
10096
10412
|
const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady: baseHandlePreloadedWebViewReady, handlePreloadedClose: baseHandlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, setPreloadedWebView);
|
|
10097
|
-
const SCREEN_HEIGHT =
|
|
10098
|
-
const slideAnim =
|
|
10413
|
+
const SCREEN_HEIGHT = useMemo3(() => Dimensions5.get("window").height, []);
|
|
10414
|
+
const slideAnim = useRef11(new Animated4.Value(SCREEN_HEIGHT)).current;
|
|
10099
10415
|
const handlePreloadedWebViewReady = useCallback19(() => {
|
|
10100
10416
|
if (PaywalloClient.getConfig()?.debug) {
|
|
10101
10417
|
console.log(`[Paywallo WEBVIEW] loaded \u2014 placement: ${preloadedWebView?.placement}`);
|
|
@@ -10104,7 +10420,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10104
10420
|
}, [preloadedWebView?.placement, baseHandlePreloadedWebViewReady]);
|
|
10105
10421
|
const handlePreloadedClose = useCallback19(() => {
|
|
10106
10422
|
const placement = preloadedWebView?.placement;
|
|
10107
|
-
|
|
10423
|
+
Animated4.timing(slideAnim, { toValue: SCREEN_HEIGHT, duration: 200, easing: Easing2.in(Easing2.cubic), useNativeDriver: true }).start(() => {
|
|
10108
10424
|
baseHandlePreloadedClose();
|
|
10109
10425
|
if (placement) triggerRepreload(placement);
|
|
10110
10426
|
});
|
|
@@ -10117,7 +10433,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10117
10433
|
presentedAtRef,
|
|
10118
10434
|
invalidateCache
|
|
10119
10435
|
);
|
|
10120
|
-
const [isPreloadPurchasing, setIsPreloadPurchasing] =
|
|
10436
|
+
const [isPreloadPurchasing, setIsPreloadPurchasing] = useState17(false);
|
|
10121
10437
|
const handlePreloadedPurchase = useCallback19(async (productId) => {
|
|
10122
10438
|
setIsPreloadPurchasing(true);
|
|
10123
10439
|
try {
|
|
@@ -10126,8 +10442,8 @@ function PaywalloProvider({ children, config }) {
|
|
|
10126
10442
|
setIsPreloadPurchasing(false);
|
|
10127
10443
|
}
|
|
10128
10444
|
}, [rawPreloadedPurchase]);
|
|
10129
|
-
const configRef =
|
|
10130
|
-
|
|
10445
|
+
const configRef = useRef11(config);
|
|
10446
|
+
useEffect14(() => {
|
|
10131
10447
|
let mounted = true;
|
|
10132
10448
|
initLocalization({ detectDevice: true });
|
|
10133
10449
|
PaywalloClient.init(configRef.current).then(async () => {
|
|
@@ -10140,6 +10456,11 @@ function PaywalloProvider({ children, config }) {
|
|
|
10140
10456
|
const placement = await PaywalloClient.waitForAutoPreloadedPlacement();
|
|
10141
10457
|
if (mounted && placement) void preloadCampaign(placement).catch(() => {
|
|
10142
10458
|
});
|
|
10459
|
+
const preloadPlacements = configRef.current.preloadPaywalls;
|
|
10460
|
+
if (mounted && preloadPlacements && preloadPlacements.length > 0) {
|
|
10461
|
+
void PaywalloClient.preloadPaywalls(preloadPlacements).catch(() => {
|
|
10462
|
+
});
|
|
10463
|
+
}
|
|
10143
10464
|
}
|
|
10144
10465
|
}).catch((err) => {
|
|
10145
10466
|
if (!mounted) return;
|
|
@@ -10154,7 +10475,22 @@ function PaywalloProvider({ children, config }) {
|
|
|
10154
10475
|
return api ? api.getPaywall(p) : null;
|
|
10155
10476
|
}, []);
|
|
10156
10477
|
const presentPaywall = useCallback19((placement) => {
|
|
10157
|
-
|
|
10478
|
+
const preloaded = paywallPreloadService.getPreloaded(placement);
|
|
10479
|
+
return new Promise((resolve) => setActivePaywall({
|
|
10480
|
+
placement,
|
|
10481
|
+
resolver: resolve,
|
|
10482
|
+
...preloaded && {
|
|
10483
|
+
paywallConfig: {
|
|
10484
|
+
id: preloaded.config.id,
|
|
10485
|
+
placement: preloaded.config.placement,
|
|
10486
|
+
config: preloaded.config.config,
|
|
10487
|
+
primaryProductId: preloaded.config.primaryProductId,
|
|
10488
|
+
secondaryProductId: preloaded.config.secondaryProductId,
|
|
10489
|
+
tertiaryProductId: preloaded.config.tertiaryProductId
|
|
10490
|
+
},
|
|
10491
|
+
preloadedProducts: preloaded.products
|
|
10492
|
+
}
|
|
10493
|
+
}));
|
|
10158
10494
|
}, []);
|
|
10159
10495
|
const trackCampaignImpression = useCallback19((placement, variantKey, campaignId) => {
|
|
10160
10496
|
const api = PaywalloClient.getApiClient();
|
|
@@ -10288,7 +10624,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10288
10624
|
}
|
|
10289
10625
|
return presentCampaign(placement);
|
|
10290
10626
|
}, [presentPreloadedWebView, presentCampaign]);
|
|
10291
|
-
|
|
10627
|
+
useEffect14(() => {
|
|
10292
10628
|
if (!isInitialized) return;
|
|
10293
10629
|
const onEmergency = async (id) => {
|
|
10294
10630
|
try {
|
|
@@ -10317,7 +10653,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10317
10653
|
const isReady = isInitialized && !isLoadingProducts;
|
|
10318
10654
|
const noOp = useCallback19(() => {
|
|
10319
10655
|
}, []);
|
|
10320
|
-
const contextValue =
|
|
10656
|
+
const contextValue = useMemo3(() => ({
|
|
10321
10657
|
config: PaywalloClient.getConfig(),
|
|
10322
10658
|
isInitialized,
|
|
10323
10659
|
isReady,
|
|
@@ -10354,17 +10690,17 @@ function PaywalloProvider({ children, config }) {
|
|
|
10354
10690
|
useLayoutEffect(() => {
|
|
10355
10691
|
if (showingPreloadedWebView) {
|
|
10356
10692
|
slideAnim.setValue(SCREEN_HEIGHT);
|
|
10357
|
-
|
|
10693
|
+
Animated4.timing(slideAnim, { toValue: 0, duration: 200, easing: Easing2.out(Easing2.cubic), useNativeDriver: true }).start();
|
|
10358
10694
|
}
|
|
10359
10695
|
}, [showingPreloadedWebView, slideAnim, SCREEN_HEIGHT]);
|
|
10360
10696
|
const preloadStyle = [
|
|
10361
|
-
|
|
10362
|
-
showingPreloadedWebView ?
|
|
10697
|
+
styles5.preloadOverlay,
|
|
10698
|
+
showingPreloadedWebView ? styles5.visible : styles5.hidden,
|
|
10363
10699
|
showingPreloadedWebView ? { transform: [{ translateY: slideAnim }] } : void 0
|
|
10364
10700
|
];
|
|
10365
|
-
return /* @__PURE__ */
|
|
10701
|
+
return /* @__PURE__ */ jsxs3(PaywalloContext.Provider, { value: contextValue, children: [
|
|
10366
10702
|
children,
|
|
10367
|
-
/* @__PURE__ */
|
|
10703
|
+
/* @__PURE__ */ jsx5(PaywallErrorBoundary, { children: !!preloadedWebView?.craftData && /* @__PURE__ */ jsx5(Animated4.View, { style: preloadStyle, pointerEvents: showingPreloadedWebView ? "auto" : "none", children: /* @__PURE__ */ jsx5(
|
|
10368
10704
|
PaywallWebView,
|
|
10369
10705
|
{
|
|
10370
10706
|
paywallId: preloadedWebView.paywallId,
|
|
@@ -10380,7 +10716,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10380
10716
|
onRestore: showingPreloadedWebView ? handlePreloadedRestore : noOp
|
|
10381
10717
|
}
|
|
10382
10718
|
) }) }),
|
|
10383
|
-
/* @__PURE__ */
|
|
10719
|
+
/* @__PURE__ */ jsx5(PaywallErrorBoundary, { children: activePaywall && /* @__PURE__ */ jsx5(
|
|
10384
10720
|
PaywallModal,
|
|
10385
10721
|
{
|
|
10386
10722
|
placement: activePaywall.placement,
|
|
@@ -10392,10 +10728,11 @@ function PaywalloProvider({ children, config }) {
|
|
|
10392
10728
|
campaignId: activePaywall.campaignId,
|
|
10393
10729
|
variantId: activePaywall.variantId
|
|
10394
10730
|
}
|
|
10395
|
-
) })
|
|
10731
|
+
) }),
|
|
10732
|
+
isInitialized && (config.preloadPaywalls?.length ?? 0) > 0 && PaywalloClient.getWebUrl() !== null && /* @__PURE__ */ jsx5(PaywallWebViewPrewarmer, { webUrl: PaywalloClient.getWebUrl() })
|
|
10396
10733
|
] });
|
|
10397
10734
|
}
|
|
10398
|
-
var
|
|
10735
|
+
var styles5 = StyleSheet5.create({
|
|
10399
10736
|
preloadOverlay: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#ffffff" },
|
|
10400
10737
|
hidden: { zIndex: -1, opacity: 0, width: 1, height: 1, overflow: "hidden" },
|
|
10401
10738
|
visible: { zIndex: 9999, opacity: 1 }
|
|
@@ -10451,6 +10788,9 @@ function resolveProductsVariable(parts, context) {
|
|
|
10451
10788
|
if (parts[1] === "secondary" && context.secondaryProductId) {
|
|
10452
10789
|
return getProductVariable(context.products.get(context.secondaryProductId), parts[2]);
|
|
10453
10790
|
}
|
|
10791
|
+
if (parts[1] === "tertiary" && context.tertiaryProductId) {
|
|
10792
|
+
return getProductVariable(context.products.get(context.tertiaryProductId), parts[2]);
|
|
10793
|
+
}
|
|
10454
10794
|
if (parts[1] === "hasIntroductoryOffer") {
|
|
10455
10795
|
return Array.from(context.products.values()).some((p) => p.introductoryPrice) ? "true" : "false";
|
|
10456
10796
|
}
|