@revenuecat/purchases-ui-js 4.10.2 → 4.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/express-purchase-button/ExpressPurchaseButton.stories.svelte +215 -0
- package/dist/components/express-purchase-button/ExpressPurchaseButton.svelte +40 -12
- package/dist/components/express-purchase-button/ExpressPurchaseButtonTestWrapper.svelte +52 -0
- package/dist/components/express-purchase-button/ExpressPurchaseButtonTestWrapper.svelte.d.ts +11 -0
- package/dist/components/express-purchase-button/component-subtree-utils.d.ts +1 -0
- package/dist/components/express-purchase-button/component-subtree-utils.js +41 -16
- package/dist/components/paywall/Paywall.svelte +11 -1
- package/dist/components/paywall/Paywall.svelte.d.ts +3 -1
- package/dist/components/wallet-button/WalletButton.svelte +126 -3
- package/dist/components/wallet-button/WalletButtonTestWrapper.svelte +52 -0
- package/dist/components/wallet-button/WalletButtonTestWrapper.svelte.d.ts +12 -0
- package/dist/components/workflows/Screen.svelte +11 -1
- package/dist/components/workflows/Screen.svelte.d.ts +3 -1
- package/dist/components/workflows/Workflow.svelte +11 -1
- package/dist/components/workflows/Workflow.svelte.d.ts +3 -1
- package/dist/index.d.ts +1 -1
- package/dist/stores/paywall.d.ts +3 -1
- package/dist/stories/paywall-decorator.d.ts +2 -2
- package/dist/stories/paywall-decorator.js +3 -1
- package/dist/types/components/wallet-button.d.ts +4 -0
- package/dist/types/paywall-component-interaction.d.ts +2 -1
- package/dist/types/wallet.d.ts +4 -0
- package/dist/utils/style-utils.d.ts +3 -3
- package/dist/utils/style-utils.js +4 -6
- package/package.json +1 -1
|
@@ -11,18 +11,45 @@
|
|
|
11
11
|
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
12
12
|
import { mockDateDecorator } from "storybook-mock-date-decorator";
|
|
13
13
|
import type { PurchaseButtonProps } from "../../types/components/purchase-button";
|
|
14
|
+
import type { StackProps } from "../../types/components/stack";
|
|
15
|
+
import type {
|
|
16
|
+
WalletPurchasePrepare,
|
|
17
|
+
WalletPurchaseStart,
|
|
18
|
+
} from "../../types/wallet";
|
|
14
19
|
|
|
15
20
|
const defaultLocale = "en_US";
|
|
16
21
|
const textLidWhenAvailable = "purchase_wallet_available_text";
|
|
17
22
|
const textLidWhenUnavailable = "purchase_wallet_unavailable_text";
|
|
23
|
+
const textLidCustomWalletButton = "custom_wallet_button_text";
|
|
18
24
|
const loc = {
|
|
19
25
|
en_US: {
|
|
20
26
|
...localizations["en_US"],
|
|
21
27
|
[textLidWhenAvailable]: "Purchase with Wallet",
|
|
22
28
|
[textLidWhenUnavailable]: "Wallet Unavailable",
|
|
29
|
+
[textLidCustomWalletButton]: "Pay with Custom Wallet Button",
|
|
23
30
|
},
|
|
24
31
|
};
|
|
25
32
|
|
|
33
|
+
const createPrepareWalletPurchase =
|
|
34
|
+
({
|
|
35
|
+
available,
|
|
36
|
+
delay,
|
|
37
|
+
}: {
|
|
38
|
+
available: boolean;
|
|
39
|
+
delay?: number;
|
|
40
|
+
}): WalletPurchasePrepare =>
|
|
41
|
+
(_selectedPackageId: string) =>
|
|
42
|
+
new Promise((resolve) => {
|
|
43
|
+
setTimeout(() => resolve(available), delay);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const createOnWalletPurchaseClicked =
|
|
47
|
+
({ delay = 500 }: { delay?: number } = {}): WalletPurchaseStart =>
|
|
48
|
+
(_selectedPackageId: string) =>
|
|
49
|
+
new Promise((resolve) => {
|
|
50
|
+
setTimeout(() => resolve(), delay);
|
|
51
|
+
});
|
|
52
|
+
|
|
26
53
|
const createWalletButtonRender =
|
|
27
54
|
({ available, delay }: { available: boolean; delay?: number }) =>
|
|
28
55
|
(
|
|
@@ -106,6 +133,116 @@
|
|
|
106
133
|
shadow: null,
|
|
107
134
|
};
|
|
108
135
|
|
|
136
|
+
const customWalletButtonProps = {
|
|
137
|
+
type: "wallet_button",
|
|
138
|
+
id: "wallet_button_custom",
|
|
139
|
+
name: "Wallet Button Custom",
|
|
140
|
+
mode: "custom",
|
|
141
|
+
size: {
|
|
142
|
+
width: { type: "fill" },
|
|
143
|
+
height: { type: "fit" },
|
|
144
|
+
},
|
|
145
|
+
margin: { top: 0, trailing: 0, bottom: 0, leading: 0 },
|
|
146
|
+
stack: {
|
|
147
|
+
type: "stack",
|
|
148
|
+
id: "wallet-button-custom-stack",
|
|
149
|
+
name: "Wallet Button Custom Stack",
|
|
150
|
+
background_color: {
|
|
151
|
+
light: {
|
|
152
|
+
type: "hex",
|
|
153
|
+
value: "#111111",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
background: {
|
|
157
|
+
type: "color",
|
|
158
|
+
value: {
|
|
159
|
+
light: {
|
|
160
|
+
type: "hex",
|
|
161
|
+
value: "#111111",
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
components: [
|
|
166
|
+
{
|
|
167
|
+
background_color: null,
|
|
168
|
+
color: {
|
|
169
|
+
light: {
|
|
170
|
+
type: "hex",
|
|
171
|
+
value: "#ffffff",
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
font_name: null,
|
|
175
|
+
font_size: "heading_m",
|
|
176
|
+
font_weight: "extra_bold",
|
|
177
|
+
horizontal_alignment: "center",
|
|
178
|
+
id: "wallet-button-custom-text",
|
|
179
|
+
margin: {
|
|
180
|
+
bottom: 0,
|
|
181
|
+
leading: 0,
|
|
182
|
+
top: 0,
|
|
183
|
+
trailing: 0,
|
|
184
|
+
},
|
|
185
|
+
name: "Text",
|
|
186
|
+
padding: {
|
|
187
|
+
bottom: 0,
|
|
188
|
+
leading: 0,
|
|
189
|
+
top: 0,
|
|
190
|
+
trailing: 0,
|
|
191
|
+
},
|
|
192
|
+
size: {
|
|
193
|
+
height: {
|
|
194
|
+
type: "fit",
|
|
195
|
+
value: null,
|
|
196
|
+
},
|
|
197
|
+
width: {
|
|
198
|
+
type: "fill",
|
|
199
|
+
value: null,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
text_lid: textLidCustomWalletButton,
|
|
203
|
+
type: "text",
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
dimension: {
|
|
207
|
+
type: "vertical",
|
|
208
|
+
alignment: "center",
|
|
209
|
+
distribution: "center",
|
|
210
|
+
},
|
|
211
|
+
margin: {
|
|
212
|
+
bottom: 0,
|
|
213
|
+
leading: 0,
|
|
214
|
+
top: 0,
|
|
215
|
+
trailing: 0,
|
|
216
|
+
},
|
|
217
|
+
padding: {
|
|
218
|
+
bottom: 16,
|
|
219
|
+
leading: 16,
|
|
220
|
+
top: 16,
|
|
221
|
+
trailing: 16,
|
|
222
|
+
},
|
|
223
|
+
border: null,
|
|
224
|
+
shadow: null,
|
|
225
|
+
shape: {
|
|
226
|
+
corners: {
|
|
227
|
+
bottom_leading: 8,
|
|
228
|
+
bottom_trailing: 8,
|
|
229
|
+
top_leading: 8,
|
|
230
|
+
top_trailing: 8,
|
|
231
|
+
},
|
|
232
|
+
type: "rectangle",
|
|
233
|
+
},
|
|
234
|
+
size: {
|
|
235
|
+
height: {
|
|
236
|
+
type: "fit",
|
|
237
|
+
},
|
|
238
|
+
width: {
|
|
239
|
+
type: "fill",
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
spacing: 8,
|
|
243
|
+
},
|
|
244
|
+
} satisfies WalletButtonProps;
|
|
245
|
+
|
|
109
246
|
const purchaseButtonWhenAvailableProps = {
|
|
110
247
|
type: "purchase_button",
|
|
111
248
|
id: "purchase-button",
|
|
@@ -216,6 +353,39 @@
|
|
|
216
353
|
purchaseButtonWhenUnavailableProps.stack.components[0].text_lid =
|
|
217
354
|
textLidWhenUnavailable;
|
|
218
355
|
|
|
356
|
+
const walletAvailableStackCustom = {
|
|
357
|
+
type: "stack",
|
|
358
|
+
id: "wallet-available-stack-custom",
|
|
359
|
+
name: "Wallet Available Stack (Custom)",
|
|
360
|
+
components: [customWalletButtonProps, purchaseButtonWhenAvailableProps],
|
|
361
|
+
size: {
|
|
362
|
+
width: { type: "fill" },
|
|
363
|
+
height: { type: "fit" },
|
|
364
|
+
},
|
|
365
|
+
dimension: {
|
|
366
|
+
type: "vertical",
|
|
367
|
+
alignment: "center",
|
|
368
|
+
distribution: "center",
|
|
369
|
+
},
|
|
370
|
+
spacing: 8,
|
|
371
|
+
margin: { top: 0, trailing: 0, bottom: 0, leading: 0 },
|
|
372
|
+
padding: { top: 16, trailing: 16, bottom: 16, leading: 16 },
|
|
373
|
+
background_color: null,
|
|
374
|
+
background: null,
|
|
375
|
+
border: null,
|
|
376
|
+
shape: {
|
|
377
|
+
type: "rectangle",
|
|
378
|
+
corners: {
|
|
379
|
+
top_leading: 8,
|
|
380
|
+
top_trailing: 8,
|
|
381
|
+
bottom_leading: 8,
|
|
382
|
+
bottom_trailing: 8,
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
shadow: null,
|
|
386
|
+
badge: null,
|
|
387
|
+
} satisfies StackProps;
|
|
388
|
+
|
|
219
389
|
const { Story } = defineMeta({
|
|
220
390
|
title: "Components/ExpressPurchaseButton",
|
|
221
391
|
component: ExpressPurchaseButton,
|
|
@@ -460,3 +630,48 @@
|
|
|
460
630
|
size: { width: { type: "fixed", value: 500 }, height: { type: "fit" } },
|
|
461
631
|
}}
|
|
462
632
|
/>
|
|
633
|
+
|
|
634
|
+
<Story
|
|
635
|
+
name="Custom button — available"
|
|
636
|
+
decorators={[
|
|
637
|
+
paywallDecorator(
|
|
638
|
+
undefined,
|
|
639
|
+
"some-package-id",
|
|
640
|
+
createPrepareWalletPurchase({ available: true, delay: 200 }),
|
|
641
|
+
createOnWalletPurchaseClicked(),
|
|
642
|
+
),
|
|
643
|
+
]}
|
|
644
|
+
args={{
|
|
645
|
+
wallet_available_stack: walletAvailableStackCustom,
|
|
646
|
+
}}
|
|
647
|
+
/>
|
|
648
|
+
|
|
649
|
+
<Story
|
|
650
|
+
name="Custom button — unavailable"
|
|
651
|
+
decorators={[
|
|
652
|
+
paywallDecorator(
|
|
653
|
+
undefined,
|
|
654
|
+
"some-package-id",
|
|
655
|
+
createPrepareWalletPurchase({ available: false, delay: 200 }),
|
|
656
|
+
createOnWalletPurchaseClicked(),
|
|
657
|
+
),
|
|
658
|
+
]}
|
|
659
|
+
args={{
|
|
660
|
+
wallet_available_stack: walletAvailableStackCustom,
|
|
661
|
+
}}
|
|
662
|
+
/>
|
|
663
|
+
|
|
664
|
+
<Story
|
|
665
|
+
name="Custom button — slow prepare (skeleton)"
|
|
666
|
+
decorators={[
|
|
667
|
+
paywallDecorator(
|
|
668
|
+
undefined,
|
|
669
|
+
"some-package-id",
|
|
670
|
+
createPrepareWalletPurchase({ available: true, delay: 3000 }),
|
|
671
|
+
createOnWalletPurchaseClicked(),
|
|
672
|
+
),
|
|
673
|
+
]}
|
|
674
|
+
args={{
|
|
675
|
+
wallet_available_stack: walletAvailableStackCustom,
|
|
676
|
+
}}
|
|
677
|
+
/>
|
|
@@ -3,37 +3,65 @@
|
|
|
3
3
|
import { getPaywallContext, setPaywallContext } from "../../stores/paywall";
|
|
4
4
|
import type { ExpressPurchaseButtonProps } from "../../types/components/express-purchase-button-props";
|
|
5
5
|
import type { StackProps } from "../../types/components/stack";
|
|
6
|
-
import {
|
|
6
|
+
import { untrack } from "svelte";
|
|
7
|
+
import {
|
|
8
|
+
findComponentOfTypeInSubtree,
|
|
9
|
+
hasComponentOfTypeInSubtree,
|
|
10
|
+
} from "./component-subtree-utils";
|
|
7
11
|
|
|
8
|
-
const props: ExpressPurchaseButtonProps = $props();
|
|
9
12
|
const {
|
|
10
13
|
wallet_unknown_stack,
|
|
11
14
|
wallet_available_stack,
|
|
12
15
|
wallet_unavailable_stack,
|
|
13
16
|
type,
|
|
14
17
|
...partialStackProps
|
|
15
|
-
} = props;
|
|
18
|
+
}: ExpressPurchaseButtonProps = $props();
|
|
16
19
|
|
|
17
|
-
const {
|
|
20
|
+
const {
|
|
21
|
+
walletButtonRender,
|
|
22
|
+
prepareWalletPurchase,
|
|
23
|
+
onWalletPurchaseClicked,
|
|
24
|
+
...rest
|
|
25
|
+
} = getPaywallContext();
|
|
18
26
|
|
|
19
|
-
const availableStackHasWalletButton =
|
|
20
|
-
wallet_available_stack,
|
|
21
|
-
|
|
27
|
+
const availableStackHasWalletButton = untrack(() =>
|
|
28
|
+
hasComponentOfTypeInSubtree(wallet_available_stack, "wallet_button"),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const walletButton = untrack(() =>
|
|
32
|
+
findComponentOfTypeInSubtree(wallet_available_stack, "wallet_button"),
|
|
22
33
|
);
|
|
23
34
|
|
|
35
|
+
// A partial custom integration (prepare without a click handler, or vice versa)
|
|
36
|
+
// would otherwise show an enabled CTA whose click can never purchase.
|
|
37
|
+
const hostReady =
|
|
38
|
+
walletButton?.type === "wallet_button" && walletButton.mode === "custom"
|
|
39
|
+
? !!prepareWalletPurchase && !!onWalletPurchaseClicked
|
|
40
|
+
: !!walletButtonRender;
|
|
41
|
+
|
|
24
42
|
let walletButtonAvailable = $state<boolean | undefined>(
|
|
25
43
|
availableStackHasWalletButton ? undefined : false,
|
|
26
44
|
);
|
|
27
45
|
let shouldShowWalletSkeleton = $state(
|
|
28
|
-
|
|
46
|
+
hostReady && availableStackHasWalletButton,
|
|
29
47
|
);
|
|
30
48
|
|
|
31
49
|
const onWalletButtonReady = (isAvailable?: boolean) => {
|
|
32
|
-
|
|
50
|
+
// wallet_available_stack (and the WalletButton inside it) always mounts, hidden
|
|
51
|
+
// via CSS, so its own successful prepare can report ready independently of
|
|
52
|
+
// hostReady; clamp to unavailable so an incomplete host integration never
|
|
53
|
+
// reveals a CTA that can't purchase.
|
|
54
|
+
walletButtonAvailable = hostReady ? isAvailable : false;
|
|
33
55
|
shouldShowWalletSkeleton = false;
|
|
34
56
|
};
|
|
35
57
|
|
|
36
|
-
setPaywallContext({
|
|
58
|
+
setPaywallContext({
|
|
59
|
+
walletButtonRender,
|
|
60
|
+
prepareWalletPurchase,
|
|
61
|
+
onWalletPurchaseClicked,
|
|
62
|
+
...rest,
|
|
63
|
+
onWalletButtonReady,
|
|
64
|
+
});
|
|
37
65
|
|
|
38
66
|
const shouldShowUnavailableStack = $derived(
|
|
39
67
|
!walletButtonAvailable || !wallet_available_stack,
|
|
@@ -46,7 +74,7 @@
|
|
|
46
74
|
),
|
|
47
75
|
);
|
|
48
76
|
|
|
49
|
-
const wrapperStackProps: StackProps = {
|
|
77
|
+
const wrapperStackProps: StackProps = $derived({
|
|
50
78
|
components: [],
|
|
51
79
|
type: "stack",
|
|
52
80
|
visible: null,
|
|
@@ -58,7 +86,7 @@
|
|
|
58
86
|
background_color: null,
|
|
59
87
|
spacing: 0,
|
|
60
88
|
...partialStackProps,
|
|
61
|
-
};
|
|
89
|
+
});
|
|
62
90
|
</script>
|
|
63
91
|
|
|
64
92
|
<Stack {...wrapperStackProps}>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { setColorModeContext } from "../../stores/color-mode";
|
|
3
|
+
import { setPaywallContext } from "../../stores/paywall";
|
|
4
|
+
import type { ExpressPurchaseButtonProps } from "../../types/components/express-purchase-button-props";
|
|
5
|
+
import type { UIConfig } from "../../types/ui-config";
|
|
6
|
+
import type {
|
|
7
|
+
WalletButtonRender,
|
|
8
|
+
WalletPurchasePrepare,
|
|
9
|
+
WalletPurchaseStart,
|
|
10
|
+
} from "../../types/wallet";
|
|
11
|
+
import { readable, writable } from "svelte/store";
|
|
12
|
+
import { untrack } from "svelte";
|
|
13
|
+
import ExpressPurchaseButton from "./ExpressPurchaseButton.svelte";
|
|
14
|
+
|
|
15
|
+
interface Props extends ExpressPurchaseButtonProps {
|
|
16
|
+
selectedPackageId?: string;
|
|
17
|
+
walletButtonRender?: WalletButtonRender;
|
|
18
|
+
prepareWalletPurchase?: WalletPurchasePrepare;
|
|
19
|
+
onWalletPurchaseClicked?: WalletPurchaseStart;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
selectedPackageId = "pkg-1",
|
|
24
|
+
walletButtonRender,
|
|
25
|
+
prepareWalletPurchase,
|
|
26
|
+
onWalletPurchaseClicked,
|
|
27
|
+
...expressPurchaseButtonProps
|
|
28
|
+
}: Props = $props();
|
|
29
|
+
|
|
30
|
+
setColorModeContext();
|
|
31
|
+
|
|
32
|
+
const uiConfig: UIConfig = { app: { fonts: {} } };
|
|
33
|
+
|
|
34
|
+
setPaywallContext(
|
|
35
|
+
untrack(() => ({
|
|
36
|
+
selectedPackageId: writable<string | undefined>(selectedPackageId),
|
|
37
|
+
variablesPerPackage: readable(undefined),
|
|
38
|
+
baseVariables: readable(undefined),
|
|
39
|
+
infoPerPackage: readable(undefined),
|
|
40
|
+
onPurchase: () => {},
|
|
41
|
+
emitComponentInteraction: () => {},
|
|
42
|
+
onButtonAction: () => {},
|
|
43
|
+
uiConfig,
|
|
44
|
+
hideBackButtons: false,
|
|
45
|
+
walletButtonRender,
|
|
46
|
+
prepareWalletPurchase,
|
|
47
|
+
onWalletPurchaseClicked,
|
|
48
|
+
})),
|
|
49
|
+
);
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<ExpressPurchaseButton {...expressPurchaseButtonProps} />
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ExpressPurchaseButtonProps } from "../../types/components/express-purchase-button-props";
|
|
2
|
+
import type { WalletButtonRender, WalletPurchasePrepare, WalletPurchaseStart } from "../../types/wallet";
|
|
3
|
+
interface Props extends ExpressPurchaseButtonProps {
|
|
4
|
+
selectedPackageId?: string;
|
|
5
|
+
walletButtonRender?: WalletButtonRender;
|
|
6
|
+
prepareWalletPurchase?: WalletPurchasePrepare;
|
|
7
|
+
onWalletPurchaseClicked?: WalletPurchaseStart;
|
|
8
|
+
}
|
|
9
|
+
declare const ExpressPurchaseButtonTestWrapper: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type ExpressPurchaseButtonTestWrapper = ReturnType<typeof ExpressPurchaseButtonTestWrapper>;
|
|
11
|
+
export default ExpressPurchaseButtonTestWrapper;
|
|
@@ -2,4 +2,5 @@ import type { Component } from "../../types/component";
|
|
|
2
2
|
import type { StackProps } from "../../types/components/stack";
|
|
3
3
|
type ComponentType = Component["type"];
|
|
4
4
|
export declare const hasComponentOfTypeInSubtree: (stack: StackProps, componentType: ComponentType) => boolean;
|
|
5
|
+
export declare const findComponentOfTypeInSubtree: (stack: StackProps, componentType: ComponentType) => Component | undefined;
|
|
5
6
|
export {};
|
|
@@ -1,31 +1,56 @@
|
|
|
1
1
|
export const hasComponentOfTypeInSubtree = (stack, componentType) => {
|
|
2
|
-
return (stack
|
|
3
|
-
(stack.badge?.stack
|
|
4
|
-
? hasComponentOfTypeInSubtree(stack.badge.stack, componentType)
|
|
5
|
-
: false));
|
|
2
|
+
return findComponentOfTypeInSubtree(stack, componentType) !== undefined;
|
|
6
3
|
};
|
|
7
|
-
const
|
|
4
|
+
export const findComponentOfTypeInSubtree = (stack, componentType) => {
|
|
5
|
+
for (const component of stack.components) {
|
|
6
|
+
const found = findComponentTypeInComponent(component, componentType);
|
|
7
|
+
if (found)
|
|
8
|
+
return found;
|
|
9
|
+
}
|
|
10
|
+
return stack.badge?.stack
|
|
11
|
+
? findComponentOfTypeInSubtree(stack.badge.stack, componentType)
|
|
12
|
+
: undefined;
|
|
13
|
+
};
|
|
14
|
+
const findComponentTypeInComponent = (component, componentType) => {
|
|
8
15
|
if (component.type === componentType) {
|
|
9
|
-
return
|
|
16
|
+
return component;
|
|
10
17
|
}
|
|
11
|
-
if (component.fallback
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
if (component.fallback) {
|
|
19
|
+
const found = findComponentTypeInComponent(component.fallback, componentType);
|
|
20
|
+
if (found)
|
|
21
|
+
return found;
|
|
14
22
|
}
|
|
15
23
|
switch (component.type) {
|
|
16
24
|
case "carousel":
|
|
17
|
-
|
|
25
|
+
for (const page of component.pages) {
|
|
26
|
+
const found = findComponentOfTypeInSubtree(page, componentType);
|
|
27
|
+
if (found)
|
|
28
|
+
return found;
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
18
31
|
case "package":
|
|
19
32
|
case "purchase_button":
|
|
20
33
|
case "tab":
|
|
21
34
|
case "tab_control_button":
|
|
22
|
-
return
|
|
35
|
+
return findComponentOfTypeInSubtree(component.stack, componentType);
|
|
36
|
+
case "wallet_button":
|
|
37
|
+
return component.stack
|
|
38
|
+
? findComponentOfTypeInSubtree(component.stack, componentType)
|
|
39
|
+
: undefined;
|
|
23
40
|
case "stack":
|
|
24
|
-
return
|
|
25
|
-
case "tabs":
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
return findComponentOfTypeInSubtree(component, componentType);
|
|
42
|
+
case "tabs": {
|
|
43
|
+
const foundInControl = findComponentOfTypeInSubtree(component.control.stack, componentType);
|
|
44
|
+
if (foundInControl)
|
|
45
|
+
return foundInControl;
|
|
46
|
+
for (const tab of component.tabs) {
|
|
47
|
+
const found = findComponentOfTypeInSubtree(tab.stack, componentType);
|
|
48
|
+
if (found)
|
|
49
|
+
return found;
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
28
53
|
default:
|
|
29
|
-
return
|
|
54
|
+
return undefined;
|
|
30
55
|
}
|
|
31
56
|
};
|
|
@@ -60,7 +60,11 @@
|
|
|
60
60
|
type PackageInfoStore,
|
|
61
61
|
setPackageInfoContext,
|
|
62
62
|
} from "../../stores/packageInfo";
|
|
63
|
-
import type {
|
|
63
|
+
import type {
|
|
64
|
+
WalletButtonRender,
|
|
65
|
+
WalletPurchasePrepare,
|
|
66
|
+
WalletPurchaseStart,
|
|
67
|
+
} from "../../types/wallet";
|
|
64
68
|
import type { ReservedAttribute } from "../../types/components/input-text";
|
|
65
69
|
import type {
|
|
66
70
|
PaywallOffering,
|
|
@@ -111,6 +115,8 @@
|
|
|
111
115
|
onError?: (error: unknown) => void;
|
|
112
116
|
hideBackButtons?: boolean;
|
|
113
117
|
walletButtonRender?: WalletButtonRender;
|
|
118
|
+
prepareWalletPurchase?: WalletPurchasePrepare;
|
|
119
|
+
onWalletPurchaseClicked?: WalletPurchaseStart;
|
|
114
120
|
onInputChanged?: (
|
|
115
121
|
fieldId: string,
|
|
116
122
|
value: string,
|
|
@@ -173,6 +179,8 @@
|
|
|
173
179
|
onError,
|
|
174
180
|
uiConfig,
|
|
175
181
|
walletButtonRender,
|
|
182
|
+
prepareWalletPurchase,
|
|
183
|
+
onWalletPurchaseClicked,
|
|
176
184
|
onInputChanged,
|
|
177
185
|
onSensitiveInputChanged,
|
|
178
186
|
onReservedAttributeChanged,
|
|
@@ -388,6 +396,8 @@
|
|
|
388
396
|
onSensitiveInputChanged,
|
|
389
397
|
onReservedAttributeChanged,
|
|
390
398
|
walletButtonRender,
|
|
399
|
+
prepareWalletPurchase,
|
|
400
|
+
onWalletPurchaseClicked,
|
|
391
401
|
uiConfig,
|
|
392
402
|
hideBackButtons,
|
|
393
403
|
});
|
|
@@ -6,7 +6,7 @@ import type { OnComponentInteraction } from "../../types/paywall-component-inter
|
|
|
6
6
|
import type { PaywallData } from "../../types/paywall";
|
|
7
7
|
import type { UIConfig } from "../../types/ui-config";
|
|
8
8
|
import { type CustomVariables, type PackageInfo, type VariableDictionary } from "../../types/variables";
|
|
9
|
-
import type { WalletButtonRender } from "../../types/wallet";
|
|
9
|
+
import type { WalletButtonRender, WalletPurchasePrepare, WalletPurchaseStart } from "../../types/wallet";
|
|
10
10
|
import type { ReservedAttribute } from "../../types/components/input-text";
|
|
11
11
|
import type { PaywallOffering, PaywallPackage, PaywallWorkflow } from "@revenuecat/workflow-web-components-sdk";
|
|
12
12
|
import { type BridgeInputs } from "../web-view/build-paywall-context";
|
|
@@ -45,6 +45,8 @@ interface Props {
|
|
|
45
45
|
onError?: (error: unknown) => void;
|
|
46
46
|
hideBackButtons?: boolean;
|
|
47
47
|
walletButtonRender?: WalletButtonRender;
|
|
48
|
+
prepareWalletPurchase?: WalletPurchasePrepare;
|
|
49
|
+
onWalletPurchaseClicked?: WalletPurchaseStart;
|
|
48
50
|
onInputChanged?: (fieldId: string, value: string, actionId?: string) => void;
|
|
49
51
|
/**
|
|
50
52
|
* Called for sensitive inputs (keyboard_type: "password") instead of
|
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import Stack from "../stack/Stack.svelte";
|
|
2
3
|
import { mapDimension } from "../stack/stack-utils";
|
|
3
4
|
import { getColorModeContext } from "../../stores/color-mode";
|
|
4
5
|
import { getPaywallContext } from "../../stores/paywall";
|
|
5
6
|
import type { WalletButtonProps } from "../../types/components/wallet-button";
|
|
6
7
|
import { css, mapSize, mapSpacing, px } from "../../utils/base-utils";
|
|
7
8
|
import { DEFAULT_SPACING } from "../../utils/constants";
|
|
9
|
+
import { onDestroy } from "svelte";
|
|
8
10
|
|
|
9
|
-
const {
|
|
10
|
-
|
|
11
|
+
const {
|
|
12
|
+
walletButtonRender,
|
|
13
|
+
onWalletButtonReady,
|
|
14
|
+
prepareWalletPurchase,
|
|
15
|
+
onWalletPurchaseClicked,
|
|
16
|
+
selectedPackageId,
|
|
17
|
+
emitComponentInteraction,
|
|
18
|
+
} = getPaywallContext();
|
|
11
19
|
const getColorMode = getColorModeContext();
|
|
12
20
|
const colorMode = $derived(getColorMode());
|
|
13
21
|
|
|
14
22
|
const props: WalletButtonProps = $props();
|
|
15
23
|
|
|
24
|
+
const mode = $derived(props.mode ?? "store");
|
|
25
|
+
const onReady = $derived(props.onWalletButtonReady ?? onWalletButtonReady);
|
|
26
|
+
|
|
16
27
|
const walletButtonTheme = $derived.by(() => {
|
|
17
28
|
const { style } = props;
|
|
18
29
|
if (style == null) {
|
|
@@ -65,9 +76,99 @@
|
|
|
65
76
|
});
|
|
66
77
|
|
|
67
78
|
const stackStyle = $derived(css(stackLayoutRules));
|
|
79
|
+
|
|
80
|
+
let busy = $state(false);
|
|
81
|
+
let prepared = $state(false);
|
|
82
|
+
const walletState = $derived(
|
|
83
|
+
busy ? "busy" : prepared ? "ready" : "preparing",
|
|
84
|
+
);
|
|
85
|
+
let prepareRequestId = 0;
|
|
86
|
+
let destroyed = false;
|
|
87
|
+
onDestroy(() => {
|
|
88
|
+
destroyed = true;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const runPrepare = (packageId: string) => {
|
|
92
|
+
const requestId = ++prepareRequestId;
|
|
93
|
+
if (!prepareWalletPurchase) {
|
|
94
|
+
onReady?.(false);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// A host that throws synchronously (rather than rejecting, as documented)
|
|
99
|
+
// must not skip the .catch below and leave onReady unreported.
|
|
100
|
+
Promise.resolve()
|
|
101
|
+
.then(() => prepareWalletPurchase(packageId))
|
|
102
|
+
.catch(() => false)
|
|
103
|
+
.then((available) => {
|
|
104
|
+
if (requestId !== prepareRequestId || destroyed) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
prepared = available;
|
|
108
|
+
onReady?.(available);
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const refreshPrepare = () => {
|
|
113
|
+
if (destroyed || mode !== "custom") {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
prepared = false;
|
|
117
|
+
if (!props.stack) {
|
|
118
|
+
onReady?.(false);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const packageId = $selectedPackageId;
|
|
123
|
+
if (!packageId) {
|
|
124
|
+
// Invalidate any in-flight prepare so its result can't mark this cleared
|
|
125
|
+
// selection ready.
|
|
126
|
+
prepareRequestId++;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
runPrepare(packageId);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
$effect(refreshPrepare);
|
|
134
|
+
|
|
135
|
+
const onclick = () => {
|
|
136
|
+
if (busy || !prepared || !onWalletPurchaseClicked) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const packageId = $selectedPackageId;
|
|
140
|
+
if (!packageId) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
busy = true;
|
|
144
|
+
prepared = false;
|
|
145
|
+
// Invoked synchronously (not deferred) to keep Safari's user activation for
|
|
146
|
+
// the wallet sheet; a synchronous throw here must still restore state below.
|
|
147
|
+
let purchase: ReturnType<typeof onWalletPurchaseClicked>;
|
|
148
|
+
try {
|
|
149
|
+
purchase = onWalletPurchaseClicked(packageId);
|
|
150
|
+
} catch {
|
|
151
|
+
busy = false;
|
|
152
|
+
refreshPrepare();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Attach cleanup before anything else can throw so a failing interaction
|
|
156
|
+
// handler cannot leave the button locked.
|
|
157
|
+
purchase
|
|
158
|
+
.catch(() => {})
|
|
159
|
+
.finally(() => {
|
|
160
|
+
busy = false;
|
|
161
|
+
refreshPrepare();
|
|
162
|
+
});
|
|
163
|
+
emitComponentInteraction({
|
|
164
|
+
componentType: "wallet_button",
|
|
165
|
+
componentName: props.name,
|
|
166
|
+
componentValue: "custom_purchase",
|
|
167
|
+
});
|
|
168
|
+
};
|
|
68
169
|
</script>
|
|
69
170
|
|
|
70
|
-
{#if walletButtonRender && $selectedPackageId}
|
|
171
|
+
{#if mode === "store" && walletButtonRender && $selectedPackageId}
|
|
71
172
|
<div style={stackStyle}>
|
|
72
173
|
{#key walletButtonTheme}
|
|
73
174
|
<div
|
|
@@ -80,4 +181,26 @@
|
|
|
80
181
|
></div>
|
|
81
182
|
{/key}
|
|
82
183
|
</div>
|
|
184
|
+
{:else if mode === "custom" && props.stack}
|
|
185
|
+
<div
|
|
186
|
+
class="custom-wallet-button"
|
|
187
|
+
style={stackStyle}
|
|
188
|
+
data-wallet-state={walletState}
|
|
189
|
+
aria-busy={busy}
|
|
190
|
+
aria-disabled={busy || !prepared}
|
|
191
|
+
>
|
|
192
|
+
<Stack {...props.stack} {onclick} testId="wallet-button-custom" />
|
|
193
|
+
</div>
|
|
83
194
|
{/if}
|
|
195
|
+
|
|
196
|
+
<style>
|
|
197
|
+
/* Default look for the not-ready states; hosts restyle via [data-wallet-state]. */
|
|
198
|
+
.custom-wallet-button:not([data-wallet-state="ready"]) {
|
|
199
|
+
opacity: 0.4;
|
|
200
|
+
cursor: not-allowed;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.custom-wallet-button:not([data-wallet-state="ready"]) > :global(*) {
|
|
204
|
+
pointer-events: none;
|
|
205
|
+
}
|
|
206
|
+
</style>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { setColorModeContext } from "../../stores/color-mode";
|
|
3
|
+
import { setPaywallContext } from "../../stores/paywall";
|
|
4
|
+
import type { WalletButtonProps } from "../../types/components/wallet-button";
|
|
5
|
+
import type { UIConfig } from "../../types/ui-config";
|
|
6
|
+
import type {
|
|
7
|
+
WalletButtonRender,
|
|
8
|
+
WalletPurchasePrepare,
|
|
9
|
+
WalletPurchaseStart,
|
|
10
|
+
} from "../../types/wallet";
|
|
11
|
+
import { readable, writable, type Writable } from "svelte/store";
|
|
12
|
+
import { untrack } from "svelte";
|
|
13
|
+
import WalletButton from "./WalletButton.svelte";
|
|
14
|
+
|
|
15
|
+
interface Props extends WalletButtonProps {
|
|
16
|
+
selectedPackageId?: Writable<string | undefined>;
|
|
17
|
+
walletButtonRender?: WalletButtonRender;
|
|
18
|
+
prepareWalletPurchase?: WalletPurchasePrepare;
|
|
19
|
+
onWalletPurchaseClicked?: WalletPurchaseStart;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
selectedPackageId = writable<string | undefined>("pkg-1"),
|
|
24
|
+
walletButtonRender,
|
|
25
|
+
prepareWalletPurchase,
|
|
26
|
+
onWalletPurchaseClicked,
|
|
27
|
+
...walletButtonProps
|
|
28
|
+
}: Props = $props();
|
|
29
|
+
|
|
30
|
+
setColorModeContext();
|
|
31
|
+
|
|
32
|
+
const uiConfig: UIConfig = { app: { fonts: {} } };
|
|
33
|
+
|
|
34
|
+
setPaywallContext(
|
|
35
|
+
untrack(() => ({
|
|
36
|
+
selectedPackageId,
|
|
37
|
+
variablesPerPackage: readable(undefined),
|
|
38
|
+
baseVariables: readable(undefined),
|
|
39
|
+
infoPerPackage: readable(undefined),
|
|
40
|
+
onPurchase: () => {},
|
|
41
|
+
emitComponentInteraction: () => {},
|
|
42
|
+
onButtonAction: () => {},
|
|
43
|
+
uiConfig,
|
|
44
|
+
hideBackButtons: false,
|
|
45
|
+
walletButtonRender,
|
|
46
|
+
prepareWalletPurchase,
|
|
47
|
+
onWalletPurchaseClicked,
|
|
48
|
+
})),
|
|
49
|
+
);
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<WalletButton {...walletButtonProps} />
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { WalletButtonProps } from "../../types/components/wallet-button";
|
|
2
|
+
import type { WalletButtonRender, WalletPurchasePrepare, WalletPurchaseStart } from "../../types/wallet";
|
|
3
|
+
import { type Writable } from "svelte/store";
|
|
4
|
+
interface Props extends WalletButtonProps {
|
|
5
|
+
selectedPackageId?: Writable<string | undefined>;
|
|
6
|
+
walletButtonRender?: WalletButtonRender;
|
|
7
|
+
prepareWalletPurchase?: WalletPurchasePrepare;
|
|
8
|
+
onWalletPurchaseClicked?: WalletPurchaseStart;
|
|
9
|
+
}
|
|
10
|
+
declare const WalletButtonTestWrapper: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type WalletButtonTestWrapper = ReturnType<typeof WalletButtonTestWrapper>;
|
|
12
|
+
export default WalletButtonTestWrapper;
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
VariableDictionary,
|
|
11
11
|
CustomVariables,
|
|
12
12
|
} from "../../types/variables";
|
|
13
|
-
import type {
|
|
13
|
+
import type {
|
|
14
|
+
WalletButtonRender,
|
|
15
|
+
WalletPurchasePrepare,
|
|
16
|
+
WalletPurchaseStart,
|
|
17
|
+
} from "../../types/wallet";
|
|
14
18
|
import type { UIConfig } from "../../types/ui-config";
|
|
15
19
|
import type { ReservedAttribute } from "../../types/components/input-text";
|
|
16
20
|
import type {
|
|
@@ -58,6 +62,8 @@
|
|
|
58
62
|
onRestorePurchasesClicked?: () => void;
|
|
59
63
|
onVisitCustomerCenterClicked?: () => void;
|
|
60
64
|
walletButtonRender?: WalletButtonRender;
|
|
65
|
+
prepareWalletPurchase?: WalletPurchasePrepare;
|
|
66
|
+
onWalletPurchaseClicked?: WalletPurchaseStart;
|
|
61
67
|
safeAreaFallbackColor?: ColorScheme | null;
|
|
62
68
|
hideBackButtons?: boolean;
|
|
63
69
|
offering?: PaywallOffering | null;
|
|
@@ -90,6 +96,8 @@
|
|
|
90
96
|
onRestorePurchasesClicked,
|
|
91
97
|
onVisitCustomerCenterClicked,
|
|
92
98
|
walletButtonRender,
|
|
99
|
+
prepareWalletPurchase,
|
|
100
|
+
onWalletPurchaseClicked,
|
|
93
101
|
safeAreaFallbackColor,
|
|
94
102
|
hideBackButtons,
|
|
95
103
|
offering,
|
|
@@ -137,6 +145,8 @@
|
|
|
137
145
|
{onSensitiveInputChanged}
|
|
138
146
|
{onReservedAttributeChanged}
|
|
139
147
|
{walletButtonRender}
|
|
148
|
+
{prepareWalletPurchase}
|
|
149
|
+
{onWalletPurchaseClicked}
|
|
140
150
|
{safeAreaFallbackColor}
|
|
141
151
|
{hideBackButtons}
|
|
142
152
|
{offering}
|
|
@@ -4,7 +4,7 @@ import type { InitialInputSelections } from "../../stores/inputValidation";
|
|
|
4
4
|
import type { OnComponentInteraction } from "../../types/paywall-component-interaction";
|
|
5
5
|
import type { WorkflowScreen } from "../../types/workflow";
|
|
6
6
|
import type { PackageInfo, VariableDictionary, CustomVariables } from "../../types/variables";
|
|
7
|
-
import type { WalletButtonRender } from "../../types/wallet";
|
|
7
|
+
import type { WalletButtonRender, WalletPurchasePrepare, WalletPurchaseStart } from "../../types/wallet";
|
|
8
8
|
import type { UIConfig } from "../../types/ui-config";
|
|
9
9
|
import type { ReservedAttribute } from "../../types/components/input-text";
|
|
10
10
|
import type { PaywallOffering, PaywallPackage, PaywallWorkflow } from "@revenuecat/workflow-web-components-sdk";
|
|
@@ -36,6 +36,8 @@ interface Props {
|
|
|
36
36
|
onRestorePurchasesClicked?: () => void;
|
|
37
37
|
onVisitCustomerCenterClicked?: () => void;
|
|
38
38
|
walletButtonRender?: WalletButtonRender;
|
|
39
|
+
prepareWalletPurchase?: WalletPurchasePrepare;
|
|
40
|
+
onWalletPurchaseClicked?: WalletPurchaseStart;
|
|
39
41
|
safeAreaFallbackColor?: ColorScheme | null;
|
|
40
42
|
hideBackButtons?: boolean;
|
|
41
43
|
offering?: PaywallOffering | null;
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
VariableDictionary,
|
|
11
11
|
CustomVariables,
|
|
12
12
|
} from "../../types/variables";
|
|
13
|
-
import type {
|
|
13
|
+
import type {
|
|
14
|
+
WalletButtonRender,
|
|
15
|
+
WalletPurchasePrepare,
|
|
16
|
+
WalletPurchaseStart,
|
|
17
|
+
} from "../../types/wallet";
|
|
14
18
|
import type { UIConfig } from "../../types/ui-config";
|
|
15
19
|
import type { ReservedAttribute } from "../../types/components/input-text";
|
|
16
20
|
import type {
|
|
@@ -51,6 +55,8 @@
|
|
|
51
55
|
initialInputSelections?: InitialInputSelections;
|
|
52
56
|
containerId?: string;
|
|
53
57
|
walletButtonRender?: WalletButtonRender;
|
|
58
|
+
prepareWalletPurchase?: WalletPurchasePrepare;
|
|
59
|
+
onWalletPurchaseClicked?: WalletPurchaseStart;
|
|
54
60
|
safeAreaFallbackColor?: ColorScheme | null;
|
|
55
61
|
hideBackButtons?: boolean;
|
|
56
62
|
onPurchaseClicked?: (
|
|
@@ -105,6 +111,8 @@
|
|
|
105
111
|
initialInputSelections,
|
|
106
112
|
containerId,
|
|
107
113
|
walletButtonRender,
|
|
114
|
+
prepareWalletPurchase,
|
|
115
|
+
onWalletPurchaseClicked,
|
|
108
116
|
safeAreaFallbackColor,
|
|
109
117
|
hideBackButtons,
|
|
110
118
|
onPurchaseClicked,
|
|
@@ -368,6 +376,8 @@
|
|
|
368
376
|
{initialInputSelections}
|
|
369
377
|
{containerId}
|
|
370
378
|
{walletButtonRender}
|
|
379
|
+
{prepareWalletPurchase}
|
|
380
|
+
{onWalletPurchaseClicked}
|
|
371
381
|
{safeAreaFallbackColor}
|
|
372
382
|
{hideBackButtons}
|
|
373
383
|
{onPurchaseClicked}
|
|
@@ -3,7 +3,7 @@ import type { ColorScheme } from "../../types/colors";
|
|
|
3
3
|
import type { InitialInputSelections } from "../../stores/inputValidation";
|
|
4
4
|
import type { OnComponentInteraction } from "../../types/paywall-component-interaction";
|
|
5
5
|
import type { PackageInfo, VariableDictionary, CustomVariables } from "../../types/variables";
|
|
6
|
-
import type { WalletButtonRender } from "../../types/wallet";
|
|
6
|
+
import type { WalletButtonRender, WalletPurchasePrepare, WalletPurchaseStart } from "../../types/wallet";
|
|
7
7
|
import type { UIConfig } from "../../types/ui-config";
|
|
8
8
|
import type { ReservedAttribute } from "../../types/components/input-text";
|
|
9
9
|
import type { WorkflowNavData, WorkflowStepChangeEvent } from "../../types/workflow-nav";
|
|
@@ -21,6 +21,8 @@ interface Props {
|
|
|
21
21
|
initialInputSelections?: InitialInputSelections;
|
|
22
22
|
containerId?: string;
|
|
23
23
|
walletButtonRender?: WalletButtonRender;
|
|
24
|
+
prepareWalletPurchase?: WalletPurchasePrepare;
|
|
25
|
+
onWalletPurchaseClicked?: WalletPurchaseStart;
|
|
24
26
|
safeAreaFallbackColor?: ColorScheme | null;
|
|
25
27
|
hideBackButtons?: boolean;
|
|
26
28
|
onPurchaseClicked?: (packageId: string, actionId: string) => void | Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export { type WorkflowData, type WorkflowStep } from "./types/workflow";
|
|
|
30
30
|
export { type InitialInputSelections } from "./stores/inputValidation";
|
|
31
31
|
export { type UIConfig } from "./types/ui-config";
|
|
32
32
|
export type { PaywallContext, PaywallOffering, PaywallPackage, PaywallWorkflow, } from "@revenuecat/workflow-web-components-sdk";
|
|
33
|
-
export { type WalletButtonRender, type WalletButtonTheme, } from "./types/wallet";
|
|
33
|
+
export { type WalletButtonRender, type WalletButtonTheme, type WalletPurchasePrepare, type WalletPurchaseStart, } from "./types/wallet";
|
|
34
34
|
export { type CustomVariables, CustomVariableValue, mergeCustomVariables, type VariableDictionary, type PackageInfo, } from "./types/variables";
|
|
35
35
|
export type { Action, CompleteWorkflowNavigateArgs, CompleteWorkflowUrlQueryParams, } from "./types/components/button";
|
|
36
36
|
export type { LoaderProps } from "./types/components/loader";
|
package/dist/stores/paywall.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { ReservedAttribute } from "../types/components/input-text";
|
|
|
3
3
|
import type { ComponentInteractionData } from "../types/paywall-component-interaction";
|
|
4
4
|
import type { UIConfig } from "../types/ui-config";
|
|
5
5
|
import type { PackageInfo, VariableDictionary } from "../types/variables";
|
|
6
|
-
import type { WalletButtonRender } from "../types/wallet";
|
|
6
|
+
import type { WalletButtonRender, WalletPurchasePrepare, WalletPurchaseStart } from "../types/wallet";
|
|
7
7
|
import type { PaywallOffering, PaywallPackage, PaywallWorkflow } from "@revenuecat/workflow-web-components-sdk";
|
|
8
8
|
import { type Readable, type Writable } from "svelte/store";
|
|
9
9
|
import type { BridgeCustomValues, BridgeInputs } from "../components/web-view/build-paywall-context";
|
|
@@ -44,6 +44,8 @@ type PaywallContext = Readonly<{
|
|
|
44
44
|
onReservedAttributeChanged?: (reservedAttribute: ReservedAttribute, value: string) => void;
|
|
45
45
|
walletButtonRender?: WalletButtonRender;
|
|
46
46
|
onWalletButtonReady?: (walletButtonAvailable?: boolean) => void;
|
|
47
|
+
prepareWalletPurchase?: WalletPurchasePrepare;
|
|
48
|
+
onWalletPurchaseClicked?: WalletPurchaseStart;
|
|
47
49
|
onButtonAction: (action: Action, actionId?: string) => void;
|
|
48
50
|
uiConfig: UIConfig;
|
|
49
51
|
hideBackButtons: boolean;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { WalletButtonRender } from "../types/wallet";
|
|
1
|
+
import type { WalletButtonRender, WalletPurchasePrepare, WalletPurchaseStart } from "../types/wallet";
|
|
2
2
|
import type { DecoratorFunction, Renderer } from "storybook/internal/csf";
|
|
3
|
-
export declare function paywallDecorator<TRenderer extends Renderer, TArgs>(walletButtonRender?: WalletButtonRender, selectedPackageId?: string): DecoratorFunction<TRenderer, TArgs>;
|
|
3
|
+
export declare function paywallDecorator<TRenderer extends Renderer, TArgs>(walletButtonRender?: WalletButtonRender, selectedPackageId?: string, prepareWalletPurchase?: WalletPurchasePrepare, onWalletPurchaseClicked?: WalletPurchaseStart): DecoratorFunction<TRenderer, TArgs>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { setPaywallContext } from "../stores/paywall";
|
|
2
2
|
import { readable, writable } from "svelte/store";
|
|
3
|
-
export function paywallDecorator(walletButtonRender, selectedPackageId) {
|
|
3
|
+
export function paywallDecorator(walletButtonRender, selectedPackageId, prepareWalletPurchase, onWalletPurchaseClicked) {
|
|
4
4
|
return (Story) => {
|
|
5
5
|
const selectedPackageIdWritable = writable(selectedPackageId);
|
|
6
6
|
selectedPackageIdWritable.subscribe((packageId) => {
|
|
@@ -24,6 +24,8 @@ export function paywallDecorator(walletButtonRender, selectedPackageId) {
|
|
|
24
24
|
onButtonAction: (action, actionId) => window.alert(`Button clicked: ${JSON.stringify({ action, actionId }, undefined, 2)}`),
|
|
25
25
|
hideBackButtons: false,
|
|
26
26
|
walletButtonRender,
|
|
27
|
+
prepareWalletPurchase,
|
|
28
|
+
onWalletPurchaseClicked,
|
|
27
29
|
uiConfig: emptyUiConfig,
|
|
28
30
|
});
|
|
29
31
|
return Story();
|
|
@@ -2,10 +2,12 @@ import type { BaseComponent } from "../base";
|
|
|
2
2
|
import type { BorderType, ShadowType, ShapeType, SizeType, Spacing } from "../..";
|
|
3
3
|
import type { Dimension } from "../alignment";
|
|
4
4
|
import type { WalletButtonTheme } from "../wallet";
|
|
5
|
+
import type { StackProps } from "./stack";
|
|
5
6
|
export interface WalletButtonStyle {
|
|
6
7
|
light: WalletButtonTheme;
|
|
7
8
|
dark?: WalletButtonTheme | null;
|
|
8
9
|
}
|
|
10
|
+
export type WalletButtonMode = "store" | "custom";
|
|
9
11
|
export interface WalletButtonProps extends BaseComponent {
|
|
10
12
|
type: "wallet_button";
|
|
11
13
|
size?: SizeType;
|
|
@@ -17,4 +19,6 @@ export interface WalletButtonProps extends BaseComponent {
|
|
|
17
19
|
style?: WalletButtonStyle | null;
|
|
18
20
|
onWalletButtonReady?: (walletButtonAvailable?: boolean) => void;
|
|
19
21
|
shadow?: ShadowType | null;
|
|
22
|
+
mode?: WalletButtonMode | null;
|
|
23
|
+
stack?: StackProps | null;
|
|
20
24
|
}
|
|
@@ -44,7 +44,8 @@ export type PackageSelectionSheetInteractionData = ComponentInteractionBase<"pac
|
|
|
44
44
|
currentProductId?: string;
|
|
45
45
|
resultingProductId?: string;
|
|
46
46
|
};
|
|
47
|
-
export type
|
|
47
|
+
export type WalletButtonInteractionData = ComponentInteractionBase<"wallet_button", "custom_purchase">;
|
|
48
|
+
export type ComponentInteractionData = TabInteractionData | SwitchInteractionData | CarouselInteractionData | ButtonInteractionData | TextInteractionData | PackageInteractionData | PurchaseButtonInteractionData | PackageSelectionSheetInteractionData | WalletButtonInteractionData;
|
|
48
49
|
export type ComponentInteractionType = ComponentInteractionData["componentType"];
|
|
49
50
|
export type OnComponentInteraction = (data: ComponentInteractionData) => void;
|
|
50
51
|
export {};
|
package/dist/types/wallet.d.ts
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* @default "black"
|
|
5
5
|
*/
|
|
6
6
|
export type WalletButtonTheme = "black" | "white" | "white-outline";
|
|
7
|
+
/** Host checks whether the wallet purchase path is usable for this package. Resolves false on any error. */
|
|
8
|
+
export type WalletPurchasePrepare = (selectedPackageId: string) => Promise<boolean>;
|
|
9
|
+
/** Host starts the wallet purchase. Must be invoked synchronously from the click so Safari keeps the user activation. Resolves when the attempt settles (success, cancel, or error). */
|
|
10
|
+
export type WalletPurchaseStart = (selectedPackageId: string) => Promise<void>;
|
|
7
11
|
export type WalletButtonRender = (node: HTMLElement, params: {
|
|
8
12
|
selectedPackageId: string;
|
|
9
13
|
onReady?: (walletButtonAvailable?: boolean) => void;
|
|
@@ -41,9 +41,9 @@ export declare const filterMatchingOverrides: <T extends Component>(context: Con
|
|
|
41
41
|
* evaluate from the context flags, so mixed pseudo + rule/state overrides get
|
|
42
42
|
* the same all-conditions-AND-ed semantics as iOS's buildPartial.
|
|
43
43
|
*
|
|
44
|
-
*
|
|
45
|
-
* component
|
|
46
|
-
* Components derive `isVisible` from the merged result
|
|
44
|
+
* An override may both hide (`visible: false`) and reveal (`visible: true`) a
|
|
45
|
+
* component, regardless of which condition types it carries, matching iOS's
|
|
46
|
+
* buildPartial. Components derive `isVisible` from the merged result
|
|
47
47
|
* (`actualProps.visible !== false`).
|
|
48
48
|
*/
|
|
49
49
|
export declare const resolveOverrideProperties: <T extends Component>(context: ConditionContext, overrides?: Overrides<T>) => Partial<T>;
|
|
@@ -131,19 +131,17 @@ export const filterMatchingOverrides = (context, overrides) => {
|
|
|
131
131
|
* evaluate from the context flags, so mixed pseudo + rule/state overrides get
|
|
132
132
|
* the same all-conditions-AND-ed semantics as iOS's buildPartial.
|
|
133
133
|
*
|
|
134
|
-
*
|
|
135
|
-
* component
|
|
136
|
-
* Components derive `isVisible` from the merged result
|
|
134
|
+
* An override may both hide (`visible: false`) and reveal (`visible: true`) a
|
|
135
|
+
* component, regardless of which condition types it carries, matching iOS's
|
|
136
|
+
* buildPartial. Components derive `isVisible` from the merged result
|
|
137
137
|
* (`actualProps.visible !== false`).
|
|
138
138
|
*/
|
|
139
139
|
export const resolveOverrideProperties = (context, overrides) => {
|
|
140
140
|
let merged = {};
|
|
141
141
|
for (const override of filterMatchingOverrides(context, overrides)) {
|
|
142
142
|
const properties = { ...override.properties };
|
|
143
|
-
const canReveal = override.conditions.some((c) => c.type === RuleKey.StateCondition);
|
|
144
143
|
// visible: null carries no intent and must not clobber an earlier false.
|
|
145
|
-
if (properties.visible === null
|
|
146
|
-
(properties.visible === true && !canReveal)) {
|
|
144
|
+
if (properties.visible === null) {
|
|
147
145
|
delete properties.visible;
|
|
148
146
|
}
|
|
149
147
|
merged = { ...merged, ...properties };
|