react-native-revnix 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/android/build.gradle +3 -0
- package/android/consumer-rules.pro +1 -0
- package/android/src/main/kotlin/com/revnix/reactnative/RevnixStoreModule.kt +43 -0
- package/dist/blocks/locale.js +7 -0
- package/dist/blocks/selection.d.ts +7 -1
- package/dist/blocks/selection.js +9 -0
- package/dist/blocks/tags.d.ts +3 -0
- package/dist/blocks/tags.js +33 -0
- package/dist/blocks/types.d.ts +45 -3
- package/dist/blocks/types.js +1 -0
- package/dist/client.d.ts +50 -11
- package/dist/client.js +408 -119
- package/dist/esm/blocks/locale.js +7 -0
- package/dist/esm/blocks/selection.js +8 -0
- package/dist/esm/blocks/tags.js +31 -0
- package/dist/esm/blocks/types.js +1 -0
- package/dist/esm/client.js +407 -119
- package/dist/esm/store/index.js +7 -0
- package/dist/esm/ui/RevnixBlockPaywall.js +103 -22
- package/dist/esm/ui/RevnixPaywall.js +7 -0
- package/dist/esm/ui/index.js +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/store/index.d.ts +8 -0
- package/dist/store/index.js +10 -1
- package/dist/types.d.ts +32 -0
- package/dist/ui/RevnixBlockPaywall.js +100 -19
- package/dist/ui/RevnixPaywall.d.ts +6 -2
- package/dist/ui/RevnixPaywall.js +8 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +2 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/ios/RevnixStore.m +4 -0
- package/ios/RevnixStore.swift +29 -0
- package/package.json +1 -1
- package/src/blocks/locale.ts +7 -0
- package/src/blocks/selection.ts +13 -0
- package/src/blocks/tags.ts +38 -0
- package/src/blocks/types.ts +51 -2
- package/src/client.ts +478 -154
- package/src/index.ts +1 -0
- package/src/store/index.ts +15 -0
- package/src/types.ts +30 -0
- package/src/ui/RevnixBlockPaywall.tsx +186 -20
- package/src/ui/RevnixPaywall.tsx +15 -1
- package/src/ui/index.ts +1 -0
- package/src/version.ts +1 -1
package/dist/esm/store/index.js
CHANGED
|
@@ -18,5 +18,12 @@ export const RevnixStore = native
|
|
|
18
18
|
finish: missing,
|
|
19
19
|
observe: () => () => { },
|
|
20
20
|
};
|
|
21
|
+
/** Pass as `deviceIntegrity` in the Revnix config: App Attest on iOS, Play
|
|
22
|
+
* Integrity on Android, via the RevnixStore native module. Resolves null
|
|
23
|
+
* where the device or build cannot produce evidence. */
|
|
24
|
+
export const nativeDeviceIntegrity = (customerId) => native?.deviceIntegrity?.(customerId) ?? Promise.resolve(null);
|
|
25
|
+
/** Pass as `advertisingId` in the Revnix config: the Google Advertising ID
|
|
26
|
+
* on Android, via the RevnixStore native module. Resolves null on iOS. */
|
|
27
|
+
export const nativeAdvertisingId = () => native?.advertisingId?.() ?? Promise.resolve(null);
|
|
21
28
|
if (native)
|
|
22
29
|
setStoreAdapter(RevnixStore);
|
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
// RevnixPaywall delegates here whenever a config carries `blocks`, so apps
|
|
18
18
|
// get designed paywalls without changing a line of their integration.
|
|
19
19
|
import * as React from "react";
|
|
20
|
-
import { ActivityIndicator, Image, Linking, Platform, Pressable, ScrollView, StatusBar, Text, View, } from "react-native";
|
|
20
|
+
import { ActivityIndicator, Image, Linking, Platform, Pressable, ScrollView, StatusBar, Switch, Text, View, } from "react-native";
|
|
21
21
|
import { gradientFallbackColor, isGradient, parseGradient, resolveColor, surfaceSolid, withAlpha, } from "../blocks/color.js";
|
|
22
22
|
import { DEFAULT_FONT_SIZE, hairlineColor, inherit, inheritedTextStyle, toTextStyle, toViewStyle, } from "../blocks/style.js";
|
|
23
23
|
import { backgroundLayers, coverPlacement, } from "../blocks/background.js";
|
|
24
24
|
import { detectLocale, linkLabels, localizeBlockDoc } from "../blocks/locale.js";
|
|
25
|
-
import { resolveTags } from "../blocks/tags.js";
|
|
26
|
-
import { canvasMetrics, effectiveStyle, isVisible, packageContext, selectedPackageId, } from "../blocks/selection.js";
|
|
25
|
+
import { resolveTags, timerText } from "../blocks/tags.js";
|
|
26
|
+
import { canvasMetrics, effectiveStyle, isVisible, packageContext, selectedPackageId, toggleSelection, } from "../blocks/selection.js";
|
|
27
27
|
import { DESIGN_CANVAS, hasCloseAction, } from "../blocks/types.js";
|
|
28
28
|
/** CSS angle (0 = up, 90 = right) → RN gradient start/end points. */
|
|
29
29
|
function angleToPoints(angle) {
|
|
@@ -259,6 +259,56 @@ function BlockText({ text, style, ctx, inherited, onPress, }) {
|
|
|
259
259
|
}
|
|
260
260
|
return React.createElement(View, { style: toViewStyle(style, styleCtx) }, body);
|
|
261
261
|
}
|
|
262
|
+
function Timer({ block, style, ctx, inherited, }) {
|
|
263
|
+
const [now, setNow] = React.useState(Date.now);
|
|
264
|
+
React.useEffect(() => {
|
|
265
|
+
const id = setInterval(() => setNow(Date.now()), 1000);
|
|
266
|
+
return () => clearInterval(id);
|
|
267
|
+
}, []);
|
|
268
|
+
const text = timerText(block, ctx.shownAt, now);
|
|
269
|
+
if (text === null)
|
|
270
|
+
return null;
|
|
271
|
+
return React.createElement(BlockText, { text: text, style: style, ctx: ctx, inherited: inherited });
|
|
272
|
+
}
|
|
273
|
+
function Carousel({ pages, autoAdvance, doc, }) {
|
|
274
|
+
const scroller = React.useRef(null);
|
|
275
|
+
const [size, setSize] = React.useState({ width: 0, height: 0 });
|
|
276
|
+
const [page, setPage] = React.useState(0);
|
|
277
|
+
React.useEffect(() => {
|
|
278
|
+
if (!autoAdvance || size.width === 0 || pages.length < 2)
|
|
279
|
+
return;
|
|
280
|
+
const id = setInterval(() => {
|
|
281
|
+
scroller.current?.scrollTo({
|
|
282
|
+
x: ((page + 1) % pages.length) * size.width,
|
|
283
|
+
animated: true,
|
|
284
|
+
});
|
|
285
|
+
}, autoAdvance * 1000);
|
|
286
|
+
return () => clearInterval(id);
|
|
287
|
+
}, [autoAdvance, size.width, page, pages.length]);
|
|
288
|
+
return (React.createElement(View, { style: { flex: 1 }, onLayout: (e) => setSize({
|
|
289
|
+
width: e.nativeEvent.layout.width,
|
|
290
|
+
height: e.nativeEvent.layout.height,
|
|
291
|
+
}) },
|
|
292
|
+
React.createElement(ScrollView, { ref: scroller, horizontal: true, pagingEnabled: true, showsHorizontalScrollIndicator: false, scrollEventThrottle: 16, onScroll: (e) => {
|
|
293
|
+
if (size.width > 0) {
|
|
294
|
+
setPage(Math.round(e.nativeEvent.contentOffset.x / size.width));
|
|
295
|
+
}
|
|
296
|
+
} }, pages.map((node, i) => (React.createElement(View, { key: i, style: size }, node)))),
|
|
297
|
+
pages.length > 1 ? (React.createElement(View, { pointerEvents: "none", style: {
|
|
298
|
+
position: "absolute",
|
|
299
|
+
bottom: 8,
|
|
300
|
+
left: 0,
|
|
301
|
+
right: 0,
|
|
302
|
+
flexDirection: "row",
|
|
303
|
+
justifyContent: "center",
|
|
304
|
+
gap: 6,
|
|
305
|
+
} }, pages.map((_, i) => (React.createElement(View, { key: i, style: {
|
|
306
|
+
width: 6,
|
|
307
|
+
height: 6,
|
|
308
|
+
borderRadius: 3,
|
|
309
|
+
backgroundColor: i === page ? doc.accent : withAlpha(doc.textColor, 0.3),
|
|
310
|
+
} }))))) : null));
|
|
311
|
+
}
|
|
262
312
|
/** The press handler for a block the design marks as the close, or undefined
|
|
263
313
|
* when it is not one (or the host wired no dismissal). One helper so text,
|
|
264
314
|
* image and button blocks cannot drift on what "action: close" means. */
|
|
@@ -363,6 +413,11 @@ function gridTracks(block) {
|
|
|
363
413
|
}
|
|
364
414
|
function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
365
415
|
const { doc } = ctx;
|
|
416
|
+
React.useEffect(() => {
|
|
417
|
+
if (block.type === "video") {
|
|
418
|
+
ctx.onDiagnostic?.("video drawn as its poster: no built-in player");
|
|
419
|
+
}
|
|
420
|
+
}, [block.type]);
|
|
366
421
|
const styleCtx = { ...ctx.style, fontSize: inherited.fontSize };
|
|
367
422
|
// A pinned card judges its own context; every other block inherits the
|
|
368
423
|
// nearest plan card's. (A repeat card is judged per instance below.)
|
|
@@ -385,9 +440,27 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
385
440
|
switch (block.type) {
|
|
386
441
|
case "text":
|
|
387
442
|
return (React.createElement(BlockText, { text: resolveTags(block.text, pkg, ctx.packages), style: style, ctx: ctx, inherited: inherited, onPress: closePress(block, ctx) }));
|
|
388
|
-
case "
|
|
389
|
-
|
|
390
|
-
|
|
443
|
+
case "timer":
|
|
444
|
+
return React.createElement(Timer, { block: block, style: style, ctx: ctx, inherited: inherited });
|
|
445
|
+
case "toggle": {
|
|
446
|
+
const toggle = toggleSelection(block, ctx.packages, ctx.selectedId);
|
|
447
|
+
if (!toggle)
|
|
448
|
+
return null;
|
|
449
|
+
const flip = () => ctx.onSelect(toggle.target);
|
|
450
|
+
return (React.createElement(Pressable, { accessibilityRole: "switch", accessibilityState: { checked: toggle.on }, onPress: flip, style: [{ flexDirection: "row", alignItems: "center", gap: 12 }, view] },
|
|
451
|
+
React.createElement(BlockFill, { block: block, ctx: ctx }),
|
|
452
|
+
React.createElement(Text, { style: [
|
|
453
|
+
{ flex: 1 },
|
|
454
|
+
inheritedTextStyle(text, ctx.style),
|
|
455
|
+
textOnly(style, styleCtx),
|
|
456
|
+
] }, resolveTags(block.label, ctx.packages.find((p) => p.packageId === ctx.selectedId), ctx.packages)),
|
|
457
|
+
React.createElement(Switch, { value: toggle.on, onValueChange: flip, trackColor: { true: doc.accent } })));
|
|
458
|
+
}
|
|
459
|
+
case "image":
|
|
460
|
+
case "video": {
|
|
461
|
+
const image = block.type === "image";
|
|
462
|
+
const url = image ? block.url || ctx.heroImageUrl : block.poster;
|
|
463
|
+
const round = image && block.shape === "circle";
|
|
391
464
|
const s = style;
|
|
392
465
|
// A converted design sizes its own slot; the 160pt default is only for
|
|
393
466
|
// a slot dropped into a flow column, and must not fight it.
|
|
@@ -400,7 +473,7 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
400
473
|
// An icon the design marks as the close (REV-252). Same rule as text:
|
|
401
474
|
// the whole box is the target, and the style lands on the Pressable so
|
|
402
475
|
// a pinned icon keeps its offsets.
|
|
403
|
-
const closeImage = closePress(block, ctx);
|
|
476
|
+
const closeImage = image ? closePress(block, ctx) : undefined;
|
|
404
477
|
const ImageBox = closeImage ? Pressable : View;
|
|
405
478
|
return (React.createElement(ImageBox, { accessibilityRole: closeImage ? "button" : undefined, accessibilityLabel: closeImage ? "Close" : undefined, hitSlop: closeImage ? 8 : undefined, onPress: closeImage, style: [
|
|
406
479
|
sized ? null : { height: 160 },
|
|
@@ -408,11 +481,11 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
408
481
|
view,
|
|
409
482
|
] },
|
|
410
483
|
React.createElement(BlockFill, { block: block, ctx: ctx }),
|
|
411
|
-
url ? (React.createElement(Image, { source: { uri: url }, resizeMode: block.fit ?? "cover", style: { width: "100%", height: "100%" } })) : (React.createElement(View, { style: {
|
|
484
|
+
url ? (React.createElement(Image, { source: { uri: url }, resizeMode: block.fit ?? "cover", style: { width: "100%", height: "100%" } })) : image ? (React.createElement(View, { style: {
|
|
412
485
|
width: "100%",
|
|
413
486
|
height: "100%",
|
|
414
487
|
backgroundColor: withAlpha("#7d879b", 0.2),
|
|
415
|
-
} }))));
|
|
488
|
+
} })) : null));
|
|
416
489
|
}
|
|
417
490
|
case "list":
|
|
418
491
|
return (React.createElement(View, { style: [{ flexDirection: "column", gap: style?.gap ?? 8 }, view] },
|
|
@@ -521,20 +594,23 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
521
594
|
return (React.createElement(View, { style: [block.flex ? { flexGrow: 1 } : { height: 16 }, view] }));
|
|
522
595
|
case "card": {
|
|
523
596
|
const layout = block.layout ?? "column";
|
|
597
|
+
const carousel = layout === "carousel";
|
|
524
598
|
const base = layout === "stack"
|
|
525
599
|
? { position: "relative" }
|
|
526
|
-
:
|
|
527
|
-
? {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
600
|
+
: carousel
|
|
601
|
+
? { position: "relative", overflow: "hidden" }
|
|
602
|
+
: layout === "grid"
|
|
603
|
+
? {
|
|
604
|
+
position: "relative",
|
|
605
|
+
flexDirection: "row",
|
|
606
|
+
flexWrap: "wrap",
|
|
607
|
+
gap: style?.gap ?? 10,
|
|
608
|
+
}
|
|
609
|
+
: {
|
|
610
|
+
position: "relative",
|
|
611
|
+
flexDirection: layout === "row" ? "row" : "column",
|
|
612
|
+
gap: style?.gap ?? 10,
|
|
613
|
+
};
|
|
538
614
|
const tracks = layout === "grid" ? gridTracks(block) : null;
|
|
539
615
|
const renderChildren = (context, childSelection, childText = text) => block.children.map((child, i) => {
|
|
540
616
|
const node = (React.createElement(Block, { key: child.id, block: child, ctx: ctx, pkg: context, inherited: childText, selection: childSelection }));
|
|
@@ -578,6 +654,9 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
578
654
|
base,
|
|
579
655
|
layout === "stack" ? null : { borderRadius: 16 },
|
|
580
656
|
view,
|
|
657
|
+
carousel
|
|
658
|
+
? { height: typeof style?.height === "number" ? style.height : 220 }
|
|
659
|
+
: null,
|
|
581
660
|
];
|
|
582
661
|
const cardPackage = block.packageIndex !== undefined
|
|
583
662
|
? (ctx.packages[block.packageIndex] ?? pkg)
|
|
@@ -586,7 +665,7 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
586
665
|
// how hand-styled plan rows (a highlighted annual beside a plain
|
|
587
666
|
// monthly) become tappable without a products block.
|
|
588
667
|
const pinned = block.packageIndex !== undefined && cardPackage !== undefined;
|
|
589
|
-
return (React.createElement(CardBox, { style: boxStyle, fill: style?.fill, doc: doc, gradient: ctx.gradient, radius: style?.radius ?? (layout === "stack" ? undefined : 16), onPress: pinned ? () => ctx.onSelect(cardPackage.packageId) : undefined }, renderChildren(cardPackage, context)));
|
|
668
|
+
return (React.createElement(CardBox, { style: boxStyle, fill: style?.fill, doc: doc, gradient: ctx.gradient, radius: style?.radius ?? (layout === "stack" ? undefined : 16), onPress: pinned ? () => ctx.onSelect(cardPackage.packageId) : undefined }, carousel ? (React.createElement(Carousel, { pages: renderChildren(cardPackage, context), autoAdvance: block.autoAdvance, doc: doc })) : (renderChildren(cardPackage, context))));
|
|
590
669
|
}
|
|
591
670
|
}
|
|
592
671
|
}
|
|
@@ -598,6 +677,7 @@ export function RevnixBlockPaywall(props) {
|
|
|
598
677
|
// re-runs on every selection tap.
|
|
599
678
|
const doc = React.useMemo(() => localizeBlockDoc(props.doc, props.locale ?? detectLocale()), [props.doc, props.locale]);
|
|
600
679
|
const [internalSelected, setInternalSelected] = React.useState(undefined);
|
|
680
|
+
const [shownAt] = React.useState(Date.now);
|
|
601
681
|
// Contract §1: host → own tap → design highlight → first package, each
|
|
602
682
|
// only when the offering carries it.
|
|
603
683
|
const selectedId = selectedPackageId(packages, props.selectedPackageId, internalSelected, props.highlightPackageId);
|
|
@@ -639,6 +719,7 @@ export function RevnixBlockPaywall(props) {
|
|
|
639
719
|
onDiagnostic: props.onDiagnostic,
|
|
640
720
|
style: styleCtx,
|
|
641
721
|
loading: props.loading === true,
|
|
722
|
+
shownAt,
|
|
642
723
|
};
|
|
643
724
|
const children = doc.blocks.map((block) => (React.createElement(Block, { key: block.id, block: block, ctx: ctx, pkg: packages.find((p) => p.packageId === selectedId) ?? packages[0], inherited: rootText })));
|
|
644
725
|
// REV-252: the renderer's own close, drawn only for a design that authors
|
|
@@ -18,6 +18,7 @@ import * as React from "react";
|
|
|
18
18
|
import { ActivityIndicator, Alert, Image, Linking, Pressable, ScrollView, StyleSheet, Text, View, } from "react-native";
|
|
19
19
|
import { PREVIEW_PLACEMENT_KEY } from "../implicit.js";
|
|
20
20
|
import { getStoreAdapter } from "../store.js";
|
|
21
|
+
import { paywallImageUrls } from "../client.js";
|
|
21
22
|
import { withAlpha } from "../blocks/color.js";
|
|
22
23
|
import { RevnixBlockPaywall, } from "./RevnixBlockPaywall.js";
|
|
23
24
|
/** `config.blocks` is opaque on the core config (the non-UI entrypoint must
|
|
@@ -66,6 +67,12 @@ const CURRENCY_SYMBOL = /[$€£¥₹₩₽₺₫₪฿₴₦₱]/;
|
|
|
66
67
|
// part of the public theme — derived from the config's mode, in lockstep
|
|
67
68
|
// with the dashboard preview's SCREEN_PALETTES.card.
|
|
68
69
|
const CARD_BG = { dark: "#181b22", light: "#f4f5f7" };
|
|
70
|
+
/** Warms React Native's image cache with every image `resolution`'s paywall
|
|
71
|
+
* loads, so it paints without waiting on the network. Pair it with
|
|
72
|
+
* `client.preload` / `resolvePlacement`. Never throws. */
|
|
73
|
+
export async function prefetchPaywallImages(resolution) {
|
|
74
|
+
await Promise.all(paywallImageUrls(resolution?.paywall).map((url) => Image.prefetch(url).catch(() => false)));
|
|
75
|
+
}
|
|
69
76
|
export function RevnixPaywall(props) {
|
|
70
77
|
// One view per mount: remounting (a re-shown paywall) is a genuine new
|
|
71
78
|
// display; re-renders are not.
|
package/dist/esm/ui/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { RevnixPaywall, DEFAULT_PAYWALL_THEME, LIGHT_PAYWALL_THEME, } from "./RevnixPaywall.js";
|
|
1
|
+
export { RevnixPaywall, DEFAULT_PAYWALL_THEME, LIGHT_PAYWALL_THEME, prefetchPaywallImages, } from "./RevnixPaywall.js";
|
|
2
2
|
export { RevnixBlockPaywall, } from "./RevnixBlockPaywall.js";
|
|
3
3
|
export { DESIGN_CANVAS, } from "../blocks/types.js";
|
|
4
4
|
export { backgroundBaseColor, backgroundLayers, coverPlacement, } from "../blocks/background.js";
|
package/dist/esm/version.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// L3: the single source of the SDK version inside src/. Must match
|
|
2
2
|
// package.json "version" — version.test.ts fails the build when they drift
|
|
3
3
|
// (they had: client.ts said 0.7.0 while the package shipped 0.9.0).
|
|
4
|
-
export const SDK_VERSION = "1.
|
|
4
|
+
export const SDK_VERSION = "1.7.0";
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export { memoryStorage, generateAnonymousId } from "./storage.js";
|
|
|
9
9
|
export { SDK_VERSION } from "./version.js";
|
|
10
10
|
export { DEVICE_HEADER, encodeDeviceHeader } from "./device.js";
|
|
11
11
|
export { browserLifecycle, DEFAULT_SESSION_TIMEOUT_MS, HOST_CALL_PLACEMENT, IMPLICIT_PLACEMENTS, PREVIEW_PLACEMENT_KEY, type ImplicitPlacement, type ImplicitSkipReason, type ImplicitTrigger, type ImplicitTriggerResponse, type RevnixLifecycle, } from "./implicit.js";
|
|
12
|
-
export type { AdRevenue, AliasResult, CustomerEntitlements, DeviceFacts, Entitlement, EntitlementSource, Environment, IdentifyResult, IdentifyStrategy, IdentityInfo, ImplicitResolution, LastDeepLink, MmpAttribution, MmpProvider, PaywallConfig, PaywallInteraction, PaywallPurchaseOutcome, PlacementOffering, PlacementPackage, PlacementPaywall, PlacementProduct, PlacementResolution, RegisterPurchaseInput, RegisterPurchaseResult, RevnixAttribution, RevnixConfig, RevnixDiagnostic, RevnixStorage, Store, } from "./types.js";
|
|
12
|
+
export type { AdRevenue, AliasResult, CustomerEntitlements, DeviceFacts, Entitlement, EntitlementSource, Environment, IdentifyResult, IdentifyStrategy, IdentityInfo, ImplicitResolution, IntegrityEvidence, LastDeepLink, MmpAttribution, MmpProvider, PaywallConfig, PaywallInteraction, PaywallPurchaseOutcome, PlacementOffering, PlacementPackage, PlacementPaywall, PlacementProduct, PlacementResolution, RegisterPurchaseInput, RegisterPurchaseResult, RevnixAttribution, RevnixConfig, RevnixDiagnostic, RevnixStorage, Store, } from "./types.js";
|
|
13
13
|
/** Singleton facade — configure once at app start, then import `Revnix`
|
|
14
14
|
* anywhere. Prefer constructing RevnixClient directly in tests. */
|
|
15
15
|
export declare const Revnix: {
|
package/dist/store/index.d.ts
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
1
|
import { type StoreAdapter } from "../store.js";
|
|
2
|
+
import type { RevnixConfig } from "../types.js";
|
|
2
3
|
export declare const RevnixStore: StoreAdapter;
|
|
4
|
+
/** Pass as `deviceIntegrity` in the Revnix config: App Attest on iOS, Play
|
|
5
|
+
* Integrity on Android, via the RevnixStore native module. Resolves null
|
|
6
|
+
* where the device or build cannot produce evidence. */
|
|
7
|
+
export declare const nativeDeviceIntegrity: NonNullable<RevnixConfig["deviceIntegrity"]>;
|
|
8
|
+
/** Pass as `advertisingId` in the Revnix config: the Google Advertising ID
|
|
9
|
+
* on Android, via the RevnixStore native module. Resolves null on iOS. */
|
|
10
|
+
export declare const nativeAdvertisingId: NonNullable<RevnixConfig["advertisingId"]>;
|
package/dist/store/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RevnixStore = void 0;
|
|
3
|
+
exports.nativeAdvertisingId = exports.nativeDeviceIntegrity = exports.RevnixStore = void 0;
|
|
4
4
|
const react_native_1 = require("react-native");
|
|
5
5
|
const store_js_1 = require("../store.js");
|
|
6
6
|
const native = react_native_1.NativeModules.RevnixStore;
|
|
@@ -21,5 +21,14 @@ exports.RevnixStore = native
|
|
|
21
21
|
finish: missing,
|
|
22
22
|
observe: () => () => { },
|
|
23
23
|
};
|
|
24
|
+
/** Pass as `deviceIntegrity` in the Revnix config: App Attest on iOS, Play
|
|
25
|
+
* Integrity on Android, via the RevnixStore native module. Resolves null
|
|
26
|
+
* where the device or build cannot produce evidence. */
|
|
27
|
+
const nativeDeviceIntegrity = (customerId) => native?.deviceIntegrity?.(customerId) ?? Promise.resolve(null);
|
|
28
|
+
exports.nativeDeviceIntegrity = nativeDeviceIntegrity;
|
|
29
|
+
/** Pass as `advertisingId` in the Revnix config: the Google Advertising ID
|
|
30
|
+
* on Android, via the RevnixStore native module. Resolves null on iOS. */
|
|
31
|
+
const nativeAdvertisingId = () => native?.advertisingId?.() ?? Promise.resolve(null);
|
|
32
|
+
exports.nativeAdvertisingId = nativeAdvertisingId;
|
|
24
33
|
if (native)
|
|
25
34
|
(0, store_js_1.setStoreAdapter)(exports.RevnixStore);
|
package/dist/types.d.ts
CHANGED
|
@@ -129,7 +129,39 @@ export interface RevnixConfig {
|
|
|
129
129
|
* Never throws into your app: an exception from this handler is reported
|
|
130
130
|
* through `onDiagnostic` and swallowed. */
|
|
131
131
|
onAttribution?: (attribution: RevnixAttribution) => void;
|
|
132
|
+
/** Opt-in device integrity evidence (Apple App Attest / Google Play
|
|
133
|
+
* Integrity) attached to every install report and `setAttribution` call.
|
|
134
|
+
* Pair it with the dashboard's "Require device integrity" setting, which
|
|
135
|
+
* strips attribution from installs that arrive without evidence. In React
|
|
136
|
+
* Native pass `nativeDeviceIntegrity` from "react-native-revnix/store";
|
|
137
|
+
* it needs iOS 14+ with App Attest, or Google Play services on Android.
|
|
138
|
+
* Simulators and emulators return nothing.
|
|
139
|
+
*
|
|
140
|
+
* Called at most once per customer id and bounded to 10s. A rejection,
|
|
141
|
+
* timeout, or missing/malformed result sends the request without
|
|
142
|
+
* evidence and reports `op: "deviceIntegrity"` through `onDiagnostic`. */
|
|
143
|
+
deviceIntegrity?: (customerId: string) => Promise<IntegrityEvidence | null | undefined>;
|
|
144
|
+
/** Google Advertising ID source, stored on the customer as `gps_adid` at
|
|
145
|
+
* launch and resent only when it changes. In React Native pass
|
|
146
|
+
* `nativeAdvertisingId` from "react-native-revnix/store"; it needs
|
|
147
|
+
* `play-services-ads-identifier` and the `AD_ID` permission in the app and
|
|
148
|
+
* answers null on iOS and on opt-out, and rejects when the id cannot be
|
|
149
|
+
* read. A null answer deletes a previously stored `gps_adid`; a
|
|
150
|
+
* rejection changes nothing and reports `op: "advertisingId"`. */
|
|
151
|
+
advertisingId?: () => Promise<string | null | undefined>;
|
|
132
152
|
}
|
|
153
|
+
/** Device integrity evidence for `POST /v1/installs` and `/v1/attribution`.
|
|
154
|
+
* iOS: an App Attest key id and its attestation (standard base64) over
|
|
155
|
+
* SHA-256 of the customer id. Android: a classic Play Integrity token whose
|
|
156
|
+
* nonce is base64url (no padding) of SHA-256 of the customer id. */
|
|
157
|
+
export type IntegrityEvidence = {
|
|
158
|
+
platform: "ios";
|
|
159
|
+
keyId: string;
|
|
160
|
+
attestation: string;
|
|
161
|
+
} | {
|
|
162
|
+
platform: "android";
|
|
163
|
+
token: string;
|
|
164
|
+
};
|
|
133
165
|
/** AT11: the install-attribution verdict — GET
|
|
134
166
|
* /v1/customers/{id}/attribution. `installMatch: "unknown"` (no install
|
|
135
167
|
* recorded yet, a normal cold-start race) is never sent to hosts as such;
|
|
@@ -295,6 +295,56 @@ function BlockText({ text, style, ctx, inherited, onPress, }) {
|
|
|
295
295
|
}
|
|
296
296
|
return React.createElement(react_native_1.View, { style: (0, style_js_1.toViewStyle)(style, styleCtx) }, body);
|
|
297
297
|
}
|
|
298
|
+
function Timer({ block, style, ctx, inherited, }) {
|
|
299
|
+
const [now, setNow] = React.useState(Date.now);
|
|
300
|
+
React.useEffect(() => {
|
|
301
|
+
const id = setInterval(() => setNow(Date.now()), 1000);
|
|
302
|
+
return () => clearInterval(id);
|
|
303
|
+
}, []);
|
|
304
|
+
const text = (0, tags_js_1.timerText)(block, ctx.shownAt, now);
|
|
305
|
+
if (text === null)
|
|
306
|
+
return null;
|
|
307
|
+
return React.createElement(BlockText, { text: text, style: style, ctx: ctx, inherited: inherited });
|
|
308
|
+
}
|
|
309
|
+
function Carousel({ pages, autoAdvance, doc, }) {
|
|
310
|
+
const scroller = React.useRef(null);
|
|
311
|
+
const [size, setSize] = React.useState({ width: 0, height: 0 });
|
|
312
|
+
const [page, setPage] = React.useState(0);
|
|
313
|
+
React.useEffect(() => {
|
|
314
|
+
if (!autoAdvance || size.width === 0 || pages.length < 2)
|
|
315
|
+
return;
|
|
316
|
+
const id = setInterval(() => {
|
|
317
|
+
scroller.current?.scrollTo({
|
|
318
|
+
x: ((page + 1) % pages.length) * size.width,
|
|
319
|
+
animated: true,
|
|
320
|
+
});
|
|
321
|
+
}, autoAdvance * 1000);
|
|
322
|
+
return () => clearInterval(id);
|
|
323
|
+
}, [autoAdvance, size.width, page, pages.length]);
|
|
324
|
+
return (React.createElement(react_native_1.View, { style: { flex: 1 }, onLayout: (e) => setSize({
|
|
325
|
+
width: e.nativeEvent.layout.width,
|
|
326
|
+
height: e.nativeEvent.layout.height,
|
|
327
|
+
}) },
|
|
328
|
+
React.createElement(react_native_1.ScrollView, { ref: scroller, horizontal: true, pagingEnabled: true, showsHorizontalScrollIndicator: false, scrollEventThrottle: 16, onScroll: (e) => {
|
|
329
|
+
if (size.width > 0) {
|
|
330
|
+
setPage(Math.round(e.nativeEvent.contentOffset.x / size.width));
|
|
331
|
+
}
|
|
332
|
+
} }, pages.map((node, i) => (React.createElement(react_native_1.View, { key: i, style: size }, node)))),
|
|
333
|
+
pages.length > 1 ? (React.createElement(react_native_1.View, { pointerEvents: "none", style: {
|
|
334
|
+
position: "absolute",
|
|
335
|
+
bottom: 8,
|
|
336
|
+
left: 0,
|
|
337
|
+
right: 0,
|
|
338
|
+
flexDirection: "row",
|
|
339
|
+
justifyContent: "center",
|
|
340
|
+
gap: 6,
|
|
341
|
+
} }, pages.map((_, i) => (React.createElement(react_native_1.View, { key: i, style: {
|
|
342
|
+
width: 6,
|
|
343
|
+
height: 6,
|
|
344
|
+
borderRadius: 3,
|
|
345
|
+
backgroundColor: i === page ? doc.accent : (0, color_js_1.withAlpha)(doc.textColor, 0.3),
|
|
346
|
+
} }))))) : null));
|
|
347
|
+
}
|
|
298
348
|
/** The press handler for a block the design marks as the close, or undefined
|
|
299
349
|
* when it is not one (or the host wired no dismissal). One helper so text,
|
|
300
350
|
* image and button blocks cannot drift on what "action: close" means. */
|
|
@@ -399,6 +449,11 @@ function gridTracks(block) {
|
|
|
399
449
|
}
|
|
400
450
|
function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
401
451
|
const { doc } = ctx;
|
|
452
|
+
React.useEffect(() => {
|
|
453
|
+
if (block.type === "video") {
|
|
454
|
+
ctx.onDiagnostic?.("video drawn as its poster: no built-in player");
|
|
455
|
+
}
|
|
456
|
+
}, [block.type]);
|
|
402
457
|
const styleCtx = { ...ctx.style, fontSize: inherited.fontSize };
|
|
403
458
|
// A pinned card judges its own context; every other block inherits the
|
|
404
459
|
// nearest plan card's. (A repeat card is judged per instance below.)
|
|
@@ -421,9 +476,27 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
421
476
|
switch (block.type) {
|
|
422
477
|
case "text":
|
|
423
478
|
return (React.createElement(BlockText, { text: (0, tags_js_1.resolveTags)(block.text, pkg, ctx.packages), style: style, ctx: ctx, inherited: inherited, onPress: closePress(block, ctx) }));
|
|
424
|
-
case "
|
|
425
|
-
|
|
426
|
-
|
|
479
|
+
case "timer":
|
|
480
|
+
return React.createElement(Timer, { block: block, style: style, ctx: ctx, inherited: inherited });
|
|
481
|
+
case "toggle": {
|
|
482
|
+
const toggle = (0, selection_js_1.toggleSelection)(block, ctx.packages, ctx.selectedId);
|
|
483
|
+
if (!toggle)
|
|
484
|
+
return null;
|
|
485
|
+
const flip = () => ctx.onSelect(toggle.target);
|
|
486
|
+
return (React.createElement(react_native_1.Pressable, { accessibilityRole: "switch", accessibilityState: { checked: toggle.on }, onPress: flip, style: [{ flexDirection: "row", alignItems: "center", gap: 12 }, view] },
|
|
487
|
+
React.createElement(BlockFill, { block: block, ctx: ctx }),
|
|
488
|
+
React.createElement(react_native_1.Text, { style: [
|
|
489
|
+
{ flex: 1 },
|
|
490
|
+
(0, style_js_1.inheritedTextStyle)(text, ctx.style),
|
|
491
|
+
textOnly(style, styleCtx),
|
|
492
|
+
] }, (0, tags_js_1.resolveTags)(block.label, ctx.packages.find((p) => p.packageId === ctx.selectedId), ctx.packages)),
|
|
493
|
+
React.createElement(react_native_1.Switch, { value: toggle.on, onValueChange: flip, trackColor: { true: doc.accent } })));
|
|
494
|
+
}
|
|
495
|
+
case "image":
|
|
496
|
+
case "video": {
|
|
497
|
+
const image = block.type === "image";
|
|
498
|
+
const url = image ? block.url || ctx.heroImageUrl : block.poster;
|
|
499
|
+
const round = image && block.shape === "circle";
|
|
427
500
|
const s = style;
|
|
428
501
|
// A converted design sizes its own slot; the 160pt default is only for
|
|
429
502
|
// a slot dropped into a flow column, and must not fight it.
|
|
@@ -436,7 +509,7 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
436
509
|
// An icon the design marks as the close (REV-252). Same rule as text:
|
|
437
510
|
// the whole box is the target, and the style lands on the Pressable so
|
|
438
511
|
// a pinned icon keeps its offsets.
|
|
439
|
-
const closeImage = closePress(block, ctx);
|
|
512
|
+
const closeImage = image ? closePress(block, ctx) : undefined;
|
|
440
513
|
const ImageBox = closeImage ? react_native_1.Pressable : react_native_1.View;
|
|
441
514
|
return (React.createElement(ImageBox, { accessibilityRole: closeImage ? "button" : undefined, accessibilityLabel: closeImage ? "Close" : undefined, hitSlop: closeImage ? 8 : undefined, onPress: closeImage, style: [
|
|
442
515
|
sized ? null : { height: 160 },
|
|
@@ -444,11 +517,11 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
444
517
|
view,
|
|
445
518
|
] },
|
|
446
519
|
React.createElement(BlockFill, { block: block, ctx: ctx }),
|
|
447
|
-
url ? (React.createElement(react_native_1.Image, { source: { uri: url }, resizeMode: block.fit ?? "cover", style: { width: "100%", height: "100%" } })) : (React.createElement(react_native_1.View, { style: {
|
|
520
|
+
url ? (React.createElement(react_native_1.Image, { source: { uri: url }, resizeMode: block.fit ?? "cover", style: { width: "100%", height: "100%" } })) : image ? (React.createElement(react_native_1.View, { style: {
|
|
448
521
|
width: "100%",
|
|
449
522
|
height: "100%",
|
|
450
523
|
backgroundColor: (0, color_js_1.withAlpha)("#7d879b", 0.2),
|
|
451
|
-
} }))));
|
|
524
|
+
} })) : null));
|
|
452
525
|
}
|
|
453
526
|
case "list":
|
|
454
527
|
return (React.createElement(react_native_1.View, { style: [{ flexDirection: "column", gap: style?.gap ?? 8 }, view] },
|
|
@@ -557,20 +630,23 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
557
630
|
return (React.createElement(react_native_1.View, { style: [block.flex ? { flexGrow: 1 } : { height: 16 }, view] }));
|
|
558
631
|
case "card": {
|
|
559
632
|
const layout = block.layout ?? "column";
|
|
633
|
+
const carousel = layout === "carousel";
|
|
560
634
|
const base = layout === "stack"
|
|
561
635
|
? { position: "relative" }
|
|
562
|
-
:
|
|
563
|
-
? {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
636
|
+
: carousel
|
|
637
|
+
? { position: "relative", overflow: "hidden" }
|
|
638
|
+
: layout === "grid"
|
|
639
|
+
? {
|
|
640
|
+
position: "relative",
|
|
641
|
+
flexDirection: "row",
|
|
642
|
+
flexWrap: "wrap",
|
|
643
|
+
gap: style?.gap ?? 10,
|
|
644
|
+
}
|
|
645
|
+
: {
|
|
646
|
+
position: "relative",
|
|
647
|
+
flexDirection: layout === "row" ? "row" : "column",
|
|
648
|
+
gap: style?.gap ?? 10,
|
|
649
|
+
};
|
|
574
650
|
const tracks = layout === "grid" ? gridTracks(block) : null;
|
|
575
651
|
const renderChildren = (context, childSelection, childText = text) => block.children.map((child, i) => {
|
|
576
652
|
const node = (React.createElement(Block, { key: child.id, block: child, ctx: ctx, pkg: context, inherited: childText, selection: childSelection }));
|
|
@@ -614,6 +690,9 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
614
690
|
base,
|
|
615
691
|
layout === "stack" ? null : { borderRadius: 16 },
|
|
616
692
|
view,
|
|
693
|
+
carousel
|
|
694
|
+
? { height: typeof style?.height === "number" ? style.height : 220 }
|
|
695
|
+
: null,
|
|
617
696
|
];
|
|
618
697
|
const cardPackage = block.packageIndex !== undefined
|
|
619
698
|
? (ctx.packages[block.packageIndex] ?? pkg)
|
|
@@ -622,7 +701,7 @@ function Block({ block, ctx, pkg, inherited, selection, }) {
|
|
|
622
701
|
// how hand-styled plan rows (a highlighted annual beside a plain
|
|
623
702
|
// monthly) become tappable without a products block.
|
|
624
703
|
const pinned = block.packageIndex !== undefined && cardPackage !== undefined;
|
|
625
|
-
return (React.createElement(CardBox, { style: boxStyle, fill: style?.fill, doc: doc, gradient: ctx.gradient, radius: style?.radius ?? (layout === "stack" ? undefined : 16), onPress: pinned ? () => ctx.onSelect(cardPackage.packageId) : undefined }, renderChildren(cardPackage, context)));
|
|
704
|
+
return (React.createElement(CardBox, { style: boxStyle, fill: style?.fill, doc: doc, gradient: ctx.gradient, radius: style?.radius ?? (layout === "stack" ? undefined : 16), onPress: pinned ? () => ctx.onSelect(cardPackage.packageId) : undefined }, carousel ? (React.createElement(Carousel, { pages: renderChildren(cardPackage, context), autoAdvance: block.autoAdvance, doc: doc })) : (renderChildren(cardPackage, context))));
|
|
626
705
|
}
|
|
627
706
|
}
|
|
628
707
|
}
|
|
@@ -634,6 +713,7 @@ function RevnixBlockPaywall(props) {
|
|
|
634
713
|
// re-runs on every selection tap.
|
|
635
714
|
const doc = React.useMemo(() => (0, locale_js_1.localizeBlockDoc)(props.doc, props.locale ?? (0, locale_js_1.detectLocale)()), [props.doc, props.locale]);
|
|
636
715
|
const [internalSelected, setInternalSelected] = React.useState(undefined);
|
|
716
|
+
const [shownAt] = React.useState(Date.now);
|
|
637
717
|
// Contract §1: host → own tap → design highlight → first package, each
|
|
638
718
|
// only when the offering carries it.
|
|
639
719
|
const selectedId = (0, selection_js_1.selectedPackageId)(packages, props.selectedPackageId, internalSelected, props.highlightPackageId);
|
|
@@ -675,6 +755,7 @@ function RevnixBlockPaywall(props) {
|
|
|
675
755
|
onDiagnostic: props.onDiagnostic,
|
|
676
756
|
style: styleCtx,
|
|
677
757
|
loading: props.loading === true,
|
|
758
|
+
shownAt,
|
|
678
759
|
};
|
|
679
760
|
const children = doc.blocks.map((block) => (React.createElement(Block, { key: block.id, block: block, ctx: ctx, pkg: packages.find((p) => p.packageId === selectedId) ?? packages[0], inherited: rootText })));
|
|
680
761
|
// REV-252: the renderer's own close, drawn only for a design that authors
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
-
import type { PaywallConfig, PaywallInteraction, PaywallPurchaseOutcome, RegisterPurchaseResult } from "../types.js";
|
|
4
|
-
import type
|
|
3
|
+
import type { PaywallConfig, PaywallInteraction, PaywallPurchaseOutcome, PlacementResolution, RegisterPurchaseResult } from "../types.js";
|
|
4
|
+
import { type RevnixClient } from "../client.js";
|
|
5
5
|
import type { FontFamilyMap } from "../blocks/style.js";
|
|
6
6
|
import { type RevnixBlurComponent, type RevnixGradientComponent } from "./RevnixBlockPaywall.js";
|
|
7
7
|
/** One purchasable row. `priceLabel` must come from the store (localized). */
|
|
@@ -155,4 +155,8 @@ export interface RevnixPaywallProps {
|
|
|
155
155
|
* layouts below render `config`'s own copy whatever this says. */
|
|
156
156
|
locale?: string;
|
|
157
157
|
}
|
|
158
|
+
/** Warms React Native's image cache with every image `resolution`'s paywall
|
|
159
|
+
* loads, so it paints without waiting on the network. Pair it with
|
|
160
|
+
* `client.preload` / `resolvePlacement`. Never throws. */
|
|
161
|
+
export declare function prefetchPaywallImages(resolution: PlacementResolution | null): Promise<void>;
|
|
158
162
|
export declare function RevnixPaywall(props: RevnixPaywallProps): React.JSX.Element;
|
package/dist/ui/RevnixPaywall.js
CHANGED
|
@@ -50,11 +50,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
50
50
|
})();
|
|
51
51
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
52
|
exports.LIGHT_PAYWALL_THEME = exports.DEFAULT_PAYWALL_THEME = void 0;
|
|
53
|
+
exports.prefetchPaywallImages = prefetchPaywallImages;
|
|
53
54
|
exports.RevnixPaywall = RevnixPaywall;
|
|
54
55
|
const React = __importStar(require("react"));
|
|
55
56
|
const react_native_1 = require("react-native");
|
|
56
57
|
const implicit_js_1 = require("../implicit.js");
|
|
57
58
|
const store_js_1 = require("../store.js");
|
|
59
|
+
const client_js_1 = require("../client.js");
|
|
58
60
|
const color_js_1 = require("../blocks/color.js");
|
|
59
61
|
const RevnixBlockPaywall_js_1 = require("./RevnixBlockPaywall.js");
|
|
60
62
|
/** `config.blocks` is opaque on the core config (the non-UI entrypoint must
|
|
@@ -103,6 +105,12 @@ const CURRENCY_SYMBOL = /[$€£¥₹₩₽₺₫₪฿₴₦₱]/;
|
|
|
103
105
|
// part of the public theme — derived from the config's mode, in lockstep
|
|
104
106
|
// with the dashboard preview's SCREEN_PALETTES.card.
|
|
105
107
|
const CARD_BG = { dark: "#181b22", light: "#f4f5f7" };
|
|
108
|
+
/** Warms React Native's image cache with every image `resolution`'s paywall
|
|
109
|
+
* loads, so it paints without waiting on the network. Pair it with
|
|
110
|
+
* `client.preload` / `resolvePlacement`. Never throws. */
|
|
111
|
+
async function prefetchPaywallImages(resolution) {
|
|
112
|
+
await Promise.all((0, client_js_1.paywallImageUrls)(resolution?.paywall).map((url) => react_native_1.Image.prefetch(url).catch(() => false)));
|
|
113
|
+
}
|
|
106
114
|
function RevnixPaywall(props) {
|
|
107
115
|
// One view per mount: remounting (a re-shown paywall) is a genuine new
|
|
108
116
|
// display; re-renders are not.
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { RevnixPaywall, DEFAULT_PAYWALL_THEME, LIGHT_PAYWALL_THEME, type RevnixPaywallPackage, type RevnixPaywallProps, type RevnixPaywallTheme, } from "./RevnixPaywall.js";
|
|
1
|
+
export { RevnixPaywall, DEFAULT_PAYWALL_THEME, LIGHT_PAYWALL_THEME, prefetchPaywallImages, type RevnixPaywallPackage, type RevnixPaywallProps, type RevnixPaywallTheme, } from "./RevnixPaywall.js";
|
|
2
2
|
export { RevnixBlockPaywall, type RevnixBlockPaywallProps, type RevnixBlurComponent, type RevnixBlurProps, type RevnixGradientComponent, type RevnixGradientProps, } from "./RevnixBlockPaywall.js";
|
|
3
3
|
export { DESIGN_CANVAS, type BlockStyle, type PaywallBlock, type PaywallBlockDoc, } from "../blocks/types.js";
|
|
4
4
|
export { backgroundBaseColor, backgroundLayers, coverPlacement, type BackgroundFit, type BackgroundImage, type BackgroundLayers, type BackgroundOverlay, type PaywallBackground, type PaywallBackgroundSpec, } from "../blocks/background.js";
|
package/dist/ui/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveTags = exports.minorUnitsForCurrency = exports.setLocale = exports.normalizeLocale = exports.localizeBlockDoc = exports.localeChain = exports.linkLabels = exports.detectLocale = exports.coverPlacement = exports.backgroundLayers = exports.backgroundBaseColor = exports.DESIGN_CANVAS = exports.RevnixBlockPaywall = exports.LIGHT_PAYWALL_THEME = exports.DEFAULT_PAYWALL_THEME = exports.RevnixPaywall = void 0;
|
|
3
|
+
exports.resolveTags = exports.minorUnitsForCurrency = exports.setLocale = exports.normalizeLocale = exports.localizeBlockDoc = exports.localeChain = exports.linkLabels = exports.detectLocale = exports.coverPlacement = exports.backgroundLayers = exports.backgroundBaseColor = exports.DESIGN_CANVAS = exports.RevnixBlockPaywall = exports.prefetchPaywallImages = exports.LIGHT_PAYWALL_THEME = exports.DEFAULT_PAYWALL_THEME = exports.RevnixPaywall = void 0;
|
|
4
4
|
var RevnixPaywall_js_1 = require("./RevnixPaywall.js");
|
|
5
5
|
Object.defineProperty(exports, "RevnixPaywall", { enumerable: true, get: function () { return RevnixPaywall_js_1.RevnixPaywall; } });
|
|
6
6
|
Object.defineProperty(exports, "DEFAULT_PAYWALL_THEME", { enumerable: true, get: function () { return RevnixPaywall_js_1.DEFAULT_PAYWALL_THEME; } });
|
|
7
7
|
Object.defineProperty(exports, "LIGHT_PAYWALL_THEME", { enumerable: true, get: function () { return RevnixPaywall_js_1.LIGHT_PAYWALL_THEME; } });
|
|
8
|
+
Object.defineProperty(exports, "prefetchPaywallImages", { enumerable: true, get: function () { return RevnixPaywall_js_1.prefetchPaywallImages; } });
|
|
8
9
|
var RevnixBlockPaywall_js_1 = require("./RevnixBlockPaywall.js");
|
|
9
10
|
Object.defineProperty(exports, "RevnixBlockPaywall", { enumerable: true, get: function () { return RevnixBlockPaywall_js_1.RevnixBlockPaywall; } });
|
|
10
11
|
var types_js_1 = require("../blocks/types.js");
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "1.
|
|
1
|
+
export declare const SDK_VERSION = "1.7.0";
|
package/dist/version.js
CHANGED
|
@@ -4,4 +4,4 @@ exports.SDK_VERSION = void 0;
|
|
|
4
4
|
// L3: the single source of the SDK version inside src/. Must match
|
|
5
5
|
// package.json "version" — version.test.ts fails the build when they drift
|
|
6
6
|
// (they had: client.ts said 0.7.0 while the package shipped 0.9.0).
|
|
7
|
-
exports.SDK_VERSION = "1.
|
|
7
|
+
exports.SDK_VERSION = "1.7.0";
|
package/ios/RevnixStore.m
CHANGED
|
@@ -15,4 +15,8 @@ RCT_EXTERN_METHOD(finish:(NSString *)transactionId
|
|
|
15
15
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
16
16
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
17
17
|
|
|
18
|
+
RCT_EXTERN_METHOD(deviceIntegrity:(NSString *)customerId
|
|
19
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
20
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
21
|
+
|
|
18
22
|
@end
|