@virex-tech/paywallo-sdk 2.2.5 → 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/android/src/main/java/com/paywallo/sdk/PaywalloEnvModule.kt +31 -0
- package/android/src/main/java/com/paywallo/sdk/PaywalloSdkPackage.kt +2 -1
- package/dist/index.d.mts +30 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +1340 -682
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1251 -594
- package/dist/index.mjs.map +1 -1
- package/ios/PaywalloEnv.m +8 -0
- package/ios/PaywalloEnv.swift +28 -0
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -814,9 +814,9 @@ var init_CampaignGateService = __esm({
|
|
|
814
814
|
}
|
|
815
815
|
}
|
|
816
816
|
getPreloadedCampaign(placement) {
|
|
817
|
-
const
|
|
818
|
-
if (!
|
|
819
|
-
const age = Date.now() -
|
|
817
|
+
const cached2 = this.preloadedCampaigns.get(placement);
|
|
818
|
+
if (!cached2) return null;
|
|
819
|
+
const age = Date.now() - cached2.preloadedAt;
|
|
820
820
|
if (age >= PRELOAD_CACHE_TTL_MS) {
|
|
821
821
|
this.preloadedCampaigns.delete(placement);
|
|
822
822
|
return null;
|
|
@@ -825,7 +825,7 @@ var init_CampaignGateService = __esm({
|
|
|
825
825
|
void this.preloadCampaign(this.apiClientRef, placement).catch(() => {
|
|
826
826
|
});
|
|
827
827
|
}
|
|
828
|
-
return
|
|
828
|
+
return cached2.campaign;
|
|
829
829
|
}
|
|
830
830
|
async preloadAllActive(apiClient, debug) {
|
|
831
831
|
this.apiClientRef = apiClient;
|
|
@@ -2157,7 +2157,7 @@ function extractPayload(data) {
|
|
|
2157
2157
|
}
|
|
2158
2158
|
function setupForegroundHandler(deps, onReceive) {
|
|
2159
2159
|
const { bridge, tracker, debug } = deps;
|
|
2160
|
-
const
|
|
2160
|
+
const unsubscribe2 = bridge.onMessage(async (message) => {
|
|
2161
2161
|
const data = message.data ?? {};
|
|
2162
2162
|
const payload = extractPayload({
|
|
2163
2163
|
...data,
|
|
@@ -2175,7 +2175,7 @@ function setupForegroundHandler(deps, onReceive) {
|
|
|
2175
2175
|
messageId
|
|
2176
2176
|
});
|
|
2177
2177
|
});
|
|
2178
|
-
return
|
|
2178
|
+
return unsubscribe2;
|
|
2179
2179
|
}
|
|
2180
2180
|
var init_ForegroundHandler = __esm({
|
|
2181
2181
|
"src/domains/notifications/handlers/ForegroundHandler.ts"() {
|
|
@@ -2370,8 +2370,8 @@ var init_NotificationHandlerSetup = __esm({
|
|
|
2370
2370
|
// src/core/version.ts
|
|
2371
2371
|
function resolveVersion() {
|
|
2372
2372
|
try {
|
|
2373
|
-
if ("2.2.
|
|
2374
|
-
return "2.2.
|
|
2373
|
+
if ("2.2.7") {
|
|
2374
|
+
return "2.2.7";
|
|
2375
2375
|
}
|
|
2376
2376
|
} catch {
|
|
2377
2377
|
}
|
|
@@ -2444,9 +2444,6 @@ function mapPermissionStatus(status) {
|
|
|
2444
2444
|
function detectPlatform2() {
|
|
2445
2445
|
return Platform6.OS === "ios" ? "ios" : "android";
|
|
2446
2446
|
}
|
|
2447
|
-
function detectEnvironment() {
|
|
2448
|
-
return typeof __DEV__ !== "undefined" && __DEV__ ? "sandbox" : "production";
|
|
2449
|
-
}
|
|
2450
2447
|
async function getAppVersion() {
|
|
2451
2448
|
try {
|
|
2452
2449
|
const { collectDeviceInfo: collectDeviceInfo2 } = await Promise.resolve().then(() => (init_deviceInfo(), deviceInfo_exports));
|
|
@@ -2486,7 +2483,7 @@ async function clearPersistedToken(secureStorage2) {
|
|
|
2486
2483
|
} catch {
|
|
2487
2484
|
}
|
|
2488
2485
|
}
|
|
2489
|
-
async function buildRegistration(token, getDeviceId, getDistinctId) {
|
|
2486
|
+
async function buildRegistration(token, getDeviceId, getDistinctId, environment) {
|
|
2490
2487
|
const appVersion = await getAppVersion();
|
|
2491
2488
|
return {
|
|
2492
2489
|
token,
|
|
@@ -2497,7 +2494,7 @@ async function buildRegistration(token, getDeviceId, getDistinctId) {
|
|
|
2497
2494
|
deviceId: getDeviceId?.() ?? void 0,
|
|
2498
2495
|
locale: await getLocale(),
|
|
2499
2496
|
timezone: getTimezone2(),
|
|
2500
|
-
environment
|
|
2497
|
+
environment
|
|
2501
2498
|
};
|
|
2502
2499
|
}
|
|
2503
2500
|
async function registerToken(deps, token) {
|
|
@@ -2506,7 +2503,8 @@ async function registerToken(deps, token) {
|
|
|
2506
2503
|
if (debug) console.log("[Paywallo PUSH] httpClient not ready, skipping token registration");
|
|
2507
2504
|
return;
|
|
2508
2505
|
}
|
|
2509
|
-
const
|
|
2506
|
+
const { environment } = deps;
|
|
2507
|
+
const registration = await buildRegistration(token, getDeviceId, getDistinctId, environment);
|
|
2510
2508
|
try {
|
|
2511
2509
|
await httpClient.post(
|
|
2512
2510
|
TOKEN_ENDPOINT,
|
|
@@ -2556,7 +2554,8 @@ async function registerAndPersistToken(deps, token) {
|
|
|
2556
2554
|
appKey: deps.appKey,
|
|
2557
2555
|
getDeviceId: deps.getDeviceId,
|
|
2558
2556
|
getDistinctId: deps.getDistinctId,
|
|
2559
|
-
debug: deps.debug
|
|
2557
|
+
debug: deps.debug,
|
|
2558
|
+
environment: deps.environment
|
|
2560
2559
|
},
|
|
2561
2560
|
token
|
|
2562
2561
|
);
|
|
@@ -2573,7 +2572,8 @@ function subscribeTokenRefresh(bridge, deps, onToken) {
|
|
|
2573
2572
|
appKey: deps.appKey,
|
|
2574
2573
|
getDeviceId: deps.getDeviceId,
|
|
2575
2574
|
getDistinctId: deps.getDistinctId,
|
|
2576
|
-
debug: deps.debug
|
|
2575
|
+
debug: deps.debug,
|
|
2576
|
+
environment: deps.environment
|
|
2577
2577
|
},
|
|
2578
2578
|
newToken
|
|
2579
2579
|
).catch(() => void 0);
|
|
@@ -2781,6 +2781,7 @@ var init_NotificationsManager = __esm({
|
|
|
2781
2781
|
this.getDistinctId = null;
|
|
2782
2782
|
this.appKey = null;
|
|
2783
2783
|
this.apiBaseUrl = null;
|
|
2784
|
+
this.environment = "production";
|
|
2784
2785
|
this.tokenRefreshCleanup = null;
|
|
2785
2786
|
this.foregroundCleanup = null;
|
|
2786
2787
|
this.pendingHandlersConfig = null;
|
|
@@ -2800,6 +2801,7 @@ var init_NotificationsManager = __esm({
|
|
|
2800
2801
|
this.secureStorage = deps.secureStorage;
|
|
2801
2802
|
this.getDeviceId = deps.getDeviceId;
|
|
2802
2803
|
if (deps.getDistinctId) this.getDistinctId = deps.getDistinctId;
|
|
2804
|
+
if (deps.environment) this.environment = deps.environment;
|
|
2803
2805
|
}
|
|
2804
2806
|
async initialize(config) {
|
|
2805
2807
|
this.applyConfig(config);
|
|
@@ -2842,7 +2844,8 @@ var init_NotificationsManager = __esm({
|
|
|
2842
2844
|
appKey: this.appKey,
|
|
2843
2845
|
getDeviceId: this.getDeviceId,
|
|
2844
2846
|
getDistinctId: this.getDistinctId,
|
|
2845
|
-
debug: this.debug
|
|
2847
|
+
debug: this.debug,
|
|
2848
|
+
environment: this.environment
|
|
2846
2849
|
};
|
|
2847
2850
|
}
|
|
2848
2851
|
async doRegisterToken(token) {
|
|
@@ -3038,6 +3041,41 @@ var init_emitPaywallClosed = __esm({
|
|
|
3038
3041
|
}
|
|
3039
3042
|
});
|
|
3040
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
|
+
|
|
3041
3079
|
// src/domains/paywall/parseWebViewMessage.ts
|
|
3042
3080
|
function deriveMessageId(message) {
|
|
3043
3081
|
if (message.id) return message.id;
|
|
@@ -3280,6 +3318,66 @@ var init_localization = __esm({
|
|
|
3280
3318
|
}
|
|
3281
3319
|
});
|
|
3282
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
|
+
|
|
3283
3381
|
// src/domains/paywall/paywallScripts.ts
|
|
3284
3382
|
function buildPaywallInjectionScript(data) {
|
|
3285
3383
|
const paywallData = {
|
|
@@ -3324,7 +3422,7 @@ var init_paywallScripts = __esm({
|
|
|
3324
3422
|
});
|
|
3325
3423
|
|
|
3326
3424
|
// src/domains/paywall/PaywallWebView.tsx
|
|
3327
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3425
|
+
import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState } from "react";
|
|
3328
3426
|
import {
|
|
3329
3427
|
BackHandler,
|
|
3330
3428
|
findNodeHandle,
|
|
@@ -3333,11 +3431,11 @@ import {
|
|
|
3333
3431
|
NativeModules as NativeModules6,
|
|
3334
3432
|
Platform as Platform9,
|
|
3335
3433
|
requireNativeComponent,
|
|
3336
|
-
StyleSheet,
|
|
3434
|
+
StyleSheet as StyleSheet2,
|
|
3337
3435
|
UIManager,
|
|
3338
|
-
View
|
|
3436
|
+
View as View2
|
|
3339
3437
|
} from "react-native";
|
|
3340
|
-
import { jsx } from "react/jsx-runtime";
|
|
3438
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
3341
3439
|
function getNativeWebView() {
|
|
3342
3440
|
if (NativeWebViewComponent) return NativeWebViewComponent;
|
|
3343
3441
|
if (nativeWebViewLookupFailed) return null;
|
|
@@ -3395,16 +3493,16 @@ function PaywallWebView({
|
|
|
3395
3493
|
onReady,
|
|
3396
3494
|
onError
|
|
3397
3495
|
}) {
|
|
3398
|
-
const paywallDataSent =
|
|
3399
|
-
const loadEndReceived =
|
|
3496
|
+
const paywallDataSent = useRef2(false);
|
|
3497
|
+
const loadEndReceived = useRef2(false);
|
|
3400
3498
|
const [scriptToInject, setScriptToInject] = useState(void 0);
|
|
3401
|
-
const webViewRef =
|
|
3402
|
-
const onErrorRef =
|
|
3499
|
+
const webViewRef = useRef2(null);
|
|
3500
|
+
const onErrorRef = useRef2(onError);
|
|
3403
3501
|
onErrorRef.current = onError;
|
|
3404
|
-
const onReadyRef =
|
|
3502
|
+
const onReadyRef = useRef2(onReady);
|
|
3405
3503
|
onReadyRef.current = onReady;
|
|
3406
3504
|
const NativeWebView = useMemo(() => getNativeWebView(), []);
|
|
3407
|
-
|
|
3505
|
+
useEffect2(() => {
|
|
3408
3506
|
if (!NativeWebView) {
|
|
3409
3507
|
onErrorRef.current?.({
|
|
3410
3508
|
code: -1,
|
|
@@ -3415,7 +3513,7 @@ function PaywallWebView({
|
|
|
3415
3513
|
const [resolvedWebUrl, setResolvedWebUrl] = useState(
|
|
3416
3514
|
() => webUrlProp ?? PaywalloClient.getWebUrl()
|
|
3417
3515
|
);
|
|
3418
|
-
|
|
3516
|
+
useEffect2(() => {
|
|
3419
3517
|
if (webUrlProp !== void 0) {
|
|
3420
3518
|
setResolvedWebUrl(webUrlProp ?? null);
|
|
3421
3519
|
return;
|
|
@@ -3434,7 +3532,7 @@ function PaywallWebView({
|
|
|
3434
3532
|
}, 200);
|
|
3435
3533
|
return () => clearInterval(interval);
|
|
3436
3534
|
}, [webUrlProp]);
|
|
3437
|
-
|
|
3535
|
+
useEffect2(() => {
|
|
3438
3536
|
if (!resolvedWebUrl) {
|
|
3439
3537
|
onErrorRef.current?.({
|
|
3440
3538
|
code: -1,
|
|
@@ -3462,9 +3560,9 @@ function PaywallWebView({
|
|
|
3462
3560
|
});
|
|
3463
3561
|
injectScript(script);
|
|
3464
3562
|
}, [craftData, products, primaryProductId, secondaryProductId, injectScript]);
|
|
3465
|
-
const processedIdsRef =
|
|
3466
|
-
const dedupeTimersRef =
|
|
3467
|
-
|
|
3563
|
+
const processedIdsRef = useRef2(/* @__PURE__ */ new Set());
|
|
3564
|
+
const dedupeTimersRef = useRef2(/* @__PURE__ */ new Map());
|
|
3565
|
+
useEffect2(() => {
|
|
3468
3566
|
return () => {
|
|
3469
3567
|
for (const timerId of dedupeTimersRef.current.values()) clearTimeout(timerId);
|
|
3470
3568
|
dedupeTimersRef.current.clear();
|
|
@@ -3516,7 +3614,7 @@ function PaywallWebView({
|
|
|
3516
3614
|
},
|
|
3517
3615
|
[buildAndSetInjectionScript, onPurchase, onClose, onRestore]
|
|
3518
3616
|
);
|
|
3519
|
-
|
|
3617
|
+
useEffect2(() => {
|
|
3520
3618
|
const emitter = getWebViewEmitter();
|
|
3521
3619
|
if (!emitter) return;
|
|
3522
3620
|
const loadEndSub = emitter.addListener("PaywalloWebViewLoadEnd", () => {
|
|
@@ -3541,7 +3639,7 @@ function PaywallWebView({
|
|
|
3541
3639
|
errorSub.remove();
|
|
3542
3640
|
};
|
|
3543
3641
|
}, [buildAndSetInjectionScript, handleParsedMessage]);
|
|
3544
|
-
|
|
3642
|
+
useEffect2(() => {
|
|
3545
3643
|
const timer = setTimeout(() => {
|
|
3546
3644
|
if (!paywallDataSent.current) {
|
|
3547
3645
|
onErrorRef.current?.({
|
|
@@ -3581,11 +3679,11 @@ function PaywallWebView({
|
|
|
3581
3679
|
},
|
|
3582
3680
|
[handleParsedMessage]
|
|
3583
3681
|
);
|
|
3584
|
-
|
|
3682
|
+
useEffect2(() => {
|
|
3585
3683
|
if (!paywallDataSent.current) return;
|
|
3586
3684
|
injectScript(buildPurchaseStateScript(isPurchasing));
|
|
3587
3685
|
}, [isPurchasing, injectScript]);
|
|
3588
|
-
|
|
3686
|
+
useEffect2(() => {
|
|
3589
3687
|
if (Platform9.OS !== "android") return;
|
|
3590
3688
|
const onBackPress = () => {
|
|
3591
3689
|
if (!paywallDataSent.current) return false;
|
|
@@ -3597,7 +3695,7 @@ function PaywallWebView({
|
|
|
3597
3695
|
}, [injectScript]);
|
|
3598
3696
|
if (!resolvedWebUrl || !NativeWebView) return null;
|
|
3599
3697
|
const paywallUrl = `${resolvedWebUrl}/paywall/${paywallId}`;
|
|
3600
|
-
return /* @__PURE__ */
|
|
3698
|
+
return /* @__PURE__ */ jsx2(View2, { style: styles2.container, children: /* @__PURE__ */ jsx2(
|
|
3601
3699
|
NativeWebView,
|
|
3602
3700
|
{
|
|
3603
3701
|
ref: webViewRef,
|
|
@@ -3606,11 +3704,11 @@ function PaywallWebView({
|
|
|
3606
3704
|
onMessage: handleMessage,
|
|
3607
3705
|
onLoadEnd: handleLoadEnd,
|
|
3608
3706
|
onError: handleNativeError,
|
|
3609
|
-
style:
|
|
3707
|
+
style: styles2.webview
|
|
3610
3708
|
}
|
|
3611
3709
|
) });
|
|
3612
3710
|
}
|
|
3613
|
-
var NativeWebViewComponent, nativeWebViewLookupFailed, webViewEmitter,
|
|
3711
|
+
var NativeWebViewComponent, nativeWebViewLookupFailed, webViewEmitter, styles2;
|
|
3614
3712
|
var init_PaywallWebView = __esm({
|
|
3615
3713
|
"src/domains/paywall/PaywallWebView.tsx"() {
|
|
3616
3714
|
"use strict";
|
|
@@ -3620,7 +3718,7 @@ var init_PaywallWebView = __esm({
|
|
|
3620
3718
|
NativeWebViewComponent = null;
|
|
3621
3719
|
nativeWebViewLookupFailed = false;
|
|
3622
3720
|
webViewEmitter = null;
|
|
3623
|
-
|
|
3721
|
+
styles2 = StyleSheet2.create({
|
|
3624
3722
|
container: { flex: 1 },
|
|
3625
3723
|
webview: { flex: 1, backgroundColor: "transparent" }
|
|
3626
3724
|
});
|
|
@@ -3914,6 +4012,32 @@ var init_IAPTransactionEmitter = __esm({
|
|
|
3914
4012
|
if (this.getDebug()) console.log("[Paywallo PURCHASE] transaction event emission failed", err);
|
|
3915
4013
|
}
|
|
3916
4014
|
}
|
|
4015
|
+
/**
|
|
4016
|
+
* Emits `checkout_started` quando o usuário inicia a compra (toca em comprar),
|
|
4017
|
+
* antes do StoreKit/Billing abrir — conta mesmo que a sheet nativa seja cancelada.
|
|
4018
|
+
* Vira InitiateCheckout no Meta/TikTok. Fire-and-forget — uma falha de tracking
|
|
4019
|
+
* nunca pode travar a compra.
|
|
4020
|
+
*/
|
|
4021
|
+
async emitCheckoutStarted(productId) {
|
|
4022
|
+
try {
|
|
4023
|
+
const apiClient = this.getApiClientFn();
|
|
4024
|
+
if (!apiClient) return;
|
|
4025
|
+
const distinctId = PaywalloClient.getDistinctId();
|
|
4026
|
+
if (!distinctId) return;
|
|
4027
|
+
const product = this.getProductFromCache(productId);
|
|
4028
|
+
const properties = {
|
|
4029
|
+
product_id: productId,
|
|
4030
|
+
amount: product?.priceValue ?? 0,
|
|
4031
|
+
currency: product?.currency ?? "USD"
|
|
4032
|
+
};
|
|
4033
|
+
await apiClient.trackEvent("checkout_started", distinctId, properties, void 0, {
|
|
4034
|
+
priority: "critical"
|
|
4035
|
+
});
|
|
4036
|
+
if (this.getDebug()) console.log("[Paywallo PURCHASE] emitted checkout_started", productId);
|
|
4037
|
+
} catch (err) {
|
|
4038
|
+
if (this.getDebug()) console.log("[Paywallo PURCHASE] checkout_started emission failed", err);
|
|
4039
|
+
}
|
|
4040
|
+
}
|
|
3917
4041
|
/**
|
|
3918
4042
|
* Forwards a native transaction update to the EventBatcher.
|
|
3919
4043
|
* Public so tests can exercise the wiring without a fake NativeEventEmitter.
|
|
@@ -3936,9 +4060,9 @@ var init_IAPTransactionEmitter = __esm({
|
|
|
3936
4060
|
product_id: update.productId
|
|
3937
4061
|
};
|
|
3938
4062
|
if (update.type !== "canceled") {
|
|
3939
|
-
const
|
|
3940
|
-
const amount = update.amount ??
|
|
3941
|
-
const currency = update.currency ??
|
|
4063
|
+
const cached2 = this.getProductFromCache(update.productId);
|
|
4064
|
+
const amount = update.amount ?? cached2?.priceValue;
|
|
4065
|
+
const currency = update.currency ?? cached2?.currency;
|
|
3942
4066
|
if (typeof amount === "number" && amount > 0) properties.amount = amount;
|
|
3943
4067
|
if (currency) properties.currency = currency;
|
|
3944
4068
|
}
|
|
@@ -4117,10 +4241,10 @@ var init_OfflineQueueStorage = __esm({
|
|
|
4117
4241
|
init_NativeStorage();
|
|
4118
4242
|
init_QueueJournal();
|
|
4119
4243
|
OfflineQueueStorage = class {
|
|
4120
|
-
constructor(config, emit,
|
|
4244
|
+
constructor(config, emit, log2) {
|
|
4121
4245
|
this.config = config;
|
|
4122
4246
|
this.emit = emit;
|
|
4123
|
-
this.log =
|
|
4247
|
+
this.log = log2;
|
|
4124
4248
|
}
|
|
4125
4249
|
async loadFromStorage(queue) {
|
|
4126
4250
|
const { merged, recovered, hadJournal } = await mergeJournalIntoMain(
|
|
@@ -4739,6 +4863,7 @@ var init_QueueProcessor = __esm({
|
|
|
4739
4863
|
for (const item of batch) {
|
|
4740
4864
|
retryIds.add(item.id);
|
|
4741
4865
|
}
|
|
4866
|
+
console.error("[Paywallo:QUEUE] event batch network error \u2014 items will retry", { size: batch.length, error: error instanceof Error ? error.message : String(error) });
|
|
4742
4867
|
this.log("Event batch threw network error - will retry", {
|
|
4743
4868
|
size: batch.length,
|
|
4744
4869
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -4756,14 +4881,25 @@ var init_QueueProcessor = __esm({
|
|
|
4756
4881
|
await offlineQueue.removeItem(item.id);
|
|
4757
4882
|
failed++;
|
|
4758
4883
|
}
|
|
4884
|
+
const responseError = typeof response.data === "object" && response.data !== null && "error" in response.data ? response.data.error : void 0;
|
|
4885
|
+
const isDuplicate = typeof responseError === "string" && responseError.toUpperCase().includes("DUPLICATE");
|
|
4886
|
+
if (isDuplicate) {
|
|
4887
|
+
if (this.config.debug) {
|
|
4888
|
+
console.log("[Paywallo:QUEUE] event batch deduplicated by server \u2014 items dropped (benign)", { status: response.status, size: batch.length, error: responseError });
|
|
4889
|
+
}
|
|
4890
|
+
} else {
|
|
4891
|
+
console.error("[Paywallo:QUEUE] event batch failed PERMANENTLY (4xx) \u2014 items dropped", { status: response.status, size: batch.length, responseBody: typeof response.data === "string" ? response.data.slice(0, 1e3) : JSON.stringify(response.data).slice(0, 1e3), firstItemBody: typeof batch[0].body === "string" ? batch[0].body.slice(0, 1e3) : JSON.stringify(batch[0].body).slice(0, 1e3) });
|
|
4892
|
+
}
|
|
4759
4893
|
this.log("Event batch failed permanently (4xx) - removing from queue", {
|
|
4760
4894
|
status: response.status,
|
|
4761
|
-
size: batch.length
|
|
4895
|
+
size: batch.length,
|
|
4896
|
+
isDuplicate
|
|
4762
4897
|
});
|
|
4763
4898
|
} else {
|
|
4764
4899
|
for (const item of batch) {
|
|
4765
4900
|
retryIds.add(item.id);
|
|
4766
4901
|
}
|
|
4902
|
+
console.error("[Paywallo:QUEUE] event batch server error (5xx) \u2014 will retry", { status: response.status, size: batch.length });
|
|
4767
4903
|
this.log("Event batch failed with server error - will retry", {
|
|
4768
4904
|
status: response.status,
|
|
4769
4905
|
size: batch.length
|
|
@@ -4793,6 +4929,7 @@ var init_QueueProcessor = __esm({
|
|
|
4793
4929
|
skipRetry: false
|
|
4794
4930
|
});
|
|
4795
4931
|
} catch (error) {
|
|
4932
|
+
console.error("[Paywallo:QUEUE] individual item network error \u2014 will retry", { id: item.id, url: item.url, error: error instanceof Error ? error.message : String(error) });
|
|
4796
4933
|
this.log("Request threw network error - will retry", {
|
|
4797
4934
|
id: item.id,
|
|
4798
4935
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -5089,6 +5226,7 @@ var init_IAPService = __esm({
|
|
|
5089
5226
|
this.purchaseInFlight = productId;
|
|
5090
5227
|
if (this.debug) console.log(`[Paywallo PURCHASE] starting ${productId}`);
|
|
5091
5228
|
try {
|
|
5229
|
+
void this.emitter.emitCheckoutStarted(productId);
|
|
5092
5230
|
const purchase = await nativeStoreKit.purchase(productId);
|
|
5093
5231
|
if (!purchase) {
|
|
5094
5232
|
if (this.debug) console.log(`[Paywallo PURCHASE] ${productId} cancelled`);
|
|
@@ -5221,21 +5359,21 @@ var init_usePaywallTracking = __esm({
|
|
|
5221
5359
|
});
|
|
5222
5360
|
|
|
5223
5361
|
// src/domains/paywall/usePaywallActions.ts
|
|
5224
|
-
import { useCallback as useCallback3, useEffect as
|
|
5225
|
-
import { Animated, AppState as AppState3, Dimensions as Dimensions2 } from "react-native";
|
|
5362
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState2 } from "react";
|
|
5363
|
+
import { Animated as Animated2, AppState as AppState3, Dimensions as Dimensions2 } from "react-native";
|
|
5226
5364
|
function usePaywallActions(placement, paywallConfig, onResult, variantKey, campaignId, openAnim, variantId) {
|
|
5227
5365
|
const [isPurchasing, setIsPurchasing] = useState2(false);
|
|
5228
|
-
const isPurchasingRef =
|
|
5366
|
+
const isPurchasingRef = useRef3(false);
|
|
5229
5367
|
const [isClosing, setIsClosing] = useState2(false);
|
|
5230
|
-
const slideAnim =
|
|
5231
|
-
const presentedAtRef =
|
|
5232
|
-
const closedRef =
|
|
5368
|
+
const slideAnim = useRef3(new Animated2.Value(0)).current;
|
|
5369
|
+
const presentedAtRef = useRef3(Date.now());
|
|
5370
|
+
const closedRef = useRef3(false);
|
|
5233
5371
|
const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
|
|
5234
5372
|
const closeWithReason = useCallback3((reason, resultOverride) => {
|
|
5235
5373
|
if (closedRef.current) return;
|
|
5236
5374
|
closedRef.current = true;
|
|
5237
5375
|
const animTarget = openAnim ?? slideAnim;
|
|
5238
|
-
|
|
5376
|
+
Animated2.timing(animTarget, {
|
|
5239
5377
|
toValue: Dimensions2.get("window").height,
|
|
5240
5378
|
duration: 350,
|
|
5241
5379
|
useNativeDriver: true
|
|
@@ -5255,7 +5393,7 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
|
|
|
5255
5393
|
setIsClosing(true);
|
|
5256
5394
|
closeWithReason("dismissed");
|
|
5257
5395
|
}, [isClosing, closeWithReason]);
|
|
5258
|
-
|
|
5396
|
+
useEffect3(() => {
|
|
5259
5397
|
const sub = AppState3.addEventListener("change", (nextState) => {
|
|
5260
5398
|
if (isPurchasingRef.current) return;
|
|
5261
5399
|
if (nextState === "background" || nextState === "inactive") {
|
|
@@ -5264,7 +5402,7 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
|
|
|
5264
5402
|
});
|
|
5265
5403
|
return () => sub.remove();
|
|
5266
5404
|
}, [closeWithReason]);
|
|
5267
|
-
|
|
5405
|
+
useEffect3(() => {
|
|
5268
5406
|
const timer = setTimeout(() => {
|
|
5269
5407
|
closeWithReason("timeout");
|
|
5270
5408
|
}, PAYWALL_TIMEOUT_MS);
|
|
@@ -5335,17 +5473,17 @@ var init_usePaywallActions = __esm({
|
|
|
5335
5473
|
});
|
|
5336
5474
|
|
|
5337
5475
|
// src/domains/paywall/usePaywallLoader.ts
|
|
5338
|
-
import { useEffect as
|
|
5476
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState3 } from "react";
|
|
5339
5477
|
function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts, variantKey, campaignId, variantId) {
|
|
5340
5478
|
const [error, setError] = useState3(null);
|
|
5341
5479
|
const [paywallConfig, setPaywallConfig] = useState3(null);
|
|
5342
5480
|
const [products, setProducts] = useState3(/* @__PURE__ */ new Map());
|
|
5343
|
-
const preloadedConfigRef =
|
|
5481
|
+
const preloadedConfigRef = useRef4(preloadedConfig);
|
|
5344
5482
|
preloadedConfigRef.current = preloadedConfig;
|
|
5345
|
-
const preloadedProductsRef =
|
|
5483
|
+
const preloadedProductsRef = useRef4(preloadedProducts);
|
|
5346
5484
|
preloadedProductsRef.current = preloadedProducts;
|
|
5347
|
-
const trackingDoneRef =
|
|
5348
|
-
|
|
5485
|
+
const trackingDoneRef = useRef4(false);
|
|
5486
|
+
useEffect4(() => {
|
|
5349
5487
|
if (!visible) {
|
|
5350
5488
|
trackingDoneRef.current = false;
|
|
5351
5489
|
return;
|
|
@@ -5440,28 +5578,28 @@ var init_usePaywallLoader = __esm({
|
|
|
5440
5578
|
});
|
|
5441
5579
|
|
|
5442
5580
|
// src/domains/paywall/PaywallModal.tsx
|
|
5443
|
-
import { useCallback as useCallback4, useEffect as
|
|
5581
|
+
import { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef5, useState as useState4 } from "react";
|
|
5444
5582
|
import {
|
|
5445
|
-
Animated as
|
|
5583
|
+
Animated as Animated3,
|
|
5446
5584
|
Dimensions as Dimensions3,
|
|
5447
5585
|
Easing,
|
|
5448
5586
|
Modal,
|
|
5449
5587
|
Pressable,
|
|
5450
|
-
StyleSheet as
|
|
5588
|
+
StyleSheet as StyleSheet3,
|
|
5451
5589
|
Text,
|
|
5452
|
-
View as
|
|
5590
|
+
View as View3
|
|
5453
5591
|
} from "react-native";
|
|
5454
|
-
import { Fragment, jsx as
|
|
5592
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
5455
5593
|
function ErrorFallback({ description, onRetry, onClose }) {
|
|
5456
5594
|
const overrides = PaywalloClient.getConfig()?.errorStrings;
|
|
5457
5595
|
const title = overrides?.title ?? getSdkString("paywallErrorTitle");
|
|
5458
5596
|
const retryLabel = overrides?.retry ?? getSdkString("paywallRetryButton");
|
|
5459
5597
|
const closeLabel = overrides?.close ?? getSdkString("paywallCloseButton");
|
|
5460
|
-
return /* @__PURE__ */
|
|
5461
|
-
/* @__PURE__ */
|
|
5462
|
-
/* @__PURE__ */
|
|
5463
|
-
/* @__PURE__ */
|
|
5464
|
-
/* @__PURE__ */
|
|
5598
|
+
return /* @__PURE__ */ jsx3(View3, { style: styles3.fallbackContainer, children: /* @__PURE__ */ jsxs2(View3, { style: styles3.fallbackCard, children: [
|
|
5599
|
+
/* @__PURE__ */ jsx3(Text, { style: styles3.fallbackTitle, children: title }),
|
|
5600
|
+
/* @__PURE__ */ jsx3(Text, { style: styles3.fallbackDescription, children: description }),
|
|
5601
|
+
/* @__PURE__ */ jsx3(Pressable, { style: styles3.retryButton, onPress: onRetry, children: /* @__PURE__ */ jsx3(Text, { style: styles3.retryButtonText, children: retryLabel }) }),
|
|
5602
|
+
/* @__PURE__ */ jsx3(Pressable, { style: styles3.closeButton, onPress: onClose, children: /* @__PURE__ */ jsx3(Text, { style: styles3.closeButtonText, children: closeLabel }) })
|
|
5465
5603
|
] }) });
|
|
5466
5604
|
}
|
|
5467
5605
|
function PaywallModal({
|
|
@@ -5484,7 +5622,7 @@ function PaywallModal({
|
|
|
5484
5622
|
variantId
|
|
5485
5623
|
);
|
|
5486
5624
|
const screenHeight = Dimensions3.get("window").height;
|
|
5487
|
-
const openAnim =
|
|
5625
|
+
const openAnim = useRef5(new Animated3.Value(screenHeight)).current;
|
|
5488
5626
|
const [modalVisible, setModalVisible] = useState4(false);
|
|
5489
5627
|
const handleResult = useCallback4((result) => {
|
|
5490
5628
|
setModalVisible(false);
|
|
@@ -5493,7 +5631,9 @@ function PaywallModal({
|
|
|
5493
5631
|
const { isPurchasing, slideAnim, handleClose, handlePurchase, handleRestore } = usePaywallActions(placement, paywallConfig, handleResult, variantKey, campaignId, openAnim, variantId);
|
|
5494
5632
|
const [webViewError, setWebViewError] = useState4(null);
|
|
5495
5633
|
const [retryCount, setRetryCount] = useState4(0);
|
|
5496
|
-
const
|
|
5634
|
+
const [showSkeleton, setShowSkeleton] = useState4(true);
|
|
5635
|
+
const skeletonOpacity = useRef5(new Animated3.Value(1)).current;
|
|
5636
|
+
const retryCountRef = useRef5(0);
|
|
5497
5637
|
const handleWebViewError = useCallback4(
|
|
5498
5638
|
(err) => {
|
|
5499
5639
|
if (retryCountRef.current >= 1) {
|
|
@@ -5509,21 +5649,39 @@ function PaywallModal({
|
|
|
5509
5649
|
retryCountRef.current = 0;
|
|
5510
5650
|
setWebViewError(null);
|
|
5511
5651
|
setRetryCount(0);
|
|
5512
|
-
|
|
5513
|
-
|
|
5652
|
+
setShowSkeleton(true);
|
|
5653
|
+
skeletonOpacity.setValue(1);
|
|
5654
|
+
}, [skeletonOpacity]);
|
|
5655
|
+
const handleWebViewReady = useCallback4(() => {
|
|
5656
|
+
Animated3.timing(skeletonOpacity, {
|
|
5657
|
+
toValue: 0,
|
|
5658
|
+
duration: 250,
|
|
5659
|
+
easing: Easing.out(Easing.cubic),
|
|
5660
|
+
useNativeDriver: true
|
|
5661
|
+
}).start(() => setShowSkeleton(false));
|
|
5662
|
+
}, [skeletonOpacity]);
|
|
5663
|
+
useEffect5(() => {
|
|
5664
|
+
if (!visible || !showSkeleton) return;
|
|
5665
|
+
const timer = setTimeout(handleWebViewReady, 8e3);
|
|
5666
|
+
return () => clearTimeout(timer);
|
|
5667
|
+
}, [visible, showSkeleton, handleWebViewReady]);
|
|
5668
|
+
useEffect5(() => {
|
|
5514
5669
|
if (visible) {
|
|
5515
5670
|
setModalVisible(true);
|
|
5671
|
+
setShowSkeleton(true);
|
|
5672
|
+
skeletonOpacity.setValue(1);
|
|
5516
5673
|
openAnim.setValue(Dimensions3.get("window").height);
|
|
5517
|
-
|
|
5674
|
+
Animated3.timing(openAnim, {
|
|
5518
5675
|
toValue: 0,
|
|
5519
5676
|
duration: 550,
|
|
5520
5677
|
easing: Easing.out(Easing.cubic),
|
|
5521
5678
|
useNativeDriver: true
|
|
5522
5679
|
}).start();
|
|
5523
5680
|
}
|
|
5524
|
-
}, [visible, openAnim]);
|
|
5681
|
+
}, [visible, openAnim, skeletonOpacity]);
|
|
5525
5682
|
if (!modalVisible && !visible) return null;
|
|
5526
|
-
|
|
5683
|
+
const skeletonBackground = paywallConfig && "craftData" in paywallConfig.config ? extractFirstPageBackground(paywallConfig.config.craftData) : "#FFFFFF";
|
|
5684
|
+
return /* @__PURE__ */ jsx3(
|
|
5527
5685
|
Modal,
|
|
5528
5686
|
{
|
|
5529
5687
|
visible: modalVisible,
|
|
@@ -5532,16 +5690,16 @@ function PaywallModal({
|
|
|
5532
5690
|
onRequestClose: handleClose,
|
|
5533
5691
|
transparent: true,
|
|
5534
5692
|
statusBarTranslucent: true,
|
|
5535
|
-
children: /* @__PURE__ */
|
|
5536
|
-
error && /* @__PURE__ */
|
|
5537
|
-
!error && paywallConfig && "craftData" in paywallConfig.config && /* @__PURE__ */
|
|
5693
|
+
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: [
|
|
5694
|
+
error && /* @__PURE__ */ jsx3(View3, { style: styles3.errorContainer, children: /* @__PURE__ */ jsx3(Text, { style: styles3.errorText, children: error.message }) }),
|
|
5695
|
+
!error && paywallConfig && "craftData" in paywallConfig.config && /* @__PURE__ */ jsx3(Fragment, { children: webViewError ? /* @__PURE__ */ jsx3(
|
|
5538
5696
|
ErrorFallback,
|
|
5539
5697
|
{
|
|
5540
5698
|
description: webViewError.description,
|
|
5541
5699
|
onRetry: handleRetry,
|
|
5542
5700
|
onClose: handleClose
|
|
5543
5701
|
}
|
|
5544
|
-
) : /* @__PURE__ */
|
|
5702
|
+
) : /* @__PURE__ */ jsx3(
|
|
5545
5703
|
PaywallWebView,
|
|
5546
5704
|
{
|
|
5547
5705
|
paywallId: paywallConfig.id,
|
|
@@ -5550,27 +5708,38 @@ function PaywallModal({
|
|
|
5550
5708
|
primaryProductId: paywallConfig.primaryProductId,
|
|
5551
5709
|
secondaryProductId: paywallConfig.secondaryProductId,
|
|
5552
5710
|
isPurchasing,
|
|
5711
|
+
onReady: handleWebViewReady,
|
|
5553
5712
|
onClose: handleClose,
|
|
5554
5713
|
onPurchase: handlePurchase,
|
|
5555
5714
|
onRestore: handleRestore,
|
|
5556
5715
|
onError: handleWebViewError
|
|
5557
5716
|
},
|
|
5558
5717
|
retryCount
|
|
5559
|
-
) })
|
|
5718
|
+
) }),
|
|
5719
|
+
showSkeleton && !error && !webViewError && /* @__PURE__ */ jsx3(
|
|
5720
|
+
Animated3.View,
|
|
5721
|
+
{
|
|
5722
|
+
style: [StyleSheet3.absoluteFill, { opacity: skeletonOpacity }],
|
|
5723
|
+
pointerEvents: "none",
|
|
5724
|
+
children: /* @__PURE__ */ jsx3(PaywallSkeleton, { backgroundColor: skeletonBackground })
|
|
5725
|
+
}
|
|
5726
|
+
)
|
|
5560
5727
|
] }) }) })
|
|
5561
5728
|
}
|
|
5562
5729
|
);
|
|
5563
5730
|
}
|
|
5564
|
-
var
|
|
5731
|
+
var styles3;
|
|
5565
5732
|
var init_PaywallModal = __esm({
|
|
5566
5733
|
"src/domains/paywall/PaywallModal.tsx"() {
|
|
5567
5734
|
"use strict";
|
|
5568
5735
|
init_PaywalloClient();
|
|
5569
5736
|
init_localization();
|
|
5737
|
+
init_extractFirstPageBackground();
|
|
5738
|
+
init_PaywallSkeleton();
|
|
5570
5739
|
init_PaywallWebView();
|
|
5571
5740
|
init_usePaywallActions();
|
|
5572
5741
|
init_usePaywallLoader();
|
|
5573
|
-
|
|
5742
|
+
styles3 = StyleSheet3.create({
|
|
5574
5743
|
overlay: { flex: 1, backgroundColor: "rgba(0,0,0,0.3)" },
|
|
5575
5744
|
animatedContainer: { flex: 1, backgroundColor: "#fff" },
|
|
5576
5745
|
container: { flex: 1, backgroundColor: "#fff" },
|
|
@@ -5626,13 +5795,176 @@ var init_PaywallModal = __esm({
|
|
|
5626
5795
|
}
|
|
5627
5796
|
});
|
|
5628
5797
|
|
|
5798
|
+
// src/domains/paywall/PaywallPreloadService.ts
|
|
5799
|
+
var PRELOAD_TTL_MS, STALE_THRESHOLD, PaywallPreloadService, paywallPreloadService;
|
|
5800
|
+
var init_PaywallPreloadService = __esm({
|
|
5801
|
+
"src/domains/paywall/PaywallPreloadService.ts"() {
|
|
5802
|
+
"use strict";
|
|
5803
|
+
init_PaywalloClient();
|
|
5804
|
+
init_IAPService();
|
|
5805
|
+
init_PaywallError();
|
|
5806
|
+
PRELOAD_TTL_MS = 5 * 60 * 1e3;
|
|
5807
|
+
STALE_THRESHOLD = 0.75;
|
|
5808
|
+
PaywallPreloadService = class {
|
|
5809
|
+
constructor() {
|
|
5810
|
+
this.preloaded = /* @__PURE__ */ new Map();
|
|
5811
|
+
this.activePreloads = /* @__PURE__ */ new Map();
|
|
5812
|
+
this.apiClientRef = null;
|
|
5813
|
+
}
|
|
5814
|
+
async preload(apiClient, placement) {
|
|
5815
|
+
this.apiClientRef = apiClient;
|
|
5816
|
+
const existing = this.activePreloads.get(placement);
|
|
5817
|
+
if (existing) return existing;
|
|
5818
|
+
const promise = this.doPreload(apiClient, placement);
|
|
5819
|
+
this.activePreloads.set(placement, promise);
|
|
5820
|
+
try {
|
|
5821
|
+
return await promise;
|
|
5822
|
+
} finally {
|
|
5823
|
+
this.activePreloads.delete(placement);
|
|
5824
|
+
}
|
|
5825
|
+
}
|
|
5826
|
+
async preloadMany(apiClient, placements) {
|
|
5827
|
+
this.apiClientRef = apiClient;
|
|
5828
|
+
if (placements.length === 0) return;
|
|
5829
|
+
const debug = PaywalloClient.getConfig()?.debug;
|
|
5830
|
+
if (debug) console.log(`[Paywallo PRELOAD] paywalls: ${placements.length} placements`);
|
|
5831
|
+
for (let i = 0; i < placements.length; i++) {
|
|
5832
|
+
if (i > 0) await new Promise((r) => setTimeout(r, 200));
|
|
5833
|
+
void this.preload(apiClient, placements[i]).catch((err) => {
|
|
5834
|
+
if (debug) console.log(`[Paywallo PRELOAD] paywall failed ${placements[i]}:`, err);
|
|
5835
|
+
});
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
getPreloaded(placement) {
|
|
5839
|
+
const cached2 = this.preloaded.get(placement);
|
|
5840
|
+
if (!cached2) return null;
|
|
5841
|
+
const age = Date.now() - cached2.preloadedAt;
|
|
5842
|
+
if (age >= PRELOAD_TTL_MS) {
|
|
5843
|
+
this.preloaded.delete(placement);
|
|
5844
|
+
return null;
|
|
5845
|
+
}
|
|
5846
|
+
if (age >= PRELOAD_TTL_MS * STALE_THRESHOLD && this.apiClientRef) {
|
|
5847
|
+
void this.preload(this.apiClientRef, placement).catch(() => {
|
|
5848
|
+
});
|
|
5849
|
+
}
|
|
5850
|
+
return { config: cached2.config, products: new Map(cached2.products) };
|
|
5851
|
+
}
|
|
5852
|
+
isPreloaded(placement) {
|
|
5853
|
+
return this.getPreloaded(placement) !== null;
|
|
5854
|
+
}
|
|
5855
|
+
clear() {
|
|
5856
|
+
this.preloaded.clear();
|
|
5857
|
+
this.activePreloads.clear();
|
|
5858
|
+
}
|
|
5859
|
+
async doPreload(apiClient, placement) {
|
|
5860
|
+
try {
|
|
5861
|
+
const config = await apiClient.getPaywall(placement);
|
|
5862
|
+
if (!config) {
|
|
5863
|
+
return {
|
|
5864
|
+
success: false,
|
|
5865
|
+
error: new PaywallError(
|
|
5866
|
+
PAYWALL_ERROR_CODES.NOT_FOUND,
|
|
5867
|
+
`Paywall not found for placement: ${placement}`
|
|
5868
|
+
)
|
|
5869
|
+
};
|
|
5870
|
+
}
|
|
5871
|
+
const products = await this.loadProductsFor(config);
|
|
5872
|
+
this.preloaded.set(placement, { config, products, preloadedAt: Date.now() });
|
|
5873
|
+
if (PaywalloClient.getConfig()?.debug) {
|
|
5874
|
+
console.log(`[Paywallo PRELOAD] paywall cached \u2014 placement: ${placement}`);
|
|
5875
|
+
}
|
|
5876
|
+
return { success: true };
|
|
5877
|
+
} catch (error) {
|
|
5878
|
+
return {
|
|
5879
|
+
success: false,
|
|
5880
|
+
error: error instanceof Error ? error : new PaywallError(PAYWALL_ERROR_CODES.LOAD_FAILED, String(error))
|
|
5881
|
+
};
|
|
5882
|
+
}
|
|
5883
|
+
}
|
|
5884
|
+
async loadProductsFor(config) {
|
|
5885
|
+
const productIds = [];
|
|
5886
|
+
if (config.primaryProductId) productIds.push(config.primaryProductId);
|
|
5887
|
+
if (config.secondaryProductId) productIds.push(config.secondaryProductId);
|
|
5888
|
+
if (productIds.length === 0) return /* @__PURE__ */ new Map();
|
|
5889
|
+
try {
|
|
5890
|
+
const loaded = await getIAPService().loadProducts(productIds);
|
|
5891
|
+
const map = /* @__PURE__ */ new Map();
|
|
5892
|
+
for (const p of loaded) map.set(p.productId, p);
|
|
5893
|
+
return map;
|
|
5894
|
+
} catch (err) {
|
|
5895
|
+
if (PaywalloClient.getConfig()?.debug) {
|
|
5896
|
+
console.log("[Paywallo PRELOAD] products failed, caching config only:", err);
|
|
5897
|
+
}
|
|
5898
|
+
return /* @__PURE__ */ new Map();
|
|
5899
|
+
}
|
|
5900
|
+
}
|
|
5901
|
+
};
|
|
5902
|
+
paywallPreloadService = new PaywallPreloadService();
|
|
5903
|
+
}
|
|
5904
|
+
});
|
|
5905
|
+
|
|
5906
|
+
// src/domains/paywall/PaywallWebViewPrewarmer.tsx
|
|
5907
|
+
import { useEffect as useEffect6, useMemo as useMemo2, useState as useState5 } from "react";
|
|
5908
|
+
import { requireNativeComponent as requireNativeComponent2, StyleSheet as StyleSheet4, View as View4 } from "react-native";
|
|
5909
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
5910
|
+
function getNativeWebView2() {
|
|
5911
|
+
if (NativeWebViewComponent2) return NativeWebViewComponent2;
|
|
5912
|
+
if (nativeLookupFailed) return null;
|
|
5913
|
+
try {
|
|
5914
|
+
NativeWebViewComponent2 = requireNativeComponent2("PaywalloWebView");
|
|
5915
|
+
return NativeWebViewComponent2;
|
|
5916
|
+
} catch {
|
|
5917
|
+
nativeLookupFailed = true;
|
|
5918
|
+
return null;
|
|
5919
|
+
}
|
|
5920
|
+
}
|
|
5921
|
+
function PaywallWebViewPrewarmer({
|
|
5922
|
+
webUrl,
|
|
5923
|
+
durationMs = DEFAULT_DURATION_MS
|
|
5924
|
+
}) {
|
|
5925
|
+
const NativeWebView = useMemo2(() => getNativeWebView2(), []);
|
|
5926
|
+
const [mounted, setMounted] = useState5(true);
|
|
5927
|
+
useEffect6(() => {
|
|
5928
|
+
if (!NativeWebView) return;
|
|
5929
|
+
const timer = setTimeout(() => setMounted(false), durationMs);
|
|
5930
|
+
return () => clearTimeout(timer);
|
|
5931
|
+
}, [NativeWebView, durationMs]);
|
|
5932
|
+
if (!NativeWebView || !mounted) return null;
|
|
5933
|
+
const preheatUrl = `${webUrl}/paywall/preheat`;
|
|
5934
|
+
if (PaywalloClient.getConfig()?.debug) {
|
|
5935
|
+
console.log(`[Paywallo PREWARM] mounting hidden WebView: ${preheatUrl}`);
|
|
5936
|
+
}
|
|
5937
|
+
return /* @__PURE__ */ jsx4(View4, { pointerEvents: "none", style: styles4.hidden, children: /* @__PURE__ */ jsx4(NativeWebView, { sourceUrl: preheatUrl, style: styles4.fill }) });
|
|
5938
|
+
}
|
|
5939
|
+
var NativeWebViewComponent2, nativeLookupFailed, DEFAULT_DURATION_MS, styles4;
|
|
5940
|
+
var init_PaywallWebViewPrewarmer = __esm({
|
|
5941
|
+
"src/domains/paywall/PaywallWebViewPrewarmer.tsx"() {
|
|
5942
|
+
"use strict";
|
|
5943
|
+
init_PaywalloClient();
|
|
5944
|
+
NativeWebViewComponent2 = null;
|
|
5945
|
+
nativeLookupFailed = false;
|
|
5946
|
+
DEFAULT_DURATION_MS = 60 * 1e3;
|
|
5947
|
+
styles4 = StyleSheet4.create({
|
|
5948
|
+
hidden: {
|
|
5949
|
+
height: 1,
|
|
5950
|
+
left: -9999,
|
|
5951
|
+
opacity: 0,
|
|
5952
|
+
position: "absolute",
|
|
5953
|
+
top: -9999,
|
|
5954
|
+
width: 1
|
|
5955
|
+
},
|
|
5956
|
+
fill: { flex: 1 }
|
|
5957
|
+
});
|
|
5958
|
+
}
|
|
5959
|
+
});
|
|
5960
|
+
|
|
5629
5961
|
// src/domains/paywall/usePaywallSession.ts
|
|
5630
|
-
import { useCallback as useCallback5, useEffect as
|
|
5962
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef6 } from "react";
|
|
5631
5963
|
function usePaywallSession(options) {
|
|
5632
|
-
const closedRef =
|
|
5633
|
-
const optsRef =
|
|
5964
|
+
const closedRef = useRef6(false);
|
|
5965
|
+
const optsRef = useRef6(options);
|
|
5634
5966
|
optsRef.current = options;
|
|
5635
|
-
|
|
5967
|
+
useEffect7(() => {
|
|
5636
5968
|
if (!options) return;
|
|
5637
5969
|
closedRef.current = false;
|
|
5638
5970
|
startHeartbeat({
|
|
@@ -5676,11 +6008,11 @@ var init_usePaywallSession = __esm({
|
|
|
5676
6008
|
});
|
|
5677
6009
|
|
|
5678
6010
|
// src/domains/paywall/usePaywallPresenter.ts
|
|
5679
|
-
import { useCallback as useCallback6, useEffect as
|
|
6011
|
+
import { useCallback as useCallback6, useEffect as useEffect8, useRef as useRef7, useState as useState6 } from "react";
|
|
5680
6012
|
function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
|
|
5681
|
-
const [showingPreloadedWebView, setShowingPreloadedWebView] =
|
|
5682
|
-
const resolverRef =
|
|
5683
|
-
const presentedAtRef =
|
|
6013
|
+
const [showingPreloadedWebView, setShowingPreloadedWebView] = useState6(false);
|
|
6014
|
+
const resolverRef = useRef7(null);
|
|
6015
|
+
const presentedAtRef = useRef7(Date.now());
|
|
5684
6016
|
const sessionOpts = showingPreloadedWebView && preloadedWebView ? {
|
|
5685
6017
|
placement: preloadedWebView.placement,
|
|
5686
6018
|
paywallId: preloadedWebView.paywallId,
|
|
@@ -5690,7 +6022,7 @@ function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
|
|
|
5690
6022
|
...preloadedWebView.campaignId && { campaignId: preloadedWebView.campaignId }
|
|
5691
6023
|
} : null;
|
|
5692
6024
|
const { markClosed } = usePaywallSession(sessionOpts);
|
|
5693
|
-
|
|
6025
|
+
useEffect8(() => {
|
|
5694
6026
|
return () => {
|
|
5695
6027
|
if (resolverRef.current) {
|
|
5696
6028
|
resolverRef.current({ presented: false, purchased: false, cancelled: true, restored: false });
|
|
@@ -5777,7 +6109,9 @@ var init_paywall = __esm({
|
|
|
5777
6109
|
init_PaywallError();
|
|
5778
6110
|
init_PaywallErrorBoundary();
|
|
5779
6111
|
init_PaywallModal();
|
|
6112
|
+
init_PaywallPreloadService();
|
|
5780
6113
|
init_PaywallWebView();
|
|
6114
|
+
init_PaywallWebViewPrewarmer();
|
|
5781
6115
|
init_usePaywallPresenter();
|
|
5782
6116
|
init_usePaywallTracking();
|
|
5783
6117
|
}
|
|
@@ -5883,10 +6217,10 @@ var init_PlanService = __esm({
|
|
|
5883
6217
|
}
|
|
5884
6218
|
async getCurrentPlan(forceRefresh = false) {
|
|
5885
6219
|
if (!forceRefresh) {
|
|
5886
|
-
const
|
|
5887
|
-
if (
|
|
5888
|
-
const current =
|
|
5889
|
-
(p) => p.identifier ===
|
|
6220
|
+
const cached2 = this.cache.get();
|
|
6221
|
+
if (cached2 && !cached2.isStale) {
|
|
6222
|
+
const current = cached2.data.plans.find(
|
|
6223
|
+
(p) => p.identifier === cached2.data.currentIdentifier
|
|
5890
6224
|
);
|
|
5891
6225
|
return current ?? null;
|
|
5892
6226
|
}
|
|
@@ -5913,8 +6247,8 @@ var init_PlanService = __esm({
|
|
|
5913
6247
|
}
|
|
5914
6248
|
async getAllPlans(forceRefresh = false) {
|
|
5915
6249
|
if (!forceRefresh) {
|
|
5916
|
-
const
|
|
5917
|
-
if (
|
|
6250
|
+
const cached2 = this.cache.get();
|
|
6251
|
+
if (cached2 && !cached2.isStale) return cached2.data;
|
|
5918
6252
|
}
|
|
5919
6253
|
if (!this.cache.get()) {
|
|
5920
6254
|
try {
|
|
@@ -6032,112 +6366,274 @@ var init_analytics = __esm({
|
|
|
6032
6366
|
}
|
|
6033
6367
|
});
|
|
6034
6368
|
|
|
6035
|
-
// src/core/
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
async function trackEvent(apiClient, state, eventName, options) {
|
|
6041
|
-
const distinctId = identityManager.getDistinctId();
|
|
6042
|
-
if (!distinctId) {
|
|
6043
|
-
if (state.config?.debug) console.log(`[Paywallo TRACK] ${eventName} skipped (no distinctId)`);
|
|
6044
|
-
return;
|
|
6045
|
-
}
|
|
6046
|
-
if (!isValidEventName(eventName)) {
|
|
6047
|
-
const msg = `Invalid event name: "${eventName}". Use snake_case or $reserved_name.`;
|
|
6048
|
-
throw new ClientError(CLIENT_ERROR_CODES.INVALID_EVENT_NAME, msg);
|
|
6049
|
-
}
|
|
6050
|
-
if (state.config?.debug && eventName.startsWith("$purchase")) console.log(`[Paywallo TRACK] ${eventName}`, options?.properties ?? {});
|
|
6051
|
-
const sessionId = sessionManager.getSessionId();
|
|
6052
|
-
await apiClient.trackEvent(
|
|
6053
|
-
eventName,
|
|
6054
|
-
distinctId,
|
|
6055
|
-
{ ...identityManager.getProperties(), ...options?.properties, ...sessionId && { sessionId } },
|
|
6056
|
-
options?.timestamp,
|
|
6057
|
-
options?.priority ? { priority: options.priority } : void 0
|
|
6058
|
-
);
|
|
6059
|
-
}
|
|
6060
|
-
async function trackOnboardingStep(apiClient, state, index, name) {
|
|
6061
|
-
const distinctId = identityManager.getDistinctId();
|
|
6062
|
-
if (!distinctId) return;
|
|
6063
|
-
if (!Number.isInteger(index) || index < 0) {
|
|
6064
|
-
const msg = `Invalid stepIndex: "${index}". Must be a non-negative integer.`;
|
|
6065
|
-
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_STEP_INDEX, msg);
|
|
6066
|
-
}
|
|
6067
|
-
const now = Date.now();
|
|
6068
|
-
const timeOnPreviousStep = state.lastOnboardingStepTimestamp !== null ? Math.round((now - state.lastOnboardingStepTimestamp) / 1e3) : 0;
|
|
6069
|
-
state.lastOnboardingStepTimestamp = now;
|
|
6070
|
-
await apiClient.trackEvent("$onboarding_step", distinctId, { stepIndex: index, stepName: name, timeOnPreviousStep });
|
|
6071
|
-
}
|
|
6072
|
-
function validateCoreAction(actionName) {
|
|
6073
|
-
if (!actionName || typeof actionName !== "string") {
|
|
6074
|
-
const msg = `Invalid actionName: "${actionName}". Must be a non-empty string.`;
|
|
6075
|
-
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_ACTION_NAME, msg);
|
|
6076
|
-
}
|
|
6077
|
-
}
|
|
6078
|
-
var init_PaywalloAnalytics = __esm({
|
|
6079
|
-
"src/core/PaywalloAnalytics.ts"() {
|
|
6080
|
-
"use strict";
|
|
6081
|
-
init_analytics();
|
|
6082
|
-
init_identity();
|
|
6083
|
-
init_session();
|
|
6084
|
-
init_ClientError();
|
|
6085
|
-
}
|
|
6086
|
-
});
|
|
6087
|
-
|
|
6088
|
-
// src/core/PaywalloFlags.ts
|
|
6089
|
-
async function getVariantCached(apiClient, flagKey, defaultValue) {
|
|
6090
|
-
const distinctId = identityManager.getDistinctId();
|
|
6091
|
-
const result = await apiClient.getVariantFromCache(flagKey, distinctId);
|
|
6092
|
-
if (result) return result;
|
|
6093
|
-
void apiClient.getVariant(flagKey, distinctId).catch(() => {
|
|
6094
|
-
});
|
|
6095
|
-
return { variant: defaultValue ?? null, payload: void 0 };
|
|
6096
|
-
}
|
|
6097
|
-
async function getVariant(apiClient, state, flagKey) {
|
|
6098
|
-
try {
|
|
6099
|
-
const distinctId = identityManager.getDistinctId();
|
|
6100
|
-
if (!distinctId) {
|
|
6101
|
-
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 null (no distinctId)`);
|
|
6102
|
-
return { variant: null };
|
|
6103
|
-
}
|
|
6104
|
-
const result = await apiClient.getVariant(flagKey, distinctId);
|
|
6105
|
-
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192`, result.variant);
|
|
6106
|
-
return result;
|
|
6107
|
-
} catch (error) {
|
|
6108
|
-
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 error`, error);
|
|
6109
|
-
if (state.apiClient) void state.apiClient.reportError("GET_VARIANT_FAILED", error instanceof Error ? error.message : String(error));
|
|
6110
|
-
return { variant: null };
|
|
6111
|
-
}
|
|
6112
|
-
}
|
|
6113
|
-
function evaluateFlags(apiClient, keys) {
|
|
6114
|
-
return apiClient.evaluateFlags(keys, identityManager.getDistinctId());
|
|
6115
|
-
}
|
|
6116
|
-
async function getConditionalFlag(apiClient, state, flagKey, context) {
|
|
6117
|
-
try {
|
|
6118
|
-
const distinctId = identityManager.getDistinctId();
|
|
6119
|
-
if (!distinctId) return false;
|
|
6120
|
-
const result = await apiClient.getConditionalFlag(flagKey, { distinctId, ...context });
|
|
6121
|
-
return result.value;
|
|
6122
|
-
} catch (error) {
|
|
6123
|
-
if (state.apiClient) void state.apiClient.reportError("GET_CONDITIONAL_FLAG_FAILED", error instanceof Error ? error.message : String(error));
|
|
6124
|
-
return false;
|
|
6125
|
-
}
|
|
6126
|
-
}
|
|
6127
|
-
var init_PaywalloFlags = __esm({
|
|
6128
|
-
"src/core/PaywalloFlags.ts"() {
|
|
6129
|
-
"use strict";
|
|
6130
|
-
init_identity();
|
|
6131
|
-
}
|
|
6132
|
-
});
|
|
6133
|
-
|
|
6134
|
-
// src/domains/onboarding/OnboardingError.ts
|
|
6135
|
-
var OnboardingError, ONBOARDING_ERROR_CODES;
|
|
6136
|
-
var init_OnboardingError = __esm({
|
|
6137
|
-
"src/domains/onboarding/OnboardingError.ts"() {
|
|
6369
|
+
// src/core/events/schemas.ts
|
|
6370
|
+
import { z } from "zod";
|
|
6371
|
+
var lifecycleEventSchema, identifyEventSchema, paywallEventSchema, transactionEventSchema, onboardingEventSchema, notificationEventSchema, customEventSchema, EVENT_SCHEMAS;
|
|
6372
|
+
var init_schemas = __esm({
|
|
6373
|
+
"src/core/events/schemas.ts"() {
|
|
6138
6374
|
"use strict";
|
|
6139
|
-
|
|
6140
|
-
|
|
6375
|
+
lifecycleEventSchema = z.object({
|
|
6376
|
+
type: z.enum([
|
|
6377
|
+
"install",
|
|
6378
|
+
"cold_start",
|
|
6379
|
+
"foreground",
|
|
6380
|
+
"background",
|
|
6381
|
+
"session_start",
|
|
6382
|
+
"session_end"
|
|
6383
|
+
]),
|
|
6384
|
+
session_id: z.string().optional(),
|
|
6385
|
+
duration_s: z.number().nonnegative().optional(),
|
|
6386
|
+
first_ever: z.boolean().optional(),
|
|
6387
|
+
// Contexto leve transportado em lifecycle (útil pra cold_start/install)
|
|
6388
|
+
app_version: z.string().optional(),
|
|
6389
|
+
device_type: z.enum(["ios", "android"]).optional(),
|
|
6390
|
+
os_version: z.string().optional(),
|
|
6391
|
+
// Timestamps opcionais — o EventBatcher injeta `timestamp` no envelope
|
|
6392
|
+
started_at: z.string().optional(),
|
|
6393
|
+
ended_at: z.string().optional()
|
|
6394
|
+
}).strict();
|
|
6395
|
+
identifyEventSchema = z.object({
|
|
6396
|
+
distinct_id: z.string().min(1),
|
|
6397
|
+
email: z.email().optional(),
|
|
6398
|
+
traits: z.record(z.string(), z.unknown()).optional(),
|
|
6399
|
+
attribution: z.record(z.string(), z.unknown()).optional(),
|
|
6400
|
+
device: z.record(z.string(), z.unknown()).optional()
|
|
6401
|
+
}).strict();
|
|
6402
|
+
paywallEventSchema = z.object({
|
|
6403
|
+
type: z.enum(["viewed", "closed", "purchased"]),
|
|
6404
|
+
paywall_id: z.string().min(1),
|
|
6405
|
+
placement: z.string().optional(),
|
|
6406
|
+
variant_id: z.string().optional(),
|
|
6407
|
+
variant_key: z.string().optional(),
|
|
6408
|
+
campaign_id: z.string().optional(),
|
|
6409
|
+
// `closed`-specific
|
|
6410
|
+
closed_at: z.string().optional(),
|
|
6411
|
+
duration_s: z.number().nonnegative().optional(),
|
|
6412
|
+
close_reason: z.enum(["dismiss", "cta", "purchase", "error", "timeout"]).optional(),
|
|
6413
|
+
scroll_depth: z.number().min(0).max(1).optional(),
|
|
6414
|
+
// `purchased`-specific (mínimo — transaction canônica leva detalhes)
|
|
6415
|
+
product_id: z.string().optional()
|
|
6416
|
+
}).strict();
|
|
6417
|
+
transactionEventSchema = z.object({
|
|
6418
|
+
type: z.enum([
|
|
6419
|
+
"completed",
|
|
6420
|
+
"failed",
|
|
6421
|
+
"refunded",
|
|
6422
|
+
"renewed",
|
|
6423
|
+
"canceled",
|
|
6424
|
+
"expired"
|
|
6425
|
+
]),
|
|
6426
|
+
tx_id: z.string().min(1),
|
|
6427
|
+
amount: z.number().optional(),
|
|
6428
|
+
currency: z.string().length(3).optional(),
|
|
6429
|
+
product_id: z.string().optional(),
|
|
6430
|
+
subscription_id: z.string().optional(),
|
|
6431
|
+
paywall_id: z.string().optional(),
|
|
6432
|
+
variant_id: z.string().optional(),
|
|
6433
|
+
reason: z.string().optional()
|
|
6434
|
+
}).strict();
|
|
6435
|
+
onboardingEventSchema = z.object({
|
|
6436
|
+
type: z.enum(["step", "complete", "drop"]),
|
|
6437
|
+
step_index: z.number().int().nonnegative().optional(),
|
|
6438
|
+
step_name: z.string().optional(),
|
|
6439
|
+
variant_key: z.string().optional(),
|
|
6440
|
+
time_on_prev_s: z.number().nonnegative().optional()
|
|
6441
|
+
}).strict();
|
|
6442
|
+
notificationEventSchema = z.object({
|
|
6443
|
+
type: z.enum(["delivered", "displayed", "clicked", "dismissed"]),
|
|
6444
|
+
notification_id: z.string().optional(),
|
|
6445
|
+
campaign_id: z.string().optional(),
|
|
6446
|
+
variant_key: z.string().nullable().optional(),
|
|
6447
|
+
message_id: z.string().optional(),
|
|
6448
|
+
device_id: z.string().optional(),
|
|
6449
|
+
push_platform: z.enum(["ios", "android"]).optional(),
|
|
6450
|
+
timezone: z.string().optional(),
|
|
6451
|
+
failure_reason: z.string().optional(),
|
|
6452
|
+
app_user_id: z.string().optional()
|
|
6453
|
+
}).strict();
|
|
6454
|
+
customEventSchema = z.object({
|
|
6455
|
+
name: z.string().min(1).regex(
|
|
6456
|
+
/^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/,
|
|
6457
|
+
"custom event name must be snake_case or $reserved"
|
|
6458
|
+
),
|
|
6459
|
+
props: z.record(z.string(), z.unknown()).optional()
|
|
6460
|
+
}).strict();
|
|
6461
|
+
EVENT_SCHEMAS = {
|
|
6462
|
+
lifecycle: lifecycleEventSchema,
|
|
6463
|
+
identify: identifyEventSchema,
|
|
6464
|
+
paywall: paywallEventSchema,
|
|
6465
|
+
transaction: transactionEventSchema,
|
|
6466
|
+
onboarding: onboardingEventSchema,
|
|
6467
|
+
notification: notificationEventSchema,
|
|
6468
|
+
custom: customEventSchema
|
|
6469
|
+
};
|
|
6470
|
+
}
|
|
6471
|
+
});
|
|
6472
|
+
|
|
6473
|
+
// src/core/events/eventFamilies.ts
|
|
6474
|
+
function isDeprecatedEvent(eventName) {
|
|
6475
|
+
return DEPRECATED_EVENT_NAMES.includes(eventName);
|
|
6476
|
+
}
|
|
6477
|
+
function isCanonicalFamily(name) {
|
|
6478
|
+
return CANONICAL_FAMILIES.includes(name);
|
|
6479
|
+
}
|
|
6480
|
+
function detectFamily(eventName) {
|
|
6481
|
+
if (isCanonicalFamily(eventName) && eventName !== "custom") {
|
|
6482
|
+
return eventName;
|
|
6483
|
+
}
|
|
6484
|
+
return "custom";
|
|
6485
|
+
}
|
|
6486
|
+
function validateEvent(eventName, properties) {
|
|
6487
|
+
const family = detectFamily(eventName);
|
|
6488
|
+
const schema = EVENT_SCHEMAS[family];
|
|
6489
|
+
const input = family === "custom" ? { name: eventName, ...properties ? { props: properties } : {} } : properties ?? {};
|
|
6490
|
+
const parsed = schema.safeParse(input);
|
|
6491
|
+
if (parsed.success) {
|
|
6492
|
+
return { ok: true, family, data: parsed.data };
|
|
6493
|
+
}
|
|
6494
|
+
return { ok: false, family, error: parsed.error };
|
|
6495
|
+
}
|
|
6496
|
+
function formatValidationError(error) {
|
|
6497
|
+
return error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
6498
|
+
}
|
|
6499
|
+
var CANONICAL_FAMILIES, DEPRECATED_EVENT_NAMES;
|
|
6500
|
+
var init_eventFamilies = __esm({
|
|
6501
|
+
"src/core/events/eventFamilies.ts"() {
|
|
6502
|
+
"use strict";
|
|
6503
|
+
init_schemas();
|
|
6504
|
+
CANONICAL_FAMILIES = [
|
|
6505
|
+
"lifecycle",
|
|
6506
|
+
"identify",
|
|
6507
|
+
"paywall",
|
|
6508
|
+
"transaction",
|
|
6509
|
+
"onboarding",
|
|
6510
|
+
"notification",
|
|
6511
|
+
"custom"
|
|
6512
|
+
];
|
|
6513
|
+
DEPRECATED_EVENT_NAMES = [
|
|
6514
|
+
"$paywall_purchased",
|
|
6515
|
+
"$paywall_product_selected",
|
|
6516
|
+
"$core_action",
|
|
6517
|
+
"$campaign_impression",
|
|
6518
|
+
"$app_open",
|
|
6519
|
+
"$app_background",
|
|
6520
|
+
"$app_foreground",
|
|
6521
|
+
"session.end"
|
|
6522
|
+
];
|
|
6523
|
+
}
|
|
6524
|
+
});
|
|
6525
|
+
|
|
6526
|
+
// src/core/PaywalloAnalytics.ts
|
|
6527
|
+
function isValidEventName(name) {
|
|
6528
|
+
if (name.startsWith("$")) return /^\$[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
6529
|
+
return /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
6530
|
+
}
|
|
6531
|
+
async function trackEvent(apiClient, state, eventName, options) {
|
|
6532
|
+
const distinctId = identityManager.getDistinctId();
|
|
6533
|
+
if (!distinctId) {
|
|
6534
|
+
console.error("[Paywallo:DROP] event dropped \u2014 no distinctId available yet", { eventName });
|
|
6535
|
+
return;
|
|
6536
|
+
}
|
|
6537
|
+
if (!isValidEventName(eventName)) {
|
|
6538
|
+
const msg = `Invalid event name: "${eventName}". Use snake_case or $reserved_name.`;
|
|
6539
|
+
throw new ClientError(CLIENT_ERROR_CODES.INVALID_EVENT_NAME, msg);
|
|
6540
|
+
}
|
|
6541
|
+
if (state.config?.debug && eventName.startsWith("$purchase")) console.log(`[Paywallo TRACK] ${eventName}`, options?.properties ?? {});
|
|
6542
|
+
const sessionId = sessionManager.getSessionId();
|
|
6543
|
+
const family = detectFamily(eventName);
|
|
6544
|
+
const isCustomFamily = family === "custom";
|
|
6545
|
+
const identityProps = isCustomFamily ? identityManager.getProperties() : {};
|
|
6546
|
+
const sessionProp = isCustomFamily && sessionId ? { sessionId } : {};
|
|
6547
|
+
await apiClient.trackEvent(
|
|
6548
|
+
eventName,
|
|
6549
|
+
distinctId,
|
|
6550
|
+
{ ...identityProps, ...options?.properties, ...sessionProp },
|
|
6551
|
+
options?.timestamp,
|
|
6552
|
+
options?.priority ? { priority: options.priority } : void 0
|
|
6553
|
+
);
|
|
6554
|
+
}
|
|
6555
|
+
async function trackOnboardingStep(apiClient, state, index, name) {
|
|
6556
|
+
const distinctId = identityManager.getDistinctId();
|
|
6557
|
+
if (!distinctId) return;
|
|
6558
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
6559
|
+
const msg = `Invalid stepIndex: "${index}". Must be a non-negative integer.`;
|
|
6560
|
+
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_STEP_INDEX, msg);
|
|
6561
|
+
}
|
|
6562
|
+
const now = Date.now();
|
|
6563
|
+
const timeOnPreviousStep = state.lastOnboardingStepTimestamp !== null ? Math.round((now - state.lastOnboardingStepTimestamp) / 1e3) : 0;
|
|
6564
|
+
state.lastOnboardingStepTimestamp = now;
|
|
6565
|
+
await apiClient.trackEvent("$onboarding_step", distinctId, { stepIndex: index, stepName: name, timeOnPreviousStep });
|
|
6566
|
+
}
|
|
6567
|
+
function validateCoreAction(actionName) {
|
|
6568
|
+
if (!actionName || typeof actionName !== "string") {
|
|
6569
|
+
const msg = `Invalid actionName: "${actionName}". Must be a non-empty string.`;
|
|
6570
|
+
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_ACTION_NAME, msg);
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6573
|
+
var init_PaywalloAnalytics = __esm({
|
|
6574
|
+
"src/core/PaywalloAnalytics.ts"() {
|
|
6575
|
+
"use strict";
|
|
6576
|
+
init_analytics();
|
|
6577
|
+
init_identity();
|
|
6578
|
+
init_session();
|
|
6579
|
+
init_ClientError();
|
|
6580
|
+
init_eventFamilies();
|
|
6581
|
+
}
|
|
6582
|
+
});
|
|
6583
|
+
|
|
6584
|
+
// src/core/PaywalloFlags.ts
|
|
6585
|
+
async function getVariantCached(apiClient, flagKey, defaultValue) {
|
|
6586
|
+
const distinctId = identityManager.getDistinctId();
|
|
6587
|
+
const result = await apiClient.getVariantFromCache(flagKey, distinctId);
|
|
6588
|
+
if (result) return result;
|
|
6589
|
+
void apiClient.getVariant(flagKey, distinctId).catch(() => {
|
|
6590
|
+
});
|
|
6591
|
+
return { variant: defaultValue ?? null, payload: void 0 };
|
|
6592
|
+
}
|
|
6593
|
+
async function getVariant(apiClient, state, flagKey) {
|
|
6594
|
+
try {
|
|
6595
|
+
const distinctId = identityManager.getDistinctId();
|
|
6596
|
+
if (!distinctId) {
|
|
6597
|
+
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 null (no distinctId)`);
|
|
6598
|
+
return { variant: null };
|
|
6599
|
+
}
|
|
6600
|
+
const result = await apiClient.getVariant(flagKey, distinctId);
|
|
6601
|
+
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192`, result.variant);
|
|
6602
|
+
return result;
|
|
6603
|
+
} catch (error) {
|
|
6604
|
+
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 error`, error);
|
|
6605
|
+
if (state.apiClient) void state.apiClient.reportError("GET_VARIANT_FAILED", error instanceof Error ? error.message : String(error));
|
|
6606
|
+
return { variant: null };
|
|
6607
|
+
}
|
|
6608
|
+
}
|
|
6609
|
+
function evaluateFlags(apiClient, keys) {
|
|
6610
|
+
return apiClient.evaluateFlags(keys, identityManager.getDistinctId());
|
|
6611
|
+
}
|
|
6612
|
+
async function getConditionalFlag(apiClient, state, flagKey, context) {
|
|
6613
|
+
try {
|
|
6614
|
+
const distinctId = identityManager.getDistinctId();
|
|
6615
|
+
if (!distinctId) return false;
|
|
6616
|
+
const result = await apiClient.getConditionalFlag(flagKey, { distinctId, ...context });
|
|
6617
|
+
return result.value;
|
|
6618
|
+
} catch (error) {
|
|
6619
|
+
if (state.apiClient) void state.apiClient.reportError("GET_CONDITIONAL_FLAG_FAILED", error instanceof Error ? error.message : String(error));
|
|
6620
|
+
return false;
|
|
6621
|
+
}
|
|
6622
|
+
}
|
|
6623
|
+
var init_PaywalloFlags = __esm({
|
|
6624
|
+
"src/core/PaywalloFlags.ts"() {
|
|
6625
|
+
"use strict";
|
|
6626
|
+
init_identity();
|
|
6627
|
+
}
|
|
6628
|
+
});
|
|
6629
|
+
|
|
6630
|
+
// src/domains/onboarding/OnboardingError.ts
|
|
6631
|
+
var OnboardingError, ONBOARDING_ERROR_CODES;
|
|
6632
|
+
var init_OnboardingError = __esm({
|
|
6633
|
+
"src/domains/onboarding/OnboardingError.ts"() {
|
|
6634
|
+
"use strict";
|
|
6635
|
+
init_PaywalloError();
|
|
6636
|
+
OnboardingError = class extends PaywalloError {
|
|
6141
6637
|
constructor(code, message) {
|
|
6142
6638
|
super("onboarding", code, message);
|
|
6143
6639
|
this.name = "OnboardingError";
|
|
@@ -6346,49 +6842,49 @@ var init_SubscriptionCache = __esm({
|
|
|
6346
6842
|
await this.storage.remove(keyFor(distinctId));
|
|
6347
6843
|
return null;
|
|
6348
6844
|
}
|
|
6349
|
-
const
|
|
6350
|
-
if (
|
|
6351
|
-
|
|
6845
|
+
const cached2 = parsed;
|
|
6846
|
+
if (cached2.data.subscription?.expiresAt) {
|
|
6847
|
+
cached2.data.subscription.expiresAt = new Date(cached2.data.subscription.expiresAt);
|
|
6352
6848
|
}
|
|
6353
|
-
if (
|
|
6354
|
-
|
|
6355
|
-
|
|
6849
|
+
if (cached2.data.subscription?.originalPurchaseDate) {
|
|
6850
|
+
cached2.data.subscription.originalPurchaseDate = new Date(
|
|
6851
|
+
cached2.data.subscription.originalPurchaseDate
|
|
6356
6852
|
);
|
|
6357
6853
|
}
|
|
6358
|
-
if (
|
|
6359
|
-
|
|
6360
|
-
|
|
6854
|
+
if (cached2.data.subscription?.latestPurchaseDate) {
|
|
6855
|
+
cached2.data.subscription.latestPurchaseDate = new Date(
|
|
6856
|
+
cached2.data.subscription.latestPurchaseDate
|
|
6361
6857
|
);
|
|
6362
6858
|
}
|
|
6363
|
-
if (
|
|
6364
|
-
|
|
6365
|
-
|
|
6859
|
+
if (cached2.data.subscription?.cancellationDate) {
|
|
6860
|
+
cached2.data.subscription.cancellationDate = new Date(
|
|
6861
|
+
cached2.data.subscription.cancellationDate
|
|
6366
6862
|
);
|
|
6367
6863
|
}
|
|
6368
|
-
if (
|
|
6369
|
-
|
|
6370
|
-
|
|
6864
|
+
if (cached2.data.subscription?.gracePeriodExpiresAt) {
|
|
6865
|
+
cached2.data.subscription.gracePeriodExpiresAt = new Date(
|
|
6866
|
+
cached2.data.subscription.gracePeriodExpiresAt
|
|
6371
6867
|
);
|
|
6372
6868
|
}
|
|
6373
|
-
|
|
6374
|
-
if (!
|
|
6375
|
-
this.memoryCache.set(distinctId,
|
|
6869
|
+
cached2.isStale = this.isExpired(cached2);
|
|
6870
|
+
if (!cached2.isStale) {
|
|
6871
|
+
this.memoryCache.set(distinctId, cached2);
|
|
6376
6872
|
}
|
|
6377
|
-
return
|
|
6873
|
+
return cached2;
|
|
6378
6874
|
} catch {
|
|
6379
6875
|
return null;
|
|
6380
6876
|
}
|
|
6381
6877
|
}
|
|
6382
6878
|
async set(distinctId, data) {
|
|
6383
6879
|
if (!distinctId) return;
|
|
6384
|
-
const
|
|
6880
|
+
const cached2 = {
|
|
6385
6881
|
data,
|
|
6386
6882
|
cachedAt: Date.now(),
|
|
6387
6883
|
isStale: false
|
|
6388
6884
|
};
|
|
6389
|
-
this.memoryCache.set(distinctId,
|
|
6885
|
+
this.memoryCache.set(distinctId, cached2);
|
|
6390
6886
|
try {
|
|
6391
|
-
await this.storage.set(keyFor(distinctId), JSON.stringify(
|
|
6887
|
+
await this.storage.set(keyFor(distinctId), JSON.stringify(cached2));
|
|
6392
6888
|
await this.addToIndex(distinctId);
|
|
6393
6889
|
} catch {
|
|
6394
6890
|
}
|
|
@@ -6432,8 +6928,8 @@ var init_SubscriptionCache = __esm({
|
|
|
6432
6928
|
...allKeys.map((k) => this.storage.remove(keyFor(k)))
|
|
6433
6929
|
]);
|
|
6434
6930
|
}
|
|
6435
|
-
isExpired(
|
|
6436
|
-
return Date.now() -
|
|
6931
|
+
isExpired(cached2) {
|
|
6932
|
+
return Date.now() - cached2.cachedAt > this.ttl;
|
|
6437
6933
|
}
|
|
6438
6934
|
setTTL(ttl) {
|
|
6439
6935
|
this.ttl = ttl;
|
|
@@ -6489,9 +6985,9 @@ var init_SubscriptionManager = __esm({
|
|
|
6489
6985
|
return this.getEmptyStatus();
|
|
6490
6986
|
}
|
|
6491
6987
|
if (!forceRefresh) {
|
|
6492
|
-
const
|
|
6493
|
-
if (
|
|
6494
|
-
return
|
|
6988
|
+
const cached2 = await this.cache.get(this.cacheKey());
|
|
6989
|
+
if (cached2 && !cached2.isStale) {
|
|
6990
|
+
return cached2.data;
|
|
6495
6991
|
}
|
|
6496
6992
|
}
|
|
6497
6993
|
try {
|
|
@@ -6501,9 +6997,9 @@ var init_SubscriptionManager = __esm({
|
|
|
6501
6997
|
return status;
|
|
6502
6998
|
} catch (error) {
|
|
6503
6999
|
this.log("Failed to fetch subscription status", error);
|
|
6504
|
-
const
|
|
6505
|
-
if (
|
|
6506
|
-
return
|
|
7000
|
+
const cached2 = await this.cache.get(this.cacheKey());
|
|
7001
|
+
if (cached2) {
|
|
7002
|
+
return cached2.data;
|
|
6507
7003
|
}
|
|
6508
7004
|
return this.getEmptyStatus();
|
|
6509
7005
|
}
|
|
@@ -6790,288 +7286,131 @@ async function writeFlagToStorage(flagKey, distinctId, flag) {
|
|
|
6790
7286
|
}
|
|
6791
7287
|
}
|
|
6792
7288
|
var FLAG_MAX_AGE_MS;
|
|
6793
|
-
var init_FlagStorage = __esm({
|
|
6794
|
-
"src/core/FlagStorage.ts"() {
|
|
6795
|
-
"use strict";
|
|
6796
|
-
init_SecureStorage();
|
|
6797
|
-
FLAG_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
6798
|
-
}
|
|
6799
|
-
});
|
|
6800
|
-
|
|
6801
|
-
// src/core/ApiClientFlags.ts
|
|
6802
|
-
async function getVariant2(deps, flagKey, distinctId) {
|
|
6803
|
-
const key = `${flagKey}:${distinctId}`;
|
|
6804
|
-
const hit = deps.cache.getVariant(key);
|
|
6805
|
-
if (hit !== void 0) return hit;
|
|
6806
|
-
const persisted = await readFlagFromStorage(flagKey, distinctId);
|
|
6807
|
-
if (persisted !== null) {
|
|
6808
|
-
deps.cache.setVariant(key, persisted);
|
|
6809
|
-
return persisted;
|
|
6810
|
-
}
|
|
6811
|
-
try {
|
|
6812
|
-
const res = await deps.get(`/sdk/flags/${flagKey}?distinctId=${encodeURIComponent(distinctId)}`);
|
|
6813
|
-
if (res.status === 404) {
|
|
6814
|
-
const v = { variant: null };
|
|
6815
|
-
deps.cache.setVariant(key, v, deps.cache.nullTTL);
|
|
6816
|
-
return v;
|
|
6817
|
-
}
|
|
6818
|
-
if (!res.ok) {
|
|
6819
|
-
const stale = deps.cache.getStaleVariant(key);
|
|
6820
|
-
return stale !== void 0 ? stale : { variant: null };
|
|
6821
|
-
}
|
|
6822
|
-
deps.cache.setVariant(key, res.data);
|
|
6823
|
-
await writeFlagToStorage(flagKey, distinctId, res.data);
|
|
6824
|
-
return res.data;
|
|
6825
|
-
} catch {
|
|
6826
|
-
const stale = deps.cache.getStaleVariant(key);
|
|
6827
|
-
return stale !== void 0 ? stale : { variant: null };
|
|
6828
|
-
}
|
|
6829
|
-
}
|
|
6830
|
-
async function evaluateFlags2(deps, keys, distinctId) {
|
|
6831
|
-
const query = keys.map(encodeURIComponent).join(",");
|
|
6832
|
-
try {
|
|
6833
|
-
const res = await deps.httpClient.get(`/sdk/flags/evaluate?keys=${query}`, {
|
|
6834
|
-
headers: { "X-App-Key": deps.appKey, "x-distinct-id": distinctId }
|
|
6835
|
-
});
|
|
6836
|
-
if (!res.ok || typeof res.data !== "object" || res.data === null || Array.isArray(res.data)) return {};
|
|
6837
|
-
const record = res.data;
|
|
6838
|
-
const result = {};
|
|
6839
|
-
for (const key of Object.keys(record)) {
|
|
6840
|
-
const value = record[key];
|
|
6841
|
-
const variant = typeof value === "string" ? value : null;
|
|
6842
|
-
result[key] = variant;
|
|
6843
|
-
const flagVariant = { variant };
|
|
6844
|
-
deps.cache.setVariant(`${key}:${distinctId}`, flagVariant);
|
|
6845
|
-
await writeFlagToStorage(key, distinctId, flagVariant);
|
|
6846
|
-
}
|
|
6847
|
-
return result;
|
|
6848
|
-
} catch {
|
|
6849
|
-
return {};
|
|
6850
|
-
}
|
|
6851
|
-
}
|
|
6852
|
-
async function getConditionalFlag2(deps, flagKey, context) {
|
|
6853
|
-
try {
|
|
6854
|
-
const url = buildConditionalFlagUrl(deps.baseUrl, flagKey, context);
|
|
6855
|
-
const response = await deps.get(url);
|
|
6856
|
-
if (response.status === 404 || !response.ok) return { value: false, flagKey };
|
|
6857
|
-
return { value: response.data.value, flagKey };
|
|
6858
|
-
} catch {
|
|
6859
|
-
return { value: false, flagKey };
|
|
6860
|
-
}
|
|
6861
|
-
}
|
|
6862
|
-
async function getVariantFromCache(deps, flagKey, distinctId) {
|
|
6863
|
-
const key = `${flagKey}:${distinctId}`;
|
|
6864
|
-
const hit = deps.cache.getVariant(key);
|
|
6865
|
-
if (hit !== void 0) return hit;
|
|
6866
|
-
const persisted = await readFlagFromStorage(flagKey, distinctId);
|
|
6867
|
-
if (persisted !== null) {
|
|
6868
|
-
deps.cache.setVariant(key, persisted);
|
|
6869
|
-
return persisted;
|
|
6870
|
-
}
|
|
6871
|
-
return null;
|
|
6872
|
-
}
|
|
6873
|
-
var init_ApiClientFlags = __esm({
|
|
6874
|
-
"src/core/ApiClientFlags.ts"() {
|
|
6875
|
-
"use strict";
|
|
6876
|
-
init_apiUrlUtils();
|
|
6877
|
-
init_FlagStorage();
|
|
6878
|
-
}
|
|
6879
|
-
});
|
|
6880
|
-
|
|
6881
|
-
// src/core/ApiClientQueue.ts
|
|
6882
|
-
async function postWithQueue(deps, url, payload, label, priority) {
|
|
6883
|
-
if (deps.isOfflineQueueEnabled() && !networkMonitor.isOnline()) {
|
|
6884
|
-
await offlineQueue.enqueue(
|
|
6885
|
-
"POST",
|
|
6886
|
-
url,
|
|
6887
|
-
payload,
|
|
6888
|
-
{ "X-App-Key": deps.getAppKey() },
|
|
6889
|
-
void 0,
|
|
6890
|
-
priority
|
|
6891
|
-
);
|
|
6892
|
-
if (deps.isDebug()) console.log("[Paywallo:ApiClient] Queued for offline:", label, priority ?? "normal");
|
|
6893
|
-
return;
|
|
6894
|
-
}
|
|
6895
|
-
try {
|
|
6896
|
-
await deps.post(url, payload);
|
|
6897
|
-
} catch (error) {
|
|
6898
|
-
if (deps.isDebug()) console.log("[Paywallo:ApiClient] Request failed:", label, error);
|
|
6899
|
-
if (deps.isOfflineQueueEnabled()) {
|
|
6900
|
-
await offlineQueue.enqueue(
|
|
6901
|
-
"POST",
|
|
6902
|
-
url,
|
|
6903
|
-
payload,
|
|
6904
|
-
{ "X-App-Key": deps.getAppKey() },
|
|
6905
|
-
void 0,
|
|
6906
|
-
priority
|
|
6907
|
-
);
|
|
6908
|
-
return;
|
|
6909
|
-
}
|
|
6910
|
-
throw error;
|
|
6911
|
-
}
|
|
6912
|
-
}
|
|
6913
|
-
var init_ApiClientQueue = __esm({
|
|
6914
|
-
"src/core/ApiClientQueue.ts"() {
|
|
6915
|
-
"use strict";
|
|
6916
|
-
init_network();
|
|
6917
|
-
init_queue();
|
|
6918
|
-
}
|
|
6919
|
-
});
|
|
6920
|
-
|
|
6921
|
-
// src/core/events/schemas.ts
|
|
6922
|
-
import { z } from "zod";
|
|
6923
|
-
var lifecycleEventSchema, identifyEventSchema, paywallEventSchema, transactionEventSchema, onboardingEventSchema, notificationEventSchema, customEventSchema, EVENT_SCHEMAS;
|
|
6924
|
-
var init_schemas = __esm({
|
|
6925
|
-
"src/core/events/schemas.ts"() {
|
|
6926
|
-
"use strict";
|
|
6927
|
-
lifecycleEventSchema = z.object({
|
|
6928
|
-
type: z.enum([
|
|
6929
|
-
"install",
|
|
6930
|
-
"cold_start",
|
|
6931
|
-
"foreground",
|
|
6932
|
-
"background",
|
|
6933
|
-
"session_start",
|
|
6934
|
-
"session_end"
|
|
6935
|
-
]),
|
|
6936
|
-
session_id: z.string().optional(),
|
|
6937
|
-
duration_s: z.number().nonnegative().optional(),
|
|
6938
|
-
first_ever: z.boolean().optional(),
|
|
6939
|
-
// Contexto leve transportado em lifecycle (útil pra cold_start/install)
|
|
6940
|
-
app_version: z.string().optional(),
|
|
6941
|
-
device_type: z.enum(["ios", "android"]).optional(),
|
|
6942
|
-
os_version: z.string().optional(),
|
|
6943
|
-
// Timestamps opcionais — o EventBatcher injeta `timestamp` no envelope
|
|
6944
|
-
started_at: z.string().optional(),
|
|
6945
|
-
ended_at: z.string().optional()
|
|
6946
|
-
}).strict();
|
|
6947
|
-
identifyEventSchema = z.object({
|
|
6948
|
-
distinct_id: z.string().min(1),
|
|
6949
|
-
email: z.email().optional(),
|
|
6950
|
-
traits: z.record(z.string(), z.unknown()).optional(),
|
|
6951
|
-
attribution: z.record(z.string(), z.unknown()).optional(),
|
|
6952
|
-
device: z.record(z.string(), z.unknown()).optional()
|
|
6953
|
-
}).strict();
|
|
6954
|
-
paywallEventSchema = z.object({
|
|
6955
|
-
type: z.enum(["viewed", "closed", "purchased"]),
|
|
6956
|
-
paywall_id: z.string().min(1),
|
|
6957
|
-
placement: z.string().optional(),
|
|
6958
|
-
variant_id: z.string().optional(),
|
|
6959
|
-
variant_key: z.string().optional(),
|
|
6960
|
-
campaign_id: z.string().optional(),
|
|
6961
|
-
// `closed`-specific
|
|
6962
|
-
closed_at: z.string().optional(),
|
|
6963
|
-
duration_s: z.number().nonnegative().optional(),
|
|
6964
|
-
close_reason: z.enum(["dismiss", "cta", "purchase", "error", "timeout"]).optional(),
|
|
6965
|
-
scroll_depth: z.number().min(0).max(1).optional(),
|
|
6966
|
-
// `purchased`-specific (mínimo — transaction canônica leva detalhes)
|
|
6967
|
-
product_id: z.string().optional()
|
|
6968
|
-
}).strict();
|
|
6969
|
-
transactionEventSchema = z.object({
|
|
6970
|
-
type: z.enum([
|
|
6971
|
-
"completed",
|
|
6972
|
-
"failed",
|
|
6973
|
-
"refunded",
|
|
6974
|
-
"renewed",
|
|
6975
|
-
"canceled",
|
|
6976
|
-
"expired"
|
|
6977
|
-
]),
|
|
6978
|
-
tx_id: z.string().min(1),
|
|
6979
|
-
amount: z.number().optional(),
|
|
6980
|
-
currency: z.string().length(3).optional(),
|
|
6981
|
-
product_id: z.string().optional(),
|
|
6982
|
-
subscription_id: z.string().optional(),
|
|
6983
|
-
paywall_id: z.string().optional(),
|
|
6984
|
-
variant_id: z.string().optional(),
|
|
6985
|
-
reason: z.string().optional()
|
|
6986
|
-
}).strict();
|
|
6987
|
-
onboardingEventSchema = z.object({
|
|
6988
|
-
type: z.enum(["step", "complete", "drop"]),
|
|
6989
|
-
step_index: z.number().int().nonnegative().optional(),
|
|
6990
|
-
step_name: z.string().optional(),
|
|
6991
|
-
variant_key: z.string().optional(),
|
|
6992
|
-
time_on_prev_s: z.number().nonnegative().optional()
|
|
6993
|
-
}).strict();
|
|
6994
|
-
notificationEventSchema = z.object({
|
|
6995
|
-
type: z.enum(["delivered", "displayed", "clicked", "dismissed"]),
|
|
6996
|
-
notification_id: z.string().optional(),
|
|
6997
|
-
campaign_id: z.string().optional(),
|
|
6998
|
-
variant_key: z.string().nullable().optional(),
|
|
6999
|
-
message_id: z.string().optional(),
|
|
7000
|
-
device_id: z.string().optional(),
|
|
7001
|
-
push_platform: z.enum(["ios", "android"]).optional(),
|
|
7002
|
-
timezone: z.string().optional(),
|
|
7003
|
-
failure_reason: z.string().optional(),
|
|
7004
|
-
app_user_id: z.string().optional()
|
|
7005
|
-
}).strict();
|
|
7006
|
-
customEventSchema = z.object({
|
|
7007
|
-
name: z.string().min(1).regex(
|
|
7008
|
-
/^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/,
|
|
7009
|
-
"custom event name must be snake_case or $reserved"
|
|
7010
|
-
),
|
|
7011
|
-
props: z.record(z.string(), z.unknown()).optional()
|
|
7012
|
-
}).strict();
|
|
7013
|
-
EVENT_SCHEMAS = {
|
|
7014
|
-
lifecycle: lifecycleEventSchema,
|
|
7015
|
-
identify: identifyEventSchema,
|
|
7016
|
-
paywall: paywallEventSchema,
|
|
7017
|
-
transaction: transactionEventSchema,
|
|
7018
|
-
onboarding: onboardingEventSchema,
|
|
7019
|
-
notification: notificationEventSchema,
|
|
7020
|
-
custom: customEventSchema
|
|
7021
|
-
};
|
|
7289
|
+
var init_FlagStorage = __esm({
|
|
7290
|
+
"src/core/FlagStorage.ts"() {
|
|
7291
|
+
"use strict";
|
|
7292
|
+
init_SecureStorage();
|
|
7293
|
+
FLAG_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
7022
7294
|
}
|
|
7023
7295
|
});
|
|
7024
7296
|
|
|
7025
|
-
// src/core/
|
|
7026
|
-
function
|
|
7027
|
-
|
|
7297
|
+
// src/core/ApiClientFlags.ts
|
|
7298
|
+
async function getVariant2(deps, flagKey, distinctId) {
|
|
7299
|
+
const key = `${flagKey}:${distinctId}`;
|
|
7300
|
+
const hit = deps.cache.getVariant(key);
|
|
7301
|
+
if (hit !== void 0) return hit;
|
|
7302
|
+
const persisted = await readFlagFromStorage(flagKey, distinctId);
|
|
7303
|
+
if (persisted !== null) {
|
|
7304
|
+
deps.cache.setVariant(key, persisted);
|
|
7305
|
+
return persisted;
|
|
7306
|
+
}
|
|
7307
|
+
try {
|
|
7308
|
+
const res = await deps.get(`/sdk/flags/${flagKey}?distinctId=${encodeURIComponent(distinctId)}`);
|
|
7309
|
+
if (res.status === 404) {
|
|
7310
|
+
const v = { variant: null };
|
|
7311
|
+
deps.cache.setVariant(key, v, deps.cache.nullTTL);
|
|
7312
|
+
return v;
|
|
7313
|
+
}
|
|
7314
|
+
if (!res.ok) {
|
|
7315
|
+
const stale = deps.cache.getStaleVariant(key);
|
|
7316
|
+
return stale !== void 0 ? stale : { variant: null };
|
|
7317
|
+
}
|
|
7318
|
+
deps.cache.setVariant(key, res.data);
|
|
7319
|
+
await writeFlagToStorage(flagKey, distinctId, res.data);
|
|
7320
|
+
return res.data;
|
|
7321
|
+
} catch {
|
|
7322
|
+
const stale = deps.cache.getStaleVariant(key);
|
|
7323
|
+
return stale !== void 0 ? stale : { variant: null };
|
|
7324
|
+
}
|
|
7028
7325
|
}
|
|
7029
|
-
function
|
|
7030
|
-
|
|
7326
|
+
async function evaluateFlags2(deps, keys, distinctId) {
|
|
7327
|
+
const query = keys.map(encodeURIComponent).join(",");
|
|
7328
|
+
try {
|
|
7329
|
+
const res = await deps.httpClient.get(`/sdk/flags/evaluate?keys=${query}`, {
|
|
7330
|
+
headers: { "X-App-Key": deps.appKey, "x-distinct-id": distinctId }
|
|
7331
|
+
});
|
|
7332
|
+
if (!res.ok || typeof res.data !== "object" || res.data === null || Array.isArray(res.data)) return {};
|
|
7333
|
+
const record = res.data;
|
|
7334
|
+
const result = {};
|
|
7335
|
+
for (const key of Object.keys(record)) {
|
|
7336
|
+
const value = record[key];
|
|
7337
|
+
const variant = typeof value === "string" ? value : null;
|
|
7338
|
+
result[key] = variant;
|
|
7339
|
+
const flagVariant = { variant };
|
|
7340
|
+
deps.cache.setVariant(`${key}:${distinctId}`, flagVariant);
|
|
7341
|
+
await writeFlagToStorage(key, distinctId, flagVariant);
|
|
7342
|
+
}
|
|
7343
|
+
return result;
|
|
7344
|
+
} catch {
|
|
7345
|
+
return {};
|
|
7346
|
+
}
|
|
7031
7347
|
}
|
|
7032
|
-
function
|
|
7033
|
-
|
|
7034
|
-
|
|
7348
|
+
async function getConditionalFlag2(deps, flagKey, context) {
|
|
7349
|
+
try {
|
|
7350
|
+
const url = buildConditionalFlagUrl(deps.baseUrl, flagKey, context);
|
|
7351
|
+
const response = await deps.get(url);
|
|
7352
|
+
if (response.status === 404 || !response.ok) return { value: false, flagKey };
|
|
7353
|
+
return { value: response.data.value, flagKey };
|
|
7354
|
+
} catch {
|
|
7355
|
+
return { value: false, flagKey };
|
|
7035
7356
|
}
|
|
7036
|
-
return "custom";
|
|
7037
7357
|
}
|
|
7038
|
-
function
|
|
7039
|
-
const
|
|
7040
|
-
const
|
|
7041
|
-
|
|
7042
|
-
const
|
|
7043
|
-
if (
|
|
7044
|
-
|
|
7358
|
+
async function getVariantFromCache(deps, flagKey, distinctId) {
|
|
7359
|
+
const key = `${flagKey}:${distinctId}`;
|
|
7360
|
+
const hit = deps.cache.getVariant(key);
|
|
7361
|
+
if (hit !== void 0) return hit;
|
|
7362
|
+
const persisted = await readFlagFromStorage(flagKey, distinctId);
|
|
7363
|
+
if (persisted !== null) {
|
|
7364
|
+
deps.cache.setVariant(key, persisted);
|
|
7365
|
+
return persisted;
|
|
7045
7366
|
}
|
|
7046
|
-
return
|
|
7367
|
+
return null;
|
|
7047
7368
|
}
|
|
7048
|
-
|
|
7049
|
-
|
|
7369
|
+
var init_ApiClientFlags = __esm({
|
|
7370
|
+
"src/core/ApiClientFlags.ts"() {
|
|
7371
|
+
"use strict";
|
|
7372
|
+
init_apiUrlUtils();
|
|
7373
|
+
init_FlagStorage();
|
|
7374
|
+
}
|
|
7375
|
+
});
|
|
7376
|
+
|
|
7377
|
+
// src/core/ApiClientQueue.ts
|
|
7378
|
+
async function postWithQueue(deps, url, payload, label, priority) {
|
|
7379
|
+
if (deps.isOfflineQueueEnabled() && !networkMonitor.isOnline()) {
|
|
7380
|
+
await offlineQueue.enqueue(
|
|
7381
|
+
"POST",
|
|
7382
|
+
url,
|
|
7383
|
+
payload,
|
|
7384
|
+
{ "X-App-Key": deps.getAppKey() },
|
|
7385
|
+
void 0,
|
|
7386
|
+
priority
|
|
7387
|
+
);
|
|
7388
|
+
if (deps.isDebug()) console.log("[Paywallo:ApiClient] Queued for offline:", label, priority ?? "normal");
|
|
7389
|
+
return;
|
|
7390
|
+
}
|
|
7391
|
+
try {
|
|
7392
|
+
await deps.post(url, payload);
|
|
7393
|
+
} catch (error) {
|
|
7394
|
+
if (deps.isDebug()) console.log("[Paywallo:ApiClient] Request failed:", label, error);
|
|
7395
|
+
if (deps.isOfflineQueueEnabled()) {
|
|
7396
|
+
await offlineQueue.enqueue(
|
|
7397
|
+
"POST",
|
|
7398
|
+
url,
|
|
7399
|
+
payload,
|
|
7400
|
+
{ "X-App-Key": deps.getAppKey() },
|
|
7401
|
+
void 0,
|
|
7402
|
+
priority
|
|
7403
|
+
);
|
|
7404
|
+
return;
|
|
7405
|
+
}
|
|
7406
|
+
throw error;
|
|
7407
|
+
}
|
|
7050
7408
|
}
|
|
7051
|
-
var
|
|
7052
|
-
|
|
7053
|
-
"src/core/events/eventFamilies.ts"() {
|
|
7409
|
+
var init_ApiClientQueue = __esm({
|
|
7410
|
+
"src/core/ApiClientQueue.ts"() {
|
|
7054
7411
|
"use strict";
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
"lifecycle",
|
|
7058
|
-
"identify",
|
|
7059
|
-
"paywall",
|
|
7060
|
-
"transaction",
|
|
7061
|
-
"onboarding",
|
|
7062
|
-
"notification",
|
|
7063
|
-
"custom"
|
|
7064
|
-
];
|
|
7065
|
-
DEPRECATED_EVENT_NAMES = [
|
|
7066
|
-
"$paywall_purchased",
|
|
7067
|
-
"$paywall_product_selected",
|
|
7068
|
-
"$core_action",
|
|
7069
|
-
"$campaign_impression",
|
|
7070
|
-
"$app_open",
|
|
7071
|
-
"$app_background",
|
|
7072
|
-
"$app_foreground",
|
|
7073
|
-
"session.end"
|
|
7074
|
-
];
|
|
7412
|
+
init_network();
|
|
7413
|
+
init_queue();
|
|
7075
7414
|
}
|
|
7076
7415
|
});
|
|
7077
7416
|
|
|
@@ -7104,6 +7443,9 @@ function buildV2Envelope(events, providerContext) {
|
|
|
7104
7443
|
if (context.distinct_id === void 0 && events.length > 0) {
|
|
7105
7444
|
context.distinct_id = events[0].distinctId;
|
|
7106
7445
|
}
|
|
7446
|
+
if (!context.distinct_id || context.distinct_id.length === 0) {
|
|
7447
|
+
console.error("[Paywallo:ENVELOPE] distinct_id is empty after resolution \u2014 envelope will fail schema", { providerContext, eventCount: events.length, firstEventName: events[0]?.eventName });
|
|
7448
|
+
}
|
|
7107
7449
|
return { context, events: v2Events };
|
|
7108
7450
|
}
|
|
7109
7451
|
function resolveProviderContext(provider) {
|
|
@@ -7155,12 +7497,18 @@ var init_eventEnvelopeV2 = __esm({
|
|
|
7155
7497
|
session_id: z2.string().optional(),
|
|
7156
7498
|
device_id: z2.string().optional(),
|
|
7157
7499
|
app_version: z2.string().optional(),
|
|
7500
|
+
app_build: z2.string().optional(),
|
|
7501
|
+
bundle_id: z2.string().optional(),
|
|
7158
7502
|
sdk_version: z2.string().optional(),
|
|
7159
7503
|
platform: z2.string().optional(),
|
|
7160
7504
|
os_version: z2.string().optional(),
|
|
7161
7505
|
device_model: z2.string().optional(),
|
|
7162
7506
|
timezone: z2.string().optional(),
|
|
7163
7507
|
locale: z2.string().optional(),
|
|
7508
|
+
carrier: z2.string().optional(),
|
|
7509
|
+
screen_width: z2.number().optional(),
|
|
7510
|
+
screen_height: z2.number().optional(),
|
|
7511
|
+
screen_density: z2.number().optional(),
|
|
7164
7512
|
attribution: z2.record(z2.string(), z2.unknown()).optional(),
|
|
7165
7513
|
ids: z2.record(z2.string(), z2.unknown()).optional()
|
|
7166
7514
|
});
|
|
@@ -7173,12 +7521,21 @@ var init_eventEnvelopeV2 = __esm({
|
|
|
7173
7521
|
session_id: ["session_id", "sessionId"],
|
|
7174
7522
|
device_id: ["device_id", "deviceId"],
|
|
7175
7523
|
app_version: ["app_version", "appVersion"],
|
|
7524
|
+
// F2.2 — alias só snake_case canônico: campos vêm do context provider, não
|
|
7525
|
+
// de properties de evento. Evita promover (e deletar) chaves do payload do
|
|
7526
|
+
// $app_installed que o server lê (screenWidth etc.).
|
|
7527
|
+
app_build: ["app_build"],
|
|
7528
|
+
bundle_id: ["bundle_id"],
|
|
7176
7529
|
sdk_version: ["sdk_version", "sdkVersion"],
|
|
7177
7530
|
platform: ["platform"],
|
|
7178
7531
|
os_version: ["os_version", "osVersion", "systemVersion"],
|
|
7179
7532
|
device_model: ["device_model", "deviceModel", "model"],
|
|
7180
7533
|
timezone: ["timezone", "timeZone"],
|
|
7181
7534
|
locale: ["locale"],
|
|
7535
|
+
carrier: ["carrier_ctx"],
|
|
7536
|
+
screen_width: ["screen_width"],
|
|
7537
|
+
screen_height: ["screen_height"],
|
|
7538
|
+
screen_density: ["screen_density"],
|
|
7182
7539
|
attribution: ["attribution"],
|
|
7183
7540
|
ids: ["ids"]
|
|
7184
7541
|
};
|
|
@@ -7305,8 +7662,8 @@ var init_EventBatcher = __esm({
|
|
|
7305
7662
|
for (const event of drained) {
|
|
7306
7663
|
try {
|
|
7307
7664
|
await this.postBatch([event], `event_critical:${event.eventName}`, "critical");
|
|
7308
|
-
} catch {
|
|
7309
|
-
|
|
7665
|
+
} catch (err) {
|
|
7666
|
+
console.error("[Paywallo:CRITICAL] critical event post failed", { eventName: event.eventName, error: err instanceof Error ? err.message : String(err) });
|
|
7310
7667
|
}
|
|
7311
7668
|
}
|
|
7312
7669
|
}
|
|
@@ -7319,8 +7676,8 @@ var init_EventBatcher = __esm({
|
|
|
7319
7676
|
}
|
|
7320
7677
|
try {
|
|
7321
7678
|
await this.postBatch(batch, `event_batch:${batch.length}`);
|
|
7322
|
-
} catch {
|
|
7323
|
-
|
|
7679
|
+
} catch (err) {
|
|
7680
|
+
console.error("[Paywallo:NORMAL] batch post failed \u2014 relying on OfflineQueue", { batchSize: batch.length, error: err instanceof Error ? err.message : String(err) });
|
|
7324
7681
|
}
|
|
7325
7682
|
}
|
|
7326
7683
|
async postBatch(events, label, priority = "normal") {
|
|
@@ -7335,6 +7692,7 @@ var init_EventBatcher = __esm({
|
|
|
7335
7692
|
const parsed = v2EnvelopeSchema.safeParse(envelope);
|
|
7336
7693
|
if (!parsed.success) {
|
|
7337
7694
|
if (this.debug) console.log("[Paywallo EVENTS] V2 envelope schema validation failed:", parsed.error.issues);
|
|
7695
|
+
console.error("[Paywallo:CRITICAL] V2 envelope schema validation failed", { issues: parsed.error.issues, eventCount: events.length, firstEventName: events[0]?.eventName, context: providerContext });
|
|
7338
7696
|
throw parsed.error;
|
|
7339
7697
|
}
|
|
7340
7698
|
await this.post(V2_BATCH_ENDPOINT, parsed.data, label, priority);
|
|
@@ -7917,7 +8275,8 @@ function setupNotifications(apiClient, config, environment, eventPipeline) {
|
|
|
7917
8275
|
httpClient: apiClient.getHttpClient(),
|
|
7918
8276
|
secureStorage: new SecureStorage(config.debug),
|
|
7919
8277
|
getDeviceId: () => identityManager.getDeviceId(),
|
|
7920
|
-
getDistinctId: () => identityManager.getDistinctId()
|
|
8278
|
+
getDistinctId: () => identityManager.getDistinctId(),
|
|
8279
|
+
environment: environment === "Sandbox" ? "sandbox" : "production"
|
|
7921
8280
|
});
|
|
7922
8281
|
notificationsManager.setupHandlers({ eventBatcher: eventPipeline });
|
|
7923
8282
|
notificationsManager.initialize({
|
|
@@ -8001,7 +8360,38 @@ var init_PaywalloState = __esm({
|
|
|
8001
8360
|
}
|
|
8002
8361
|
});
|
|
8003
8362
|
|
|
8363
|
+
// src/utils/detectEnvironment.ts
|
|
8364
|
+
import { NativeModules as NativeModules8 } from "react-native";
|
|
8365
|
+
async function detectEnvironment() {
|
|
8366
|
+
if (cached !== null) return cached;
|
|
8367
|
+
if (typeof globalThis.__DEV__ !== "undefined" && globalThis.__DEV__) {
|
|
8368
|
+
cached = "sandbox";
|
|
8369
|
+
return cached;
|
|
8370
|
+
}
|
|
8371
|
+
if (!nativeModule) {
|
|
8372
|
+
cached = "production";
|
|
8373
|
+
return cached;
|
|
8374
|
+
}
|
|
8375
|
+
try {
|
|
8376
|
+
const result = await nativeModule.detect();
|
|
8377
|
+
cached = result === "sandbox" ? "sandbox" : "production";
|
|
8378
|
+
return cached;
|
|
8379
|
+
} catch {
|
|
8380
|
+
cached = "production";
|
|
8381
|
+
return cached;
|
|
8382
|
+
}
|
|
8383
|
+
}
|
|
8384
|
+
var nativeModule, cached;
|
|
8385
|
+
var init_detectEnvironment = __esm({
|
|
8386
|
+
"src/utils/detectEnvironment.ts"() {
|
|
8387
|
+
"use strict";
|
|
8388
|
+
nativeModule = NativeModules8.PaywalloEnv ?? null;
|
|
8389
|
+
cached = null;
|
|
8390
|
+
}
|
|
8391
|
+
});
|
|
8392
|
+
|
|
8004
8393
|
// src/core/PaywalloInitializer.ts
|
|
8394
|
+
import { Dimensions as Dimensions4, PixelRatio as PixelRatio2 } from "react-native";
|
|
8005
8395
|
async function initWithRetry(state, config) {
|
|
8006
8396
|
while (state.initAttempts <= MAX_INIT_RETRIES) {
|
|
8007
8397
|
try {
|
|
@@ -8080,7 +8470,13 @@ async function resolveSessionFlags(state, keys, apiClient, distinctId) {
|
|
|
8080
8470
|
}
|
|
8081
8471
|
}
|
|
8082
8472
|
async function doInit(state, config) {
|
|
8083
|
-
|
|
8473
|
+
let environment;
|
|
8474
|
+
if (config.environment) {
|
|
8475
|
+
environment = config.environment;
|
|
8476
|
+
} else {
|
|
8477
|
+
const detected = await detectEnvironment();
|
|
8478
|
+
environment = detected === "sandbox" ? "Sandbox" : "Production";
|
|
8479
|
+
}
|
|
8084
8480
|
const offlineQueueEnabled = config.offlineQueueEnabled !== false;
|
|
8085
8481
|
await Promise.all([
|
|
8086
8482
|
networkMonitor.initialize({ debug: config.debug }),
|
|
@@ -8168,15 +8564,31 @@ async function doInit(state, config) {
|
|
|
8168
8564
|
timezone = opts.timeZone || void 0;
|
|
8169
8565
|
} catch {
|
|
8170
8566
|
}
|
|
8567
|
+
let screenWidth;
|
|
8568
|
+
let screenHeight;
|
|
8569
|
+
let screenDensity;
|
|
8570
|
+
try {
|
|
8571
|
+
const screen = Dimensions4.get("screen");
|
|
8572
|
+
screenWidth = Math.round(screen.width) || void 0;
|
|
8573
|
+
screenHeight = Math.round(screen.height) || void 0;
|
|
8574
|
+
screenDensity = PixelRatio2.get() || void 0;
|
|
8575
|
+
} catch {
|
|
8576
|
+
}
|
|
8171
8577
|
return {
|
|
8172
8578
|
distinct_id: identityManager.getDistinctId() || void 0,
|
|
8173
8579
|
device_id: identityManager.getDeviceId() ?? void 0,
|
|
8174
8580
|
session_id: sessionManager.getSessionId() ?? void 0,
|
|
8175
8581
|
app_version: device?.appVersion && device.appVersion !== "0.0.0" ? device.appVersion : void 0,
|
|
8582
|
+
app_build: device?.buildNumber && device.buildNumber !== "0" ? device.buildNumber : void 0,
|
|
8583
|
+
bundle_id: device?.bundleId || void 0,
|
|
8176
8584
|
os_version: device?.systemVersion && device.systemVersion !== "0.0.0" ? device.systemVersion : void 0,
|
|
8177
8585
|
device_model: device?.modelId || device?.model || void 0,
|
|
8178
8586
|
timezone,
|
|
8179
8587
|
locale,
|
|
8588
|
+
carrier: device?.carrier || void 0,
|
|
8589
|
+
screen_width: screenWidth,
|
|
8590
|
+
screen_height: screenHeight,
|
|
8591
|
+
screen_density: screenDensity,
|
|
8180
8592
|
...attribution && Object.keys(attribution).length > 0 && { attribution },
|
|
8181
8593
|
...ids && Object.keys(ids).length > 0 && { ids }
|
|
8182
8594
|
};
|
|
@@ -8248,6 +8660,7 @@ var init_PaywalloInitializer = __esm({
|
|
|
8248
8660
|
init_PaywalloState();
|
|
8249
8661
|
init_queue();
|
|
8250
8662
|
init_NativeStorage();
|
|
8663
|
+
init_detectEnvironment();
|
|
8251
8664
|
}
|
|
8252
8665
|
});
|
|
8253
8666
|
|
|
@@ -8270,6 +8683,7 @@ async function doFullReset(state) {
|
|
|
8270
8683
|
offlineQueue.dispose();
|
|
8271
8684
|
networkMonitor.dispose();
|
|
8272
8685
|
campaignGateService.clearPreloaded();
|
|
8686
|
+
paywallPreloadService.clear();
|
|
8273
8687
|
await notificationsManager.invalidateLocalToken().catch(() => void 0);
|
|
8274
8688
|
notificationsManager.destroy();
|
|
8275
8689
|
deepLinkAttributionCapture.stop();
|
|
@@ -8304,6 +8718,7 @@ var init_PaywalloLifecycle = __esm({
|
|
|
8304
8718
|
init_identity();
|
|
8305
8719
|
init_DeepLinkAttributionCapture();
|
|
8306
8720
|
init_notifications();
|
|
8721
|
+
init_paywall();
|
|
8307
8722
|
init_session();
|
|
8308
8723
|
init_SubscriptionCache();
|
|
8309
8724
|
init_network();
|
|
@@ -8607,6 +9022,15 @@ var init_PaywalloClient = __esm({
|
|
|
8607
9022
|
isPreloaded(placement) {
|
|
8608
9023
|
return campaignGateService.getPreloadedCampaign(placement) !== null;
|
|
8609
9024
|
}
|
|
9025
|
+
async preloadPaywall(placement) {
|
|
9026
|
+
return paywallPreloadService.preload(this.getApiClientOrThrow(), placement);
|
|
9027
|
+
}
|
|
9028
|
+
async preloadPaywalls(placements) {
|
|
9029
|
+
return paywallPreloadService.preloadMany(this.getApiClientOrThrow(), placements);
|
|
9030
|
+
}
|
|
9031
|
+
isPaywallPreloaded(placement) {
|
|
9032
|
+
return paywallPreloadService.isPreloaded(placement);
|
|
9033
|
+
}
|
|
8610
9034
|
getAutoPreloadedPlacement() {
|
|
8611
9035
|
return this.state.autoPreloadedPlacement;
|
|
8612
9036
|
}
|
|
@@ -8705,8 +9129,8 @@ var init_OfferingService = __esm({
|
|
|
8705
9129
|
this.config = config;
|
|
8706
9130
|
}
|
|
8707
9131
|
async getOfferings(ids) {
|
|
8708
|
-
const
|
|
8709
|
-
if (
|
|
9132
|
+
const cached2 = this.getMemCache();
|
|
9133
|
+
if (cached2 && !cached2.isStale) return this.filterByIds(cached2.data, ids);
|
|
8710
9134
|
if (!this.cache) {
|
|
8711
9135
|
try {
|
|
8712
9136
|
const stored = await secureStorage.get(OFFERINGS_CACHE_KEY);
|
|
@@ -8821,7 +9245,7 @@ init_session();
|
|
|
8821
9245
|
init_PaywalloError();
|
|
8822
9246
|
|
|
8823
9247
|
// src/hooks/useOfferingPurchase.ts
|
|
8824
|
-
import { useCallback as useCallback7, useState as
|
|
9248
|
+
import { useCallback as useCallback7, useState as useState7 } from "react";
|
|
8825
9249
|
|
|
8826
9250
|
// src/domains/plan/invalidateCache.ts
|
|
8827
9251
|
async function invalidatePlanCache() {
|
|
@@ -8834,7 +9258,7 @@ async function invalidatePlanCache() {
|
|
|
8834
9258
|
|
|
8835
9259
|
// src/hooks/useOfferingPurchase.ts
|
|
8836
9260
|
function useOfferingPurchase() {
|
|
8837
|
-
const [state, setState] =
|
|
9261
|
+
const [state, setState] = useState7("idle");
|
|
8838
9262
|
const purchase = useCallback7(async (productId) => {
|
|
8839
9263
|
setState("purchasing");
|
|
8840
9264
|
try {
|
|
@@ -8859,7 +9283,7 @@ function useOfferingPurchase() {
|
|
|
8859
9283
|
}
|
|
8860
9284
|
|
|
8861
9285
|
// src/hooks/useOfferings.ts
|
|
8862
|
-
import { useCallback as useCallback8, useEffect as
|
|
9286
|
+
import { useCallback as useCallback8, useEffect as useEffect9, useState as useState8 } from "react";
|
|
8863
9287
|
async function getOfferingService() {
|
|
8864
9288
|
try {
|
|
8865
9289
|
const mod = await Promise.resolve().then(() => (init_OfferingService(), OfferingService_exports));
|
|
@@ -8869,8 +9293,8 @@ async function getOfferingService() {
|
|
|
8869
9293
|
}
|
|
8870
9294
|
}
|
|
8871
9295
|
function useOfferings(ids) {
|
|
8872
|
-
const [offerings, setOfferings] =
|
|
8873
|
-
const [isLoading, setIsLoading] =
|
|
9296
|
+
const [offerings, setOfferings] = useState8([]);
|
|
9297
|
+
const [isLoading, setIsLoading] = useState8(true);
|
|
8874
9298
|
const idsKey = ids?.join(",") ?? "";
|
|
8875
9299
|
const fetchOfferings = useCallback8(async () => {
|
|
8876
9300
|
setIsLoading(true);
|
|
@@ -8888,7 +9312,7 @@ function useOfferings(ids) {
|
|
|
8888
9312
|
setIsLoading(false);
|
|
8889
9313
|
}
|
|
8890
9314
|
}, [idsKey]);
|
|
8891
|
-
|
|
9315
|
+
useEffect9(() => {
|
|
8892
9316
|
void fetchOfferings();
|
|
8893
9317
|
}, [fetchOfferings]);
|
|
8894
9318
|
return { offerings, isLoading };
|
|
@@ -8925,10 +9349,10 @@ function usePaywallo() {
|
|
|
8925
9349
|
}
|
|
8926
9350
|
|
|
8927
9351
|
// src/hooks/usePlanPurchase.ts
|
|
8928
|
-
import { useCallback as useCallback10, useState as
|
|
9352
|
+
import { useCallback as useCallback10, useState as useState9 } from "react";
|
|
8929
9353
|
function usePlanPurchase() {
|
|
8930
|
-
const [state, setState] =
|
|
8931
|
-
const [error, setError] =
|
|
9354
|
+
const [state, setState] = useState9("idle");
|
|
9355
|
+
const [error, setError] = useState9(null);
|
|
8932
9356
|
const purchase = useCallback10(async (productId) => {
|
|
8933
9357
|
setState("purchasing");
|
|
8934
9358
|
setError(null);
|
|
@@ -8981,7 +9405,7 @@ function usePlanPurchase() {
|
|
|
8981
9405
|
}
|
|
8982
9406
|
|
|
8983
9407
|
// src/hooks/usePlans.ts
|
|
8984
|
-
import { useCallback as useCallback11, useEffect as
|
|
9408
|
+
import { useCallback as useCallback11, useEffect as useEffect10, useState as useState10 } from "react";
|
|
8985
9409
|
async function getPlanService() {
|
|
8986
9410
|
try {
|
|
8987
9411
|
const mod = await Promise.resolve().then(() => (init_PlanService(), PlanService_exports));
|
|
@@ -8991,10 +9415,10 @@ async function getPlanService() {
|
|
|
8991
9415
|
}
|
|
8992
9416
|
}
|
|
8993
9417
|
function usePlans() {
|
|
8994
|
-
const [allPlans, setAllPlans] =
|
|
8995
|
-
const [currentPlan, setCurrentPlan] =
|
|
8996
|
-
const [isLoading, setIsLoading] =
|
|
8997
|
-
const [error, setError] =
|
|
9418
|
+
const [allPlans, setAllPlans] = useState10([]);
|
|
9419
|
+
const [currentPlan, setCurrentPlan] = useState10(null);
|
|
9420
|
+
const [isLoading, setIsLoading] = useState10(true);
|
|
9421
|
+
const [error, setError] = useState10(null);
|
|
8998
9422
|
const fetchPlans = useCallback11(async (forceRefresh = false) => {
|
|
8999
9423
|
setIsLoading(true);
|
|
9000
9424
|
setError(null);
|
|
@@ -9019,14 +9443,14 @@ function usePlans() {
|
|
|
9019
9443
|
const refresh = useCallback11(() => {
|
|
9020
9444
|
return fetchPlans(true);
|
|
9021
9445
|
}, [fetchPlans]);
|
|
9022
|
-
|
|
9446
|
+
useEffect10(() => {
|
|
9023
9447
|
void fetchPlans(false);
|
|
9024
9448
|
}, [fetchPlans]);
|
|
9025
9449
|
return { currentPlan, allPlans, isLoading, error, refresh };
|
|
9026
9450
|
}
|
|
9027
9451
|
|
|
9028
9452
|
// src/hooks/useProducts.ts
|
|
9029
|
-
import { useCallback as useCallback12, useEffect as
|
|
9453
|
+
import { useCallback as useCallback12, useEffect as useEffect11, useState as useState11 } from "react";
|
|
9030
9454
|
|
|
9031
9455
|
// src/utils/productMappers.ts
|
|
9032
9456
|
function mapToIAPProduct(product) {
|
|
@@ -9065,10 +9489,10 @@ function mapToFormattedProduct(product) {
|
|
|
9065
9489
|
|
|
9066
9490
|
// src/hooks/useProducts.ts
|
|
9067
9491
|
function useProducts(initialProductIds) {
|
|
9068
|
-
const [products, setProducts] =
|
|
9069
|
-
const [formattedProducts, setFormattedProducts] =
|
|
9070
|
-
const [isLoading, setIsLoading] =
|
|
9071
|
-
const [error, setError] =
|
|
9492
|
+
const [products, setProducts] = useState11([]);
|
|
9493
|
+
const [formattedProducts, setFormattedProducts] = useState11([]);
|
|
9494
|
+
const [isLoading, setIsLoading] = useState11(false);
|
|
9495
|
+
const [error, setError] = useState11(null);
|
|
9072
9496
|
const loadProducts = useCallback12(async (productIds) => {
|
|
9073
9497
|
setIsLoading(true);
|
|
9074
9498
|
setError(null);
|
|
@@ -9083,7 +9507,7 @@ function useProducts(initialProductIds) {
|
|
|
9083
9507
|
setIsLoading(false);
|
|
9084
9508
|
}
|
|
9085
9509
|
}, []);
|
|
9086
|
-
|
|
9510
|
+
useEffect11(() => {
|
|
9087
9511
|
if (initialProductIds && initialProductIds.length > 0) {
|
|
9088
9512
|
void loadProducts(initialProductIds);
|
|
9089
9513
|
}
|
|
@@ -9129,13 +9553,13 @@ function useProducts(initialProductIds) {
|
|
|
9129
9553
|
}
|
|
9130
9554
|
|
|
9131
9555
|
// src/hooks/usePurchase.ts
|
|
9132
|
-
import { useCallback as useCallback13, useState as
|
|
9556
|
+
import { useCallback as useCallback13, useState as useState12 } from "react";
|
|
9133
9557
|
function toIAPPurchase(p) {
|
|
9134
9558
|
return { productId: p.productId, transactionId: p.transactionId, transactionDate: p.transactionDate, transactionReceipt: p.receipt };
|
|
9135
9559
|
}
|
|
9136
9560
|
function usePurchase() {
|
|
9137
|
-
const [state, setState] =
|
|
9138
|
-
const [error, setError] =
|
|
9561
|
+
const [state, setState] = useState12("idle");
|
|
9562
|
+
const [error, setError] = useState12(null);
|
|
9139
9563
|
const purchase = useCallback13(async (productId) => {
|
|
9140
9564
|
setState("purchasing");
|
|
9141
9565
|
setError(null);
|
|
@@ -9183,13 +9607,13 @@ function usePurchase() {
|
|
|
9183
9607
|
}
|
|
9184
9608
|
|
|
9185
9609
|
// src/hooks/useSubscription.ts
|
|
9186
|
-
import { useCallback as useCallback14, useEffect as
|
|
9610
|
+
import { useCallback as useCallback14, useEffect as useEffect12, useState as useState13 } from "react";
|
|
9187
9611
|
init_SubscriptionManager();
|
|
9188
9612
|
var EMPTY_ENTITLEMENTS = [];
|
|
9189
9613
|
function useSubscription() {
|
|
9190
|
-
const [status, setStatus] =
|
|
9191
|
-
const [isLoading, setIsLoading] =
|
|
9192
|
-
const [error, setError] =
|
|
9614
|
+
const [status, setStatus] = useState13(null);
|
|
9615
|
+
const [isLoading, setIsLoading] = useState13(true);
|
|
9616
|
+
const [error, setError] = useState13(null);
|
|
9193
9617
|
const refresh = useCallback14(async () => {
|
|
9194
9618
|
setIsLoading(true);
|
|
9195
9619
|
setError(null);
|
|
@@ -9216,7 +9640,7 @@ function useSubscription() {
|
|
|
9216
9640
|
setIsLoading(false);
|
|
9217
9641
|
}
|
|
9218
9642
|
}, []);
|
|
9219
|
-
|
|
9643
|
+
useEffect12(() => {
|
|
9220
9644
|
void subscriptionManager.getSubscriptionStatus().then((s) => {
|
|
9221
9645
|
setStatus(s);
|
|
9222
9646
|
setIsLoading(false);
|
|
@@ -9244,20 +9668,215 @@ function useSubscription() {
|
|
|
9244
9668
|
}
|
|
9245
9669
|
|
|
9246
9670
|
// src/PaywalloProvider.tsx
|
|
9247
|
-
import { useCallback as useCallback19, useEffect as
|
|
9248
|
-
import { Animated as
|
|
9671
|
+
import { useCallback as useCallback19, useEffect as useEffect14, useLayoutEffect, useMemo as useMemo3, useRef as useRef11, useState as useState17 } from "react";
|
|
9672
|
+
import { Animated as Animated4, Dimensions as Dimensions5, Easing as Easing2, StyleSheet as StyleSheet5 } from "react-native";
|
|
9249
9673
|
init_PaywalloClient();
|
|
9674
|
+
init_NativeStorage();
|
|
9250
9675
|
init_SecureStorage();
|
|
9251
9676
|
init_campaign();
|
|
9252
9677
|
init_paywall();
|
|
9253
9678
|
init_paywall();
|
|
9254
9679
|
init_paywall();
|
|
9255
9680
|
init_paywall();
|
|
9681
|
+
init_paywall();
|
|
9682
|
+
init_paywall();
|
|
9683
|
+
|
|
9684
|
+
// src/domains/paywall/SuperwallAutoBridge.ts
|
|
9685
|
+
init_PaywalloClient();
|
|
9686
|
+
|
|
9687
|
+
// src/core/utils/eventId.ts
|
|
9688
|
+
function hashString(input) {
|
|
9689
|
+
let h1 = 3735928559;
|
|
9690
|
+
let h2 = 1103547991;
|
|
9691
|
+
for (let i = 0; i < input.length; i++) {
|
|
9692
|
+
const ch = input.charCodeAt(i);
|
|
9693
|
+
h1 = Math.imul(h1 ^ ch, 2654435769);
|
|
9694
|
+
h2 = Math.imul(h2 ^ ch, 1598716949);
|
|
9695
|
+
}
|
|
9696
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 73244475);
|
|
9697
|
+
h1 ^= Math.imul(h2 ^ h2 >>> 13, 351452291);
|
|
9698
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 73244475);
|
|
9699
|
+
h2 ^= Math.imul(h1 ^ h1 >>> 13, 351452291);
|
|
9700
|
+
const toHex = (n) => (n >>> 0).toString(16).padStart(8, "0");
|
|
9701
|
+
return toHex(h1) + toHex(h2);
|
|
9702
|
+
}
|
|
9703
|
+
function buildDeterministicEventId(parts) {
|
|
9704
|
+
const raw = hashString(parts.join("|"));
|
|
9705
|
+
const p1 = raw.slice(0, 8);
|
|
9706
|
+
const p2 = raw.slice(8, 12);
|
|
9707
|
+
const p3 = "4" + raw.slice(13, 16);
|
|
9708
|
+
const p4 = (parseInt(raw[16], 16) & 3 | 8).toString(16) + raw.slice(17, 20);
|
|
9709
|
+
const p5 = raw.padEnd(32, "0").slice(20, 32);
|
|
9710
|
+
return `${p1}-${p2}-${p3}-${p4}-${p5}`;
|
|
9711
|
+
}
|
|
9712
|
+
|
|
9713
|
+
// src/domains/paywall/SuperwallAutoBridge.ts
|
|
9714
|
+
var bridgeStarted = false;
|
|
9715
|
+
var unsubscribe = null;
|
|
9716
|
+
var debugEnabled = false;
|
|
9717
|
+
function log(msg, data) {
|
|
9718
|
+
if (!debugEnabled) return;
|
|
9719
|
+
if (data !== void 0) {
|
|
9720
|
+
console.log(`[Paywallo:SuperwallBridge] ${msg}`, data);
|
|
9721
|
+
} else {
|
|
9722
|
+
console.log(`[Paywallo:SuperwallBridge] ${msg}`);
|
|
9723
|
+
}
|
|
9724
|
+
}
|
|
9725
|
+
function minuteBucket(ts) {
|
|
9726
|
+
return String(Math.floor(ts / 6e4));
|
|
9727
|
+
}
|
|
9728
|
+
function buildEventId(type, identifier, ts) {
|
|
9729
|
+
return buildDeterministicEventId(["sw", type, identifier, minuteBucket(ts)]);
|
|
9730
|
+
}
|
|
9731
|
+
async function trackPaywallEvent(type, identifier, ts, extra) {
|
|
9732
|
+
const _eventId = buildEventId(type, identifier, ts);
|
|
9733
|
+
try {
|
|
9734
|
+
const cleanExtra = {};
|
|
9735
|
+
if (extra) {
|
|
9736
|
+
for (const [k, v] of Object.entries(extra)) if (v !== void 0) cleanExtra[k] = v;
|
|
9737
|
+
}
|
|
9738
|
+
await PaywalloClient.track("paywall", {
|
|
9739
|
+
priority: "critical",
|
|
9740
|
+
properties: {
|
|
9741
|
+
type,
|
|
9742
|
+
paywall_id: identifier,
|
|
9743
|
+
...cleanExtra
|
|
9744
|
+
}
|
|
9745
|
+
});
|
|
9746
|
+
} catch (err) {
|
|
9747
|
+
log("track error", { type, identifier, eventId: _eventId, err: String(err) });
|
|
9748
|
+
}
|
|
9749
|
+
}
|
|
9750
|
+
function mapDismissToCloseReason(resultType) {
|
|
9751
|
+
switch (resultType) {
|
|
9752
|
+
case "purchased":
|
|
9753
|
+
return "purchased";
|
|
9754
|
+
case "restored":
|
|
9755
|
+
return "purchased";
|
|
9756
|
+
// restore counts as a purchase outcome for funnel
|
|
9757
|
+
case "declined":
|
|
9758
|
+
return "dismissed";
|
|
9759
|
+
default:
|
|
9760
|
+
return void 0;
|
|
9761
|
+
}
|
|
9762
|
+
}
|
|
9763
|
+
function buildCallbacks() {
|
|
9764
|
+
return {
|
|
9765
|
+
onPaywallPresent: (info) => {
|
|
9766
|
+
const ts = Date.now();
|
|
9767
|
+
log("onPaywallPresent", { identifier: info.identifier });
|
|
9768
|
+
void trackPaywallEvent("viewed", info.identifier, ts, {
|
|
9769
|
+
placement: info.presentedByEventWithName,
|
|
9770
|
+
variant_id: info.experiment?.variant?.id,
|
|
9771
|
+
campaign_id: info.experiment?.id
|
|
9772
|
+
});
|
|
9773
|
+
},
|
|
9774
|
+
onPaywallDismiss: (info, result) => {
|
|
9775
|
+
const ts = Date.now();
|
|
9776
|
+
log("onPaywallDismiss", { identifier: info.identifier, result: result.type });
|
|
9777
|
+
void trackPaywallEvent("closed", info.identifier, ts, {
|
|
9778
|
+
placement: info.presentedByEventWithName,
|
|
9779
|
+
variant_id: info.experiment?.variant?.id,
|
|
9780
|
+
campaign_id: info.experiment?.id,
|
|
9781
|
+
close_reason: mapDismissToCloseReason(result.type),
|
|
9782
|
+
closed_at: new Date(ts).toISOString()
|
|
9783
|
+
});
|
|
9784
|
+
},
|
|
9785
|
+
onPurchase: (params) => {
|
|
9786
|
+
const ts = Date.now();
|
|
9787
|
+
const safe = params !== null && typeof params === "object" ? params : {};
|
|
9788
|
+
const productId = "productId" in safe && typeof safe.productId === "string" ? safe.productId : "unknown";
|
|
9789
|
+
const identifier = "paywallIdentifier" in safe && typeof safe.paywallIdentifier === "string" ? safe.paywallIdentifier : productId;
|
|
9790
|
+
log("onPurchase", { productId, identifier });
|
|
9791
|
+
void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
|
|
9792
|
+
},
|
|
9793
|
+
// Global Superwall event listener — used specifically for `transactionComplete`
|
|
9794
|
+
// which is the only event that exposes the full product (currency/price) +
|
|
9795
|
+
// transaction (storeTransactionId) shape needed to create a server-side
|
|
9796
|
+
// `transactions` row. Other events are skipped (paywallOpen/Dismiss already
|
|
9797
|
+
// handled above with their typed callbacks).
|
|
9798
|
+
onSuperwallEvent: (info) => {
|
|
9799
|
+
const ev = info?.event;
|
|
9800
|
+
if (!ev || ev.event !== "transactionComplete") return;
|
|
9801
|
+
const ts = Date.now();
|
|
9802
|
+
const product = ev.product;
|
|
9803
|
+
const transaction = ev.transaction;
|
|
9804
|
+
if (!product) {
|
|
9805
|
+
log("transactionComplete missing product \u2014 skipping transaction track");
|
|
9806
|
+
return;
|
|
9807
|
+
}
|
|
9808
|
+
const productId = product.productIdentifier ?? product.id ?? "unknown";
|
|
9809
|
+
const currency = product.currencyCode ?? "USD";
|
|
9810
|
+
const price = product.price ?? 0;
|
|
9811
|
+
const transactionId = transaction?.storeTransactionId ?? transaction?.originalTransactionIdentifier ?? `${productId}_${String(ts)}`;
|
|
9812
|
+
const isTrial = product.hasFreeTrial === true || (product.trialPeriodPrice ?? 0) > 0;
|
|
9813
|
+
const paywallIdentifier = ev.paywallInfo?.identifier;
|
|
9814
|
+
log("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
|
|
9815
|
+
void (async () => {
|
|
9816
|
+
try {
|
|
9817
|
+
await PaywalloClient.track("transaction", {
|
|
9818
|
+
priority: "critical",
|
|
9819
|
+
properties: {
|
|
9820
|
+
type: isTrial ? "trial_started" : "completed",
|
|
9821
|
+
// Server's IngestTranslator looks for `transaction_id`/`product_id`
|
|
9822
|
+
// /`amount`/`currency`/`paywall_id` keys (not `tx_id`). PurchaseHandler
|
|
9823
|
+
// then resolves type=trial/new/renewal based on amount being 0 vs >0.
|
|
9824
|
+
transaction_id: transactionId,
|
|
9825
|
+
product_id: productId,
|
|
9826
|
+
amount: price,
|
|
9827
|
+
currency,
|
|
9828
|
+
...paywallIdentifier ? { paywall_id: paywallIdentifier } : {}
|
|
9829
|
+
}
|
|
9830
|
+
});
|
|
9831
|
+
} catch (err) {
|
|
9832
|
+
log("transactionComplete track error", { err: String(err) });
|
|
9833
|
+
}
|
|
9834
|
+
})();
|
|
9835
|
+
}
|
|
9836
|
+
};
|
|
9837
|
+
}
|
|
9838
|
+
function startSuperwallAutoBridge(debug = false) {
|
|
9839
|
+
debugEnabled = debug;
|
|
9840
|
+
if (bridgeStarted) {
|
|
9841
|
+
log("already started, skipping");
|
|
9842
|
+
return;
|
|
9843
|
+
}
|
|
9844
|
+
void detectAndStart();
|
|
9845
|
+
}
|
|
9846
|
+
async function detectAndStart() {
|
|
9847
|
+
let subscribeFn;
|
|
9848
|
+
try {
|
|
9849
|
+
const mod = await import("expo-superwall");
|
|
9850
|
+
if (!mod || typeof mod.subscribeToSuperwallEvents !== "function") {
|
|
9851
|
+
const internal = await import("expo-superwall/build/src/internal/superwallEventBridge");
|
|
9852
|
+
if (typeof internal.subscribeToSuperwallEvents !== "function") {
|
|
9853
|
+
log("expo-superwall subscribeToSuperwallEvents not found \u2014 bridge inactive");
|
|
9854
|
+
return;
|
|
9855
|
+
}
|
|
9856
|
+
subscribeFn = internal.subscribeToSuperwallEvents;
|
|
9857
|
+
} else {
|
|
9858
|
+
subscribeFn = mod.subscribeToSuperwallEvents;
|
|
9859
|
+
}
|
|
9860
|
+
} catch {
|
|
9861
|
+
log("expo-superwall not installed \u2014 bridge inactive");
|
|
9862
|
+
return;
|
|
9863
|
+
}
|
|
9864
|
+
try {
|
|
9865
|
+
const callbacks = buildCallbacks();
|
|
9866
|
+
unsubscribe = subscribeFn(void 0, () => callbacks);
|
|
9867
|
+
bridgeStarted = true;
|
|
9868
|
+
log("bridge started");
|
|
9869
|
+
} catch (err) {
|
|
9870
|
+
log("subscribe failed", { err: String(err) });
|
|
9871
|
+
}
|
|
9872
|
+
}
|
|
9873
|
+
|
|
9874
|
+
// src/PaywalloProvider.tsx
|
|
9256
9875
|
init_session();
|
|
9257
9876
|
init_ClientError();
|
|
9258
9877
|
|
|
9259
9878
|
// src/hooks/useAppAutoEvents.ts
|
|
9260
|
-
import { useEffect as
|
|
9879
|
+
import { useEffect as useEffect13, useRef as useRef8 } from "react";
|
|
9261
9880
|
|
|
9262
9881
|
// src/utils/platformDetection.ts
|
|
9263
9882
|
async function detectPlatform3() {
|
|
@@ -9279,12 +9898,13 @@ async function detectOsVersion() {
|
|
|
9279
9898
|
}
|
|
9280
9899
|
|
|
9281
9900
|
// src/hooks/useAppAutoEvents.ts
|
|
9282
|
-
var FIRST_SEEN_KEY = "@paywallo:
|
|
9901
|
+
var FIRST_SEEN_KEY = "@paywallo:firstSeen_v226";
|
|
9283
9902
|
var DEBOUNCE_MS = 1e3;
|
|
9284
9903
|
function useAppAutoEvents(config) {
|
|
9285
9904
|
const { isInitialized, getAppVersion: getAppVersion2, getSessionId, emitLifecycle, storageGet, storageSet, debug } = config;
|
|
9286
|
-
const didRunRef =
|
|
9287
|
-
|
|
9905
|
+
const didRunRef = useRef8(false);
|
|
9906
|
+
useEffect13(() => {
|
|
9907
|
+
if (debug) console.log("[Paywallo EVENTS] useEffect fired", { isInitialized, didRunRefCurrent: didRunRef.current });
|
|
9288
9908
|
if (!isInitialized || didRunRef.current) return;
|
|
9289
9909
|
const timer = setTimeout(() => {
|
|
9290
9910
|
if (didRunRef.current) return;
|
|
@@ -9294,17 +9914,23 @@ function useAppAutoEvents(config) {
|
|
|
9294
9914
|
const appVersion = getAppVersion2();
|
|
9295
9915
|
const deviceType = await detectPlatform3();
|
|
9296
9916
|
const osVersion = await detectOsVersion();
|
|
9917
|
+
if (debug) console.log("[Paywallo EVENTS] computed device context", { deviceType, osVersion, appVersion, sessionId, hasSessionId: !!sessionId });
|
|
9297
9918
|
try {
|
|
9919
|
+
if (debug) console.log("[Paywallo EVENTS] checking install \u2014 about to read FIRST_SEEN_KEY");
|
|
9298
9920
|
const firstSeen = await storageGet(FIRST_SEEN_KEY);
|
|
9299
9921
|
if (!firstSeen) {
|
|
9300
9922
|
await emitLifecycle({ type: "install", device_type: deviceType, os_version: osVersion, app_version: appVersion, ...sessionId ? { session_id: sessionId } : {} }, "critical");
|
|
9301
9923
|
await storageSet(FIRST_SEEN_KEY, (/* @__PURE__ */ new Date()).toISOString());
|
|
9924
|
+
if (debug) console.log("[Paywallo EVENTS] install event emitted (first time)");
|
|
9302
9925
|
if (debug) console.log("[Paywallo EVENTS] lifecycle install emitted");
|
|
9926
|
+
} else {
|
|
9927
|
+
if (debug) console.log("[Paywallo EVENTS] install skipped \u2014 firstSeen already exists", { firstSeen });
|
|
9303
9928
|
}
|
|
9304
9929
|
} catch (err) {
|
|
9305
9930
|
if (debug) console.log("[Paywallo EVENTS] lifecycle install check failed", err);
|
|
9306
9931
|
}
|
|
9307
9932
|
try {
|
|
9933
|
+
if (debug) console.log("[Paywallo EVENTS] emitting cold_start");
|
|
9308
9934
|
await emitLifecycle({ type: "cold_start", app_version: appVersion, device_type: deviceType, os_version: osVersion, ...sessionId ? { session_id: sessionId } : {} }, "normal");
|
|
9309
9935
|
if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start emitted");
|
|
9310
9936
|
} catch (err) {
|
|
@@ -9319,7 +9945,7 @@ function useAppAutoEvents(config) {
|
|
|
9319
9945
|
// src/hooks/useCampaignPreload.ts
|
|
9320
9946
|
init_PaywalloClient();
|
|
9321
9947
|
init_CampaignError();
|
|
9322
|
-
import { useCallback as useCallback15, useRef as
|
|
9948
|
+
import { useCallback as useCallback15, useRef as useRef9, useState as useState14 } from "react";
|
|
9323
9949
|
|
|
9324
9950
|
// src/utils/loadCampaignProducts.ts
|
|
9325
9951
|
init_IAPService();
|
|
@@ -9388,8 +10014,8 @@ function getCacheEntry(cache, placement) {
|
|
|
9388
10014
|
|
|
9389
10015
|
// src/hooks/useCampaignPreload.ts
|
|
9390
10016
|
function useCampaignPreload() {
|
|
9391
|
-
const [preloadedWebView, setPreloadedWebView] =
|
|
9392
|
-
const preloadedCampaignsRef =
|
|
10017
|
+
const [preloadedWebView, setPreloadedWebView] = useState14(null);
|
|
10018
|
+
const preloadedCampaignsRef = useRef9(/* @__PURE__ */ new Map());
|
|
9393
10019
|
const isPreloadValid = useCallback15(
|
|
9394
10020
|
(placement) => isCacheEntryValid(preloadedCampaignsRef.current.get(placement)),
|
|
9395
10021
|
[]
|
|
@@ -9467,10 +10093,10 @@ function useCampaignPreload() {
|
|
|
9467
10093
|
}
|
|
9468
10094
|
|
|
9469
10095
|
// src/hooks/useProductLoader.ts
|
|
9470
|
-
import { useCallback as useCallback16, useState as
|
|
10096
|
+
import { useCallback as useCallback16, useState as useState15 } from "react";
|
|
9471
10097
|
function useProductLoader() {
|
|
9472
|
-
const [products, setProducts] =
|
|
9473
|
-
const [isLoadingProducts, setIsLoadingProducts] =
|
|
10098
|
+
const [products, setProducts] = useState15(/* @__PURE__ */ new Map());
|
|
10099
|
+
const [isLoadingProducts, setIsLoadingProducts] = useState15(false);
|
|
9474
10100
|
const refreshProducts = useCallback16(async (productIds) => {
|
|
9475
10101
|
setIsLoadingProducts(true);
|
|
9476
10102
|
try {
|
|
@@ -9556,7 +10182,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
9556
10182
|
// src/hooks/useSubscriptionSync.ts
|
|
9557
10183
|
init_PaywalloClient();
|
|
9558
10184
|
init_SubscriptionCache();
|
|
9559
|
-
import { useCallback as useCallback18, useRef as
|
|
10185
|
+
import { useCallback as useCallback18, useRef as useRef10, useState as useState16 } from "react";
|
|
9560
10186
|
|
|
9561
10187
|
// src/hooks/subscriptionSyncHelpers.ts
|
|
9562
10188
|
init_SubscriptionCache();
|
|
@@ -9619,9 +10245,9 @@ function subscriptionInfoToSub(s) {
|
|
|
9619
10245
|
}
|
|
9620
10246
|
async function resolveOfflineFromCache(distinctId) {
|
|
9621
10247
|
try {
|
|
9622
|
-
const
|
|
9623
|
-
if (!
|
|
9624
|
-
const sub =
|
|
10248
|
+
const cached2 = await subscriptionCache.get(distinctId);
|
|
10249
|
+
if (!cached2) return false;
|
|
10250
|
+
const sub = cached2.data.subscription;
|
|
9625
10251
|
return isSubscriptionStillActive(
|
|
9626
10252
|
sub ? { status: sub.status, expiresAt: sub.expiresAt ?? null } : null
|
|
9627
10253
|
);
|
|
@@ -9664,8 +10290,8 @@ async function checkPersistedCache(distinctId, onHit) {
|
|
|
9664
10290
|
|
|
9665
10291
|
// src/hooks/useSubscriptionSync.ts
|
|
9666
10292
|
function useSubscriptionSync() {
|
|
9667
|
-
const [subscription, setSubscription] =
|
|
9668
|
-
const cacheRef =
|
|
10293
|
+
const [subscription, setSubscription] = useState16(null);
|
|
10294
|
+
const cacheRef = useRef10(null);
|
|
9669
10295
|
const setCacheAndState = useCallback18((sub) => {
|
|
9670
10296
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
9671
10297
|
setSubscription(sub);
|
|
@@ -9691,8 +10317,8 @@ function useSubscriptionSync() {
|
|
|
9691
10317
|
if (!apiClient || !distinctId) return false;
|
|
9692
10318
|
const storeKitResult = await checkStoreKitActive(PaywalloClient.getConfig()?.debug);
|
|
9693
10319
|
if (storeKitResult !== null) return storeKitResult;
|
|
9694
|
-
const
|
|
9695
|
-
if (
|
|
10320
|
+
const cached2 = cacheRef.current;
|
|
10321
|
+
if (cached2 && cached2.expiresAt > Date.now()) return isActiveCacheEntry(cached2);
|
|
9696
10322
|
const persistedResult = await checkPersistedCache(distinctId, (sub) => setCacheAndState(sub));
|
|
9697
10323
|
if (persistedResult !== null) return persistedResult;
|
|
9698
10324
|
try {
|
|
@@ -9709,8 +10335,8 @@ function useSubscriptionSync() {
|
|
|
9709
10335
|
const apiClient = PaywalloClient.getApiClient();
|
|
9710
10336
|
const distinctId = PaywalloClient.getDistinctId();
|
|
9711
10337
|
if (!apiClient || !distinctId) return null;
|
|
9712
|
-
const
|
|
9713
|
-
if (
|
|
10338
|
+
const cached2 = cacheRef.current;
|
|
10339
|
+
if (cached2 && cached2.expiresAt > Date.now()) return cached2.data;
|
|
9714
10340
|
try {
|
|
9715
10341
|
return await fetchAndCache();
|
|
9716
10342
|
} catch {
|
|
@@ -9728,22 +10354,22 @@ function useSubscriptionSync() {
|
|
|
9728
10354
|
|
|
9729
10355
|
// src/PaywalloProvider.tsx
|
|
9730
10356
|
init_localization();
|
|
9731
|
-
import { jsx as
|
|
10357
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
9732
10358
|
function PaywalloProvider({ children, config }) {
|
|
9733
|
-
const [isInitialized, setIsInitialized] =
|
|
9734
|
-
const [initError, setInitError] =
|
|
9735
|
-
const [distinctId, setDistinctId] =
|
|
9736
|
-
const [activePaywall, setActivePaywall] =
|
|
10359
|
+
const [isInitialized, setIsInitialized] = useState17(false);
|
|
10360
|
+
const [initError, setInitError] = useState17(null);
|
|
10361
|
+
const [distinctId, setDistinctId] = useState17(null);
|
|
10362
|
+
const [activePaywall, setActivePaywall] = useState17(null);
|
|
9737
10363
|
const { products, isLoadingProducts, refreshProducts } = useProductLoader();
|
|
9738
10364
|
const { subscription, hasActiveSubscription, getSubscription, invalidateCache } = useSubscriptionSync();
|
|
9739
10365
|
const { preloadedWebView, setPreloadedWebView, preloadCampaign, consumePreloadedCampaign, isPreloadValid } = useCampaignPreload();
|
|
9740
10366
|
const clearPreloadedWebView = useCallback19(() => setPreloadedWebView(null), [setPreloadedWebView]);
|
|
9741
|
-
const autoPreloadTriggeredRef =
|
|
9742
|
-
const preloadedWebViewRef =
|
|
9743
|
-
const pollTimerRef =
|
|
9744
|
-
const pollCancelledRef =
|
|
9745
|
-
const secureStorageRef =
|
|
9746
|
-
|
|
10367
|
+
const autoPreloadTriggeredRef = useRef11(false);
|
|
10368
|
+
const preloadedWebViewRef = useRef11(preloadedWebView);
|
|
10369
|
+
const pollTimerRef = useRef11(null);
|
|
10370
|
+
const pollCancelledRef = useRef11(false);
|
|
10371
|
+
const secureStorageRef = useRef11(new SecureStorage(config.debug));
|
|
10372
|
+
useEffect14(() => {
|
|
9747
10373
|
preloadedWebViewRef.current = preloadedWebView;
|
|
9748
10374
|
}, [preloadedWebView]);
|
|
9749
10375
|
useAppAutoEvents({
|
|
@@ -9755,8 +10381,18 @@ function PaywalloProvider({ children, config }) {
|
|
|
9755
10381
|
for (const [k, v] of Object.entries(props)) if (v !== void 0) flat[k] = v;
|
|
9756
10382
|
return PaywalloClient.track("lifecycle", { properties: flat, priority });
|
|
9757
10383
|
},
|
|
9758
|
-
storageGet: (key) =>
|
|
9759
|
-
|
|
10384
|
+
storageGet: async (key) => {
|
|
10385
|
+
if (key === "@paywallo:firstSeen_v226") {
|
|
10386
|
+
return nativeStorage.get(key);
|
|
10387
|
+
}
|
|
10388
|
+
return secureStorageRef.current.get(key);
|
|
10389
|
+
},
|
|
10390
|
+
storageSet: async (key, value) => {
|
|
10391
|
+
if (key === "@paywallo:firstSeen_v226") {
|
|
10392
|
+
return nativeStorage.set(key, value);
|
|
10393
|
+
}
|
|
10394
|
+
return secureStorageRef.current.set(key, value);
|
|
10395
|
+
},
|
|
9760
10396
|
debug: config.debug
|
|
9761
10397
|
});
|
|
9762
10398
|
const triggerRepreload = useCallback19((placement) => {
|
|
@@ -9764,8 +10400,8 @@ function PaywalloProvider({ children, config }) {
|
|
|
9764
10400
|
});
|
|
9765
10401
|
}, []);
|
|
9766
10402
|
const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady: baseHandlePreloadedWebViewReady, handlePreloadedClose: baseHandlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, setPreloadedWebView);
|
|
9767
|
-
const SCREEN_HEIGHT =
|
|
9768
|
-
const slideAnim =
|
|
10403
|
+
const SCREEN_HEIGHT = useMemo3(() => Dimensions5.get("window").height, []);
|
|
10404
|
+
const slideAnim = useRef11(new Animated4.Value(SCREEN_HEIGHT)).current;
|
|
9769
10405
|
const handlePreloadedWebViewReady = useCallback19(() => {
|
|
9770
10406
|
if (PaywalloClient.getConfig()?.debug) {
|
|
9771
10407
|
console.log(`[Paywallo WEBVIEW] loaded \u2014 placement: ${preloadedWebView?.placement}`);
|
|
@@ -9774,7 +10410,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
9774
10410
|
}, [preloadedWebView?.placement, baseHandlePreloadedWebViewReady]);
|
|
9775
10411
|
const handlePreloadedClose = useCallback19(() => {
|
|
9776
10412
|
const placement = preloadedWebView?.placement;
|
|
9777
|
-
|
|
10413
|
+
Animated4.timing(slideAnim, { toValue: SCREEN_HEIGHT, duration: 200, easing: Easing2.in(Easing2.cubic), useNativeDriver: true }).start(() => {
|
|
9778
10414
|
baseHandlePreloadedClose();
|
|
9779
10415
|
if (placement) triggerRepreload(placement);
|
|
9780
10416
|
});
|
|
@@ -9787,7 +10423,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
9787
10423
|
presentedAtRef,
|
|
9788
10424
|
invalidateCache
|
|
9789
10425
|
);
|
|
9790
|
-
const [isPreloadPurchasing, setIsPreloadPurchasing] =
|
|
10426
|
+
const [isPreloadPurchasing, setIsPreloadPurchasing] = useState17(false);
|
|
9791
10427
|
const handlePreloadedPurchase = useCallback19(async (productId) => {
|
|
9792
10428
|
setIsPreloadPurchasing(true);
|
|
9793
10429
|
try {
|
|
@@ -9796,19 +10432,25 @@ function PaywalloProvider({ children, config }) {
|
|
|
9796
10432
|
setIsPreloadPurchasing(false);
|
|
9797
10433
|
}
|
|
9798
10434
|
}, [rawPreloadedPurchase]);
|
|
9799
|
-
const configRef =
|
|
9800
|
-
|
|
10435
|
+
const configRef = useRef11(config);
|
|
10436
|
+
useEffect14(() => {
|
|
9801
10437
|
let mounted = true;
|
|
9802
10438
|
initLocalization({ detectDevice: true });
|
|
9803
10439
|
PaywalloClient.init(configRef.current).then(async () => {
|
|
9804
10440
|
if (!mounted) return;
|
|
9805
10441
|
setDistinctId(PaywalloClient.getDistinctId());
|
|
10442
|
+
startSuperwallAutoBridge(configRef.current.debug);
|
|
9806
10443
|
setIsInitialized(true);
|
|
9807
10444
|
if (!autoPreloadTriggeredRef.current) {
|
|
9808
10445
|
autoPreloadTriggeredRef.current = true;
|
|
9809
10446
|
const placement = await PaywalloClient.waitForAutoPreloadedPlacement();
|
|
9810
10447
|
if (mounted && placement) void preloadCampaign(placement).catch(() => {
|
|
9811
10448
|
});
|
|
10449
|
+
const preloadPlacements = configRef.current.preloadPaywalls;
|
|
10450
|
+
if (mounted && preloadPlacements && preloadPlacements.length > 0) {
|
|
10451
|
+
void PaywalloClient.preloadPaywalls(preloadPlacements).catch(() => {
|
|
10452
|
+
});
|
|
10453
|
+
}
|
|
9812
10454
|
}
|
|
9813
10455
|
}).catch((err) => {
|
|
9814
10456
|
if (!mounted) return;
|
|
@@ -9823,7 +10465,21 @@ function PaywalloProvider({ children, config }) {
|
|
|
9823
10465
|
return api ? api.getPaywall(p) : null;
|
|
9824
10466
|
}, []);
|
|
9825
10467
|
const presentPaywall = useCallback19((placement) => {
|
|
9826
|
-
|
|
10468
|
+
const preloaded = paywallPreloadService.getPreloaded(placement);
|
|
10469
|
+
return new Promise((resolve) => setActivePaywall({
|
|
10470
|
+
placement,
|
|
10471
|
+
resolver: resolve,
|
|
10472
|
+
...preloaded && {
|
|
10473
|
+
paywallConfig: {
|
|
10474
|
+
id: preloaded.config.id,
|
|
10475
|
+
placement: preloaded.config.placement,
|
|
10476
|
+
config: preloaded.config.config,
|
|
10477
|
+
primaryProductId: preloaded.config.primaryProductId,
|
|
10478
|
+
secondaryProductId: preloaded.config.secondaryProductId
|
|
10479
|
+
},
|
|
10480
|
+
preloadedProducts: preloaded.products
|
|
10481
|
+
}
|
|
10482
|
+
}));
|
|
9827
10483
|
}, []);
|
|
9828
10484
|
const trackCampaignImpression = useCallback19((placement, variantKey, campaignId) => {
|
|
9829
10485
|
const api = PaywalloClient.getApiClient();
|
|
@@ -9957,7 +10613,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
9957
10613
|
}
|
|
9958
10614
|
return presentCampaign(placement);
|
|
9959
10615
|
}, [presentPreloadedWebView, presentCampaign]);
|
|
9960
|
-
|
|
10616
|
+
useEffect14(() => {
|
|
9961
10617
|
if (!isInitialized) return;
|
|
9962
10618
|
const onEmergency = async (id) => {
|
|
9963
10619
|
try {
|
|
@@ -9986,7 +10642,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
9986
10642
|
const isReady = isInitialized && !isLoadingProducts;
|
|
9987
10643
|
const noOp = useCallback19(() => {
|
|
9988
10644
|
}, []);
|
|
9989
|
-
const contextValue =
|
|
10645
|
+
const contextValue = useMemo3(() => ({
|
|
9990
10646
|
config: PaywalloClient.getConfig(),
|
|
9991
10647
|
isInitialized,
|
|
9992
10648
|
isReady,
|
|
@@ -10023,17 +10679,17 @@ function PaywalloProvider({ children, config }) {
|
|
|
10023
10679
|
useLayoutEffect(() => {
|
|
10024
10680
|
if (showingPreloadedWebView) {
|
|
10025
10681
|
slideAnim.setValue(SCREEN_HEIGHT);
|
|
10026
|
-
|
|
10682
|
+
Animated4.timing(slideAnim, { toValue: 0, duration: 200, easing: Easing2.out(Easing2.cubic), useNativeDriver: true }).start();
|
|
10027
10683
|
}
|
|
10028
10684
|
}, [showingPreloadedWebView, slideAnim, SCREEN_HEIGHT]);
|
|
10029
10685
|
const preloadStyle = [
|
|
10030
|
-
|
|
10031
|
-
showingPreloadedWebView ?
|
|
10686
|
+
styles5.preloadOverlay,
|
|
10687
|
+
showingPreloadedWebView ? styles5.visible : styles5.hidden,
|
|
10032
10688
|
showingPreloadedWebView ? { transform: [{ translateY: slideAnim }] } : void 0
|
|
10033
10689
|
];
|
|
10034
|
-
return /* @__PURE__ */
|
|
10690
|
+
return /* @__PURE__ */ jsxs3(PaywalloContext.Provider, { value: contextValue, children: [
|
|
10035
10691
|
children,
|
|
10036
|
-
/* @__PURE__ */
|
|
10692
|
+
/* @__PURE__ */ jsx5(PaywallErrorBoundary, { children: !!preloadedWebView?.craftData && /* @__PURE__ */ jsx5(Animated4.View, { style: preloadStyle, pointerEvents: showingPreloadedWebView ? "auto" : "none", children: /* @__PURE__ */ jsx5(
|
|
10037
10693
|
PaywallWebView,
|
|
10038
10694
|
{
|
|
10039
10695
|
paywallId: preloadedWebView.paywallId,
|
|
@@ -10049,7 +10705,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10049
10705
|
onRestore: showingPreloadedWebView ? handlePreloadedRestore : noOp
|
|
10050
10706
|
}
|
|
10051
10707
|
) }) }),
|
|
10052
|
-
/* @__PURE__ */
|
|
10708
|
+
/* @__PURE__ */ jsx5(PaywallErrorBoundary, { children: activePaywall && /* @__PURE__ */ jsx5(
|
|
10053
10709
|
PaywallModal,
|
|
10054
10710
|
{
|
|
10055
10711
|
placement: activePaywall.placement,
|
|
@@ -10061,10 +10717,11 @@ function PaywalloProvider({ children, config }) {
|
|
|
10061
10717
|
campaignId: activePaywall.campaignId,
|
|
10062
10718
|
variantId: activePaywall.variantId
|
|
10063
10719
|
}
|
|
10064
|
-
) })
|
|
10720
|
+
) }),
|
|
10721
|
+
isInitialized && (config.preloadPaywalls?.length ?? 0) > 0 && PaywalloClient.getWebUrl() !== null && /* @__PURE__ */ jsx5(PaywallWebViewPrewarmer, { webUrl: PaywalloClient.getWebUrl() })
|
|
10065
10722
|
] });
|
|
10066
10723
|
}
|
|
10067
|
-
var
|
|
10724
|
+
var styles5 = StyleSheet5.create({
|
|
10068
10725
|
preloadOverlay: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#ffffff" },
|
|
10069
10726
|
hidden: { zIndex: -1, opacity: 0, width: 1, height: 1, overflow: "hidden" },
|
|
10070
10727
|
visible: { zIndex: 9999, opacity: 1 }
|