@frak-labs/components 1.0.2 → 1.0.3-beta.64c76510
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/cdn/Banner.BaYEToC4.js +170 -0
- package/cdn/ButtonShare.BMkk39K_.js +1 -0
- package/cdn/{ButtonWallet.Cwz9qFhE.js → ButtonWallet.DT6bWcXS.js} +1 -1
- package/cdn/{OpenInAppButton.Hq9EjwJE.js → OpenInAppButton.CBzl8Rgk.js} +1 -1
- package/cdn/PostPurchase.Bo_QLHvi.js +51 -0
- package/cdn/components.js +1 -1
- package/cdn/{formatReward.BaR9pE50.js → formatReward.CEAF0Lox.js} +1 -1
- package/cdn/loader.js +2 -2
- package/cdn/sharingPage.Do_xfrTN.js +1 -0
- package/cdn/{sprinkles.css.ts.vanilla.Ct795MMK.js → sprinkles.css.ts.vanilla.eIpk1s3W.js} +1 -1
- package/cdn/useGlobalComponents.UJmjUUxk.js +1 -0
- package/cdn/{useLightDomStyles.DqYouFn3.js → useLightDomStyles.Gt7YUMDl.js} +1 -1
- package/cdn/{usePlacement.Di6eZ4ty.js → usePlacement.BJ7qe-pw.js} +1 -1
- package/cdn/{useReward.DWyyva4u.js → useReward.Bn1RVE3i.js} +1 -1
- package/dist/{GiftIcon-4sr9xXyq.js → GiftIcon-2DC0OUeF.js} +33 -16
- package/dist/banner.js +41 -24
- package/dist/buttonShare.d.ts +9 -4
- package/dist/buttonShare.js +13 -177
- package/dist/openInApp.js +1 -1
- package/dist/postPurchase.d.ts +17 -1
- package/dist/postPurchase.js +99 -20
- package/dist/sharingPage-DFvQbviS.js +15 -0
- package/dist/useLightDomStyles-gbuSWvRx.js +89 -0
- package/package.json +3 -3
- package/cdn/Banner.1iUbfe7Z.js +0 -162
- package/cdn/ButtonShare.APhrT3sb.js +0 -1
- package/cdn/PostPurchase.gHQmp1c4.js +0 -52
- package/cdn/useGlobalComponents.CLH7id-Y.js +0 -1
- package/cdn/useShareModal.pszXJ-rf.js +0 -1
- package/dist/useLightDomStyles-C3lcOwY2.js +0 -41
- package/dist/useShareModal-BEVkLrBP.js +0 -54
package/dist/banner.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { a as registerWebComponent, i as useClientReady, t as usePlacement } from "./usePlacement-V7NrKoub.js";
|
|
2
2
|
import { t as useGlobalComponents } from "./useGlobalComponents-Cmfszr7v.js";
|
|
3
|
-
import { t as useLightDomStyles } from "./useLightDomStyles-
|
|
3
|
+
import { t as useLightDomStyles } from "./useLightDomStyles-gbuSWvRx.js";
|
|
4
4
|
import { t as useReward } from "./useReward-DU3_yP8Q.js";
|
|
5
|
-
import { a as CloseCircleIcon, c as cssSource$5, i as ExternalLinkIcon, n as WarningIcon, o as cssSource$6, s as cssSource$4, t as GiftIcon } from "./GiftIcon-
|
|
5
|
+
import { a as CloseCircleIcon, c as cssSource$5, i as ExternalLinkIcon, n as WarningIcon, o as cssSource$6, r as LogoFrakWithName, s as cssSource$4, t as GiftIcon } from "./GiftIcon-2DC0OUeF.js";
|
|
6
6
|
import { isInAppBrowser, redirectToExternalBrowser, trackEvent } from "@frak-labs/core-sdk";
|
|
7
7
|
import { REFERRAL_SUCCESS_EVENT, getMergeToken } from "@frak-labs/core-sdk/actions";
|
|
8
8
|
import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
@@ -194,6 +194,8 @@ const cssSource$1 = `@keyframes Banner_fadeIn__1gnumzi0 {
|
|
|
194
194
|
padding: 16px;
|
|
195
195
|
background-color: #ffffff;
|
|
196
196
|
color: var(--text-primary__pbq4ak0);
|
|
197
|
+
border: 1px solid var(--border-default__pbq4akv);
|
|
198
|
+
border-radius: 12px;
|
|
197
199
|
}
|
|
198
200
|
.Banner_referralIconWrapper__1gnumzi4 {
|
|
199
201
|
flex-shrink: 0;
|
|
@@ -231,9 +233,16 @@ const cssSource$1 = `@keyframes Banner_fadeIn__1gnumzi0 {
|
|
|
231
233
|
.Banner_referralCta__1gnumzi8:focus-visible {
|
|
232
234
|
outline: 2px solid #000000;
|
|
233
235
|
outline-offset: 2px;
|
|
236
|
+
}
|
|
237
|
+
.Banner_frakLogo__1gnumzi9 {
|
|
238
|
+
position: absolute;
|
|
239
|
+
right: 16px;
|
|
240
|
+
bottom: 12px;
|
|
241
|
+
pointer-events: none;
|
|
234
242
|
}`;
|
|
235
243
|
//#endregion
|
|
236
244
|
//#region src/components/Banner/Banner.css.ts
|
|
245
|
+
var frakLogo = "Banner_frakLogo__1gnumzi9";
|
|
237
246
|
var iconSvg = "Banner_iconSvg__1gnumzi2";
|
|
238
247
|
var referral = "Banner_referral__1gnumzi3 reset_base__1831jhd0 Banner_rootBase__1gnumzi1";
|
|
239
248
|
var referralBody = "Banner_referralBody__1gnumzi5";
|
|
@@ -421,28 +430,36 @@ function Banner({ placement: placementId, classname = "", interaction, referralT
|
|
|
421
430
|
return /* @__PURE__ */ jsxs("div", {
|
|
422
431
|
class: bannerClass,
|
|
423
432
|
role: "alert",
|
|
424
|
-
children: [
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
433
|
+
children: [
|
|
434
|
+
/* @__PURE__ */ jsx("div", {
|
|
435
|
+
class: `${referralIconWrapper} frak-banner__icon`,
|
|
436
|
+
children: /* @__PURE__ */ jsx(GiftIcon, { class: iconSvg })
|
|
437
|
+
}),
|
|
438
|
+
/* @__PURE__ */ jsxs("div", {
|
|
439
|
+
class: `${referralBody} frak-banner__text`,
|
|
440
|
+
children: [
|
|
441
|
+
/* @__PURE__ */ jsx("p", {
|
|
442
|
+
class: `${referralTitle} frak-banner__title`,
|
|
443
|
+
children: texts.title
|
|
444
|
+
}),
|
|
445
|
+
/* @__PURE__ */ jsx("p", {
|
|
446
|
+
class: `${referralDescription} frak-banner__description`,
|
|
447
|
+
children: texts.description
|
|
448
|
+
}),
|
|
449
|
+
/* @__PURE__ */ jsx("button", {
|
|
450
|
+
type: "button",
|
|
451
|
+
class: `${referralCta} frak-banner__cta`,
|
|
452
|
+
onClick: handleAction,
|
|
453
|
+
children: texts.cta
|
|
454
|
+
})
|
|
455
|
+
]
|
|
456
|
+
}),
|
|
457
|
+
/* @__PURE__ */ jsx(LogoFrakWithName, {
|
|
458
|
+
class: `${frakLogo} frak-banner__logo`,
|
|
459
|
+
width: 42,
|
|
460
|
+
height: 24
|
|
461
|
+
})
|
|
462
|
+
]
|
|
446
463
|
});
|
|
447
464
|
}
|
|
448
465
|
//#endregion
|
package/dist/buttonShare.d.ts
CHANGED
|
@@ -32,10 +32,15 @@ type ButtonShareProps = {
|
|
|
32
32
|
*/
|
|
33
33
|
targetInteraction?: InteractionTypeKey;
|
|
34
34
|
/**
|
|
35
|
-
* Which UI to open on click
|
|
36
|
-
*
|
|
35
|
+
* Which UI to open on click.
|
|
36
|
+
*
|
|
37
|
+
* Legacy values (e.g. `"share-modal"`) are accepted at runtime and
|
|
38
|
+
* gracefully route to the full-page sharing UI — the modal-flow
|
|
39
|
+
* share path was retired in favour of `displaySharingPage`.
|
|
40
|
+
*
|
|
41
|
+
* @defaultValue `"sharing-page"`
|
|
37
42
|
*/
|
|
38
|
-
clickAction?: "embedded-wallet" | "
|
|
43
|
+
clickAction?: "embedded-wallet" | "sharing-page";
|
|
39
44
|
/**
|
|
40
45
|
* When set, renders the button in preview mode (e.g. Shopify/WP editor).
|
|
41
46
|
* Skips the client-ready gating so the button is always enabled visually,
|
|
@@ -84,7 +89,7 @@ type ButtonShareProps = {
|
|
|
84
89
|
* <frak-button-share use-reward text="Share and earn up to {REWARD}!" no-reward-text="Share and earn!" target-interaction="custom.customerMeeting"></frak-button-share>
|
|
85
90
|
* ```
|
|
86
91
|
*
|
|
87
|
-
* @see {@link @frak-labs/core-sdk!actions.
|
|
92
|
+
* @see {@link @frak-labs/core-sdk!actions.displaySharingPage | `displaySharingPage()`} for more info about the sharing-page flow
|
|
88
93
|
* @see {@link @frak-labs/core-sdk!actions.getMerchantInformation | `getMerchantInformation()`} for more info about the estimated reward fetching
|
|
89
94
|
*/
|
|
90
95
|
declare function ButtonShare({
|
package/dist/buttonShare.js
CHANGED
|
@@ -1,176 +1,12 @@
|
|
|
1
1
|
import { a as registerWebComponent, i as useClientReady, s as openEmbeddedWallet, t as usePlacement } from "./usePlacement-V7NrKoub.js";
|
|
2
2
|
import { t as useGlobalComponents } from "./useGlobalComponents-Cmfszr7v.js";
|
|
3
|
-
import { t as useLightDomStyles } from "./useLightDomStyles-
|
|
3
|
+
import { t as useLightDomStyles } from "./useLightDomStyles-gbuSWvRx.js";
|
|
4
4
|
import { t as applyRewardPlaceholder } from "./formatReward-Bub6Z6eY.js";
|
|
5
5
|
import { t as useReward } from "./useReward-DU3_yP8Q.js";
|
|
6
|
-
import { t as
|
|
6
|
+
import { t as openSharingPage } from "./sharingPage-DFvQbviS.js";
|
|
7
7
|
import { trackEvent } from "@frak-labs/core-sdk";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
|
|
11
|
-
//#region src/utils/sharingPage.ts
|
|
12
|
-
async function openSharingPage(targetInteraction, placement) {
|
|
13
|
-
if (!window.FrakSetup?.client) {
|
|
14
|
-
console.error("Frak client not found");
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
await displaySharingPage(window.FrakSetup.client, { metadata: { ...targetInteraction && { targetInteraction } } }, placement);
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
//#region src/hooks/useCopyToClipboard.ts
|
|
21
|
-
function useCopyToClipboard(options = {}) {
|
|
22
|
-
const { successDuration = 2e3 } = options;
|
|
23
|
-
const [copied, setCopied] = useState(false);
|
|
24
|
-
return {
|
|
25
|
-
copy: useCallback(async (text) => {
|
|
26
|
-
try {
|
|
27
|
-
if (navigator.clipboard && window.isSecureContext) {
|
|
28
|
-
await navigator.clipboard.writeText(text);
|
|
29
|
-
setCopied(true);
|
|
30
|
-
} else {
|
|
31
|
-
const textArea = document.createElement("textarea");
|
|
32
|
-
textArea.value = text;
|
|
33
|
-
textArea.style.position = "fixed";
|
|
34
|
-
textArea.style.opacity = "0";
|
|
35
|
-
document.body.appendChild(textArea);
|
|
36
|
-
textArea.focus();
|
|
37
|
-
textArea.select();
|
|
38
|
-
try {
|
|
39
|
-
document.execCommand("copy");
|
|
40
|
-
setCopied(true);
|
|
41
|
-
} catch (err) {
|
|
42
|
-
console.error("Failed to copy text:", err);
|
|
43
|
-
return false;
|
|
44
|
-
} finally {
|
|
45
|
-
textArea.remove();
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
setTimeout(() => {
|
|
49
|
-
setCopied(false);
|
|
50
|
-
}, successDuration);
|
|
51
|
-
return true;
|
|
52
|
-
} catch (err) {
|
|
53
|
-
console.error("Failed to copy text:", err);
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
}, [successDuration]),
|
|
57
|
-
copied
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
//#endregion
|
|
61
|
-
//#region src/components/ButtonShare/components/ErrorMessage.tsx
|
|
62
|
-
const styles = {
|
|
63
|
-
errorContainer: {
|
|
64
|
-
marginTop: "16px",
|
|
65
|
-
padding: "16px",
|
|
66
|
-
backgroundColor: "#FEE2E2",
|
|
67
|
-
border: "1px solid #FCA5A5",
|
|
68
|
-
borderRadius: "4px",
|
|
69
|
-
color: "#991B1B"
|
|
70
|
-
},
|
|
71
|
-
header: {
|
|
72
|
-
display: "flex",
|
|
73
|
-
alignItems: "center",
|
|
74
|
-
gap: "8px",
|
|
75
|
-
marginBottom: "12px"
|
|
76
|
-
},
|
|
77
|
-
title: {
|
|
78
|
-
margin: 0,
|
|
79
|
-
fontSize: "16px",
|
|
80
|
-
fontWeight: 500
|
|
81
|
-
},
|
|
82
|
-
message: {
|
|
83
|
-
fontSize: "14px",
|
|
84
|
-
lineHeight: "1.5",
|
|
85
|
-
margin: "0 0 12px 0"
|
|
86
|
-
},
|
|
87
|
-
link: {
|
|
88
|
-
color: "#991B1B",
|
|
89
|
-
textDecoration: "underline",
|
|
90
|
-
textUnderlineOffset: "2px"
|
|
91
|
-
},
|
|
92
|
-
copyButton: {
|
|
93
|
-
display: "inline-flex",
|
|
94
|
-
alignItems: "center",
|
|
95
|
-
gap: "8px",
|
|
96
|
-
marginBottom: "10px",
|
|
97
|
-
padding: "8px 12px",
|
|
98
|
-
backgroundColor: "white",
|
|
99
|
-
border: "1px solid #D1D5DB",
|
|
100
|
-
borderRadius: "4px",
|
|
101
|
-
color: "black",
|
|
102
|
-
fontSize: "14px",
|
|
103
|
-
fontWeight: 500
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
/**
|
|
107
|
-
* Renders a toggleable debug information section
|
|
108
|
-
* @param {Object} props - Component props
|
|
109
|
-
* @param {string} [props.debugInfo] - Debug information to display in textarea
|
|
110
|
-
*/
|
|
111
|
-
function ToggleMessage({ debugInfo }) {
|
|
112
|
-
const [showInfo, setShowInfo] = useState(false);
|
|
113
|
-
return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("button", {
|
|
114
|
-
type: "button",
|
|
115
|
-
style: styles.copyButton,
|
|
116
|
-
onClick: () => setShowInfo(!showInfo),
|
|
117
|
-
children: "Ouvrir les informations"
|
|
118
|
-
}), showInfo && /* @__PURE__ */ jsx("textarea", {
|
|
119
|
-
style: {
|
|
120
|
-
display: "block",
|
|
121
|
-
width: "100%",
|
|
122
|
-
height: "200px",
|
|
123
|
-
fontSize: "12px"
|
|
124
|
-
},
|
|
125
|
-
children: debugInfo
|
|
126
|
-
})] });
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Displays an error message with debug information and copy functionality
|
|
130
|
-
* @param {Object} props - Component props
|
|
131
|
-
* @param {string} [props.debugInfo] - Debug information that can be copied or displayed
|
|
132
|
-
*/
|
|
133
|
-
function ErrorMessage({ debugInfo }) {
|
|
134
|
-
const { copied, copy } = useCopyToClipboard();
|
|
135
|
-
const handleCopy = () => {
|
|
136
|
-
copy(debugInfo ?? "");
|
|
137
|
-
};
|
|
138
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
139
|
-
style: styles.errorContainer,
|
|
140
|
-
children: [
|
|
141
|
-
/* @__PURE__ */ jsx("div", {
|
|
142
|
-
style: styles.header,
|
|
143
|
-
children: /* @__PURE__ */ jsx("h3", {
|
|
144
|
-
style: styles.title,
|
|
145
|
-
children: "Oups ! Nous avons rencontré un petit problème"
|
|
146
|
-
})
|
|
147
|
-
}),
|
|
148
|
-
/* @__PURE__ */ jsxs("p", {
|
|
149
|
-
style: styles.message,
|
|
150
|
-
children: [
|
|
151
|
-
"Impossible d'ouvrir le menu de partage pour le moment. Si le problème persiste, copiez les informations ci-dessous et collez-les dans votre mail à",
|
|
152
|
-
" ",
|
|
153
|
-
/* @__PURE__ */ jsx("a", {
|
|
154
|
-
href: "mailto:help@frak-labs.com?subject=Debug",
|
|
155
|
-
style: styles.link,
|
|
156
|
-
children: "help@frak-labs.com"
|
|
157
|
-
}),
|
|
158
|
-
" ",
|
|
159
|
-
/* @__PURE__ */ jsx("br", {}),
|
|
160
|
-
"Merci pour votre retour, nous traitons votre demande dans les plus brefs délais."
|
|
161
|
-
]
|
|
162
|
-
}),
|
|
163
|
-
/* @__PURE__ */ jsx("button", {
|
|
164
|
-
type: "button",
|
|
165
|
-
onClick: handleCopy,
|
|
166
|
-
style: styles.copyButton,
|
|
167
|
-
children: copied ? "Informations copiées !" : "Copier les informations de débogage"
|
|
168
|
-
}),
|
|
169
|
-
/* @__PURE__ */ jsx(ToggleMessage, { debugInfo })
|
|
170
|
-
]
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
//#endregion
|
|
8
|
+
import { useCallback, useMemo } from "preact/hooks";
|
|
9
|
+
import { jsx } from "preact/jsx-runtime";
|
|
174
10
|
//#region src/components/ButtonShare/ButtonShare.tsx
|
|
175
11
|
/**
|
|
176
12
|
* Button to share the current page
|
|
@@ -210,7 +46,7 @@ function ErrorMessage({ debugInfo }) {
|
|
|
210
46
|
* <frak-button-share use-reward text="Share and earn up to {REWARD}!" no-reward-text="Share and earn!" target-interaction="custom.customerMeeting"></frak-button-share>
|
|
211
47
|
* ```
|
|
212
48
|
*
|
|
213
|
-
* @see {@link @frak-labs/core-sdk!actions.
|
|
49
|
+
* @see {@link @frak-labs/core-sdk!actions.displaySharingPage | `displaySharingPage()`} for more info about the sharing-page flow
|
|
214
50
|
* @see {@link @frak-labs/core-sdk!actions.getMerchantInformation | `getMerchantInformation()`} for more info about the estimated reward fetching
|
|
215
51
|
*/
|
|
216
52
|
function ButtonShare({ placement: placementId, text = "Share and earn!", classname = "", useReward: rawUseReward, noRewardText, targetInteraction, clickAction: rawClickAction, preview }) {
|
|
@@ -226,7 +62,6 @@ function ButtonShare({ placement: placementId, text = "Share and earn!", classna
|
|
|
226
62
|
const resolvedClickAction = useMemo(() => componentConfig?.clickAction ?? rawClickAction ?? "sharing-page", [componentConfig?.clickAction, rawClickAction]);
|
|
227
63
|
const { shouldRender, isHidden, isClientReady } = useClientReady();
|
|
228
64
|
const { reward } = useReward(shouldUseReward && isClientReady, resolvedTargetInteraction);
|
|
229
|
-
const { handleShare, isError, debugInfo } = useShareModal(resolvedTargetInteraction, placementId);
|
|
230
65
|
const btnText = useMemo(() => {
|
|
231
66
|
if (!shouldUseReward) return resolvedText;
|
|
232
67
|
if (!reward) return resolvedNoRewardText ?? applyRewardPlaceholder(resolvedText, void 0);
|
|
@@ -237,7 +72,7 @@ function ButtonShare({ placement: placementId, text = "Share and earn!", classna
|
|
|
237
72
|
resolvedNoRewardText,
|
|
238
73
|
reward
|
|
239
74
|
]);
|
|
240
|
-
const onClick = useCallback(
|
|
75
|
+
const onClick = useCallback(() => {
|
|
241
76
|
if (isPreview) return;
|
|
242
77
|
trackEvent(window.FrakSetup.client, "share_button_clicked", {
|
|
243
78
|
placement: placementId,
|
|
@@ -245,13 +80,14 @@ function ButtonShare({ placement: placementId, text = "Share and earn!", classna
|
|
|
245
80
|
has_reward: Boolean(reward),
|
|
246
81
|
click_action: resolvedClickAction
|
|
247
82
|
});
|
|
248
|
-
if (resolvedClickAction === "embedded-wallet")
|
|
249
|
-
|
|
250
|
-
|
|
83
|
+
if (resolvedClickAction === "embedded-wallet") {
|
|
84
|
+
openEmbeddedWallet(resolvedTargetInteraction, placementId);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
openSharingPage(resolvedTargetInteraction, placementId);
|
|
251
88
|
}, [
|
|
252
89
|
isPreview,
|
|
253
90
|
resolvedClickAction,
|
|
254
|
-
handleShare,
|
|
255
91
|
resolvedTargetInteraction,
|
|
256
92
|
placementId,
|
|
257
93
|
reward
|
|
@@ -262,13 +98,13 @@ function ButtonShare({ placement: placementId, text = "Share and earn!", classna
|
|
|
262
98
|
"button__fadeIn",
|
|
263
99
|
classname
|
|
264
100
|
].filter(Boolean).join(" ");
|
|
265
|
-
return /* @__PURE__ */
|
|
101
|
+
return /* @__PURE__ */ jsx("button", {
|
|
266
102
|
type: "button",
|
|
267
103
|
disabled: !isPreview && !isClientReady,
|
|
268
104
|
class: buttonClass,
|
|
269
105
|
onClick,
|
|
270
106
|
children: btnText
|
|
271
|
-
})
|
|
107
|
+
});
|
|
272
108
|
}
|
|
273
109
|
//#endregion
|
|
274
110
|
//#region src/components/ButtonShare/index.ts
|
package/dist/openInApp.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as registerWebComponent, i as useClientReady, t as usePlacement } from "./usePlacement-V7NrKoub.js";
|
|
2
|
-
import { t as useLightDomStyles } from "./useLightDomStyles-
|
|
2
|
+
import { t as useLightDomStyles } from "./useLightDomStyles-gbuSWvRx.js";
|
|
3
3
|
import { DEEP_LINK_SCHEME, trackEvent, triggerDeepLinkWithFallback } from "@frak-labs/core-sdk";
|
|
4
4
|
import { useMemo } from "preact/hooks";
|
|
5
5
|
import { jsx } from "preact/jsx-runtime";
|
package/dist/postPurchase.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SharingPageProduct } from "@frak-labs/core-sdk";
|
|
1
2
|
import * as _$preact from "preact";
|
|
2
3
|
|
|
3
4
|
//#region src/components/PostPurchase/types.d.ts
|
|
@@ -61,6 +62,20 @@ type PostPurchaseProps = {
|
|
|
61
62
|
* Use `{REWARD}` as placeholder for the reward amount.
|
|
62
63
|
*/
|
|
63
64
|
ctaText?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Optional product cards forwarded to the sharing page when the user
|
|
67
|
+
* clicks the CTA. Accepts either a real {@link SharingPageProduct}
|
|
68
|
+
* array (when set imperatively via the JS property,
|
|
69
|
+
* `el.products = [...]`) or a JSON-stringified array (when set as an
|
|
70
|
+
* HTML attribute, `<frak-post-purchase products='[...]'>`). The HTML
|
|
71
|
+
* attribute path is required for server-rendered surfaces — e.g.
|
|
72
|
+
* WooCommerce / Magento plugins — because `preact-custom-element`
|
|
73
|
+
* delivers attribute values as raw strings.
|
|
74
|
+
*
|
|
75
|
+
* Empty arrays / unparseable strings are treated as "no products" so
|
|
76
|
+
* the sharing page renders without the product card section.
|
|
77
|
+
*/
|
|
78
|
+
products?: SharingPageProduct[] | string;
|
|
64
79
|
/**
|
|
65
80
|
* When set, renders the card in preview mode (e.g. Shopify/WP editor).
|
|
66
81
|
* Bypasses the client-ready / RPC gates that normally hide the card
|
|
@@ -118,7 +133,8 @@ declare function PostPurchase({
|
|
|
118
133
|
refereeText: propRefereeText,
|
|
119
134
|
ctaText: propCtaText,
|
|
120
135
|
preview,
|
|
121
|
-
previewVariant
|
|
136
|
+
previewVariant,
|
|
137
|
+
products
|
|
122
138
|
}: PostPurchaseProps): _$preact.JSX.Element | null;
|
|
123
139
|
//#endregion
|
|
124
140
|
//#region src/components/PostPurchase/index.d.ts
|
package/dist/postPurchase.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { a as registerWebComponent, i as useClientReady, t as usePlacement } from "./usePlacement-V7NrKoub.js";
|
|
2
2
|
import { t as useGlobalComponents } from "./useGlobalComponents-Cmfszr7v.js";
|
|
3
|
-
import { t as useLightDomStyles } from "./useLightDomStyles-
|
|
3
|
+
import { t as useLightDomStyles } from "./useLightDomStyles-gbuSWvRx.js";
|
|
4
4
|
import { n as formatEstimatedReward, t as applyRewardPlaceholder } from "./formatReward-Bub6Z6eY.js";
|
|
5
|
-
import { t as
|
|
6
|
-
import { l as createElement, o as cssSource$4, r as
|
|
5
|
+
import { t as openSharingPage } from "./sharingPage-DFvQbviS.js";
|
|
6
|
+
import { c as cssSource$5, l as createElement, o as cssSource$4, r as LogoFrakWithName, s as cssSource$3, t as GiftIcon } from "./GiftIcon-2DC0OUeF.js";
|
|
7
7
|
import { trackEvent } from "@frak-labs/core-sdk";
|
|
8
8
|
import { getMerchantInformation, getUserReferralStatus, trackPurchaseStatus } from "@frak-labs/core-sdk/actions";
|
|
9
|
-
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
9
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
10
10
|
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
11
11
|
import { FrakRpcError, RpcErrorCodes } from "@frak-labs/frame-connector";
|
|
12
12
|
//#region ../../node_modules/.bun/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
|
|
@@ -1391,15 +1391,75 @@ const cssSource$1 = `.PostPurchase_card__5fv5lh0 {
|
|
|
1391
1391
|
.PostPurchase_frakLogo__5fv5lh6 {
|
|
1392
1392
|
display: block;
|
|
1393
1393
|
margin-left: auto;
|
|
1394
|
-
color: var(--surface-primary__pbq4aka);
|
|
1395
1394
|
}`;
|
|
1396
1395
|
var card = "PostPurchase_card__5fv5lh0";
|
|
1397
|
-
var cta = "PostPurchase_cta__5fv5lh3";
|
|
1396
|
+
var cta = "PostPurchase_cta__5fv5lh3 reset_element_button__1831jhd6 reset_fieldAppearance__1831jhd2 reset_focusRing__1831jhd1";
|
|
1398
1397
|
var frakLogo = "PostPurchase_frakLogo__5fv5lh6";
|
|
1399
1398
|
var giftIcon = "PostPurchase_giftIcon__5fv5lh5";
|
|
1400
1399
|
var icon = "PostPurchase_icon__5fv5lh4";
|
|
1401
1400
|
var message = "PostPurchase_message__5fv5lh2";
|
|
1402
|
-
const cssSource = cssSource$3 + cssSource$4 + cssSource$1;
|
|
1401
|
+
const cssSource = cssSource$5 + cssSource$3 + cssSource$4 + cssSource$1;
|
|
1402
|
+
//#endregion
|
|
1403
|
+
//#region src/components/PostPurchase/products.ts
|
|
1404
|
+
/**
|
|
1405
|
+
* Whether `value` is a syntactically valid URL with an `http(s):` scheme.
|
|
1406
|
+
*
|
|
1407
|
+
* Used to gate `imageUrl` / `link` fields coming from the public `products`
|
|
1408
|
+
* prop — the listener-side sharing-page builder calls `new URL(...)` on the
|
|
1409
|
+
* incoming product link, and a `javascript:` URL would be a XSS sink in any
|
|
1410
|
+
* consumer that binds the value to an `href`.
|
|
1411
|
+
*/
|
|
1412
|
+
function isHttpUrl(value) {
|
|
1413
|
+
try {
|
|
1414
|
+
const parsed = new URL(value);
|
|
1415
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
1416
|
+
} catch {
|
|
1417
|
+
return false;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Coerce a raw `products` prop value into a candidate array suitable for
|
|
1422
|
+
* per-item normalisation, or null when it cannot be reduced to one.
|
|
1423
|
+
*
|
|
1424
|
+
* Surfaces that set the prop via the JS property (`el.products = [...]`)
|
|
1425
|
+
* deliver a real array; surfaces that bind it as an HTML attribute
|
|
1426
|
+
* (WP / Magento server-render) deliver a JSON-stringified array. Anything
|
|
1427
|
+
* else (truthy non-array non-string, JSON parse failure, JSON that decodes
|
|
1428
|
+
* to a non-array) is treated as "no products" so the share still works
|
|
1429
|
+
* without the product card section.
|
|
1430
|
+
*/
|
|
1431
|
+
function coerceProductCandidates(products) {
|
|
1432
|
+
if (!products) return null;
|
|
1433
|
+
if (Array.isArray(products)) return products;
|
|
1434
|
+
if (typeof products !== "string") return null;
|
|
1435
|
+
try {
|
|
1436
|
+
const parsed = JSON.parse(products);
|
|
1437
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
1438
|
+
} catch {
|
|
1439
|
+
return null;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Normalise one untrusted candidate into a {@link SharingPageProduct}, or
|
|
1444
|
+
* return null when the candidate has no usable title.
|
|
1445
|
+
*
|
|
1446
|
+
* The `products` prop is a public API boundary (merchants can set it
|
|
1447
|
+
* server-side via WP/Magento or imperatively from arbitrary JS). Each entry
|
|
1448
|
+
* is validated structurally so a malformed `link` reaching `new URL(...)`
|
|
1449
|
+
* downstream would not crash the sharing-page builder, and so a
|
|
1450
|
+
* `javascript:` URL cannot slip through as `imageUrl` / `link`.
|
|
1451
|
+
*/
|
|
1452
|
+
function normalizeProductCandidate(candidate) {
|
|
1453
|
+
if (!candidate || typeof candidate !== "object") return null;
|
|
1454
|
+
const item = candidate;
|
|
1455
|
+
const title = typeof item.title === "string" ? item.title.trim() : "";
|
|
1456
|
+
if (title === "") return null;
|
|
1457
|
+
const entry = { title };
|
|
1458
|
+
if (typeof item.imageUrl === "string" && isHttpUrl(item.imageUrl)) entry.imageUrl = item.imageUrl;
|
|
1459
|
+
if (typeof item.link === "string" && isHttpUrl(item.link)) entry.link = item.link;
|
|
1460
|
+
if (typeof item.utmContent === "string" && item.utmContent !== "") entry.utmContent = item.utmContent;
|
|
1461
|
+
return entry;
|
|
1462
|
+
}
|
|
1403
1463
|
//#endregion
|
|
1404
1464
|
//#region src/components/PostPurchase/PostPurchase.tsx
|
|
1405
1465
|
/**
|
|
@@ -1444,7 +1504,7 @@ function resolvePostPurchaseContext(referralStatus, merchantInfo) {
|
|
|
1444
1504
|
* ></frak-post-purchase>
|
|
1445
1505
|
* ```
|
|
1446
1506
|
*/
|
|
1447
|
-
function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, placement: placementId, classname = "", variant: forcedVariant, badgeText: propBadgeText, referrerText: propReferrerText, refereeText: propRefereeText, ctaText: propCtaText, preview, previewVariant }) {
|
|
1507
|
+
function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, placement: placementId, classname = "", variant: forcedVariant, badgeText: propBadgeText, referrerText: propReferrerText, refereeText: propRefereeText, ctaText: propCtaText, preview, previewVariant, products }) {
|
|
1448
1508
|
const isPreview = !!preview;
|
|
1449
1509
|
const { shouldRender, isHidden, isClientReady } = useClientReady();
|
|
1450
1510
|
const placement = usePlacement(placementId);
|
|
@@ -1531,7 +1591,32 @@ function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, plac
|
|
|
1531
1591
|
placementId,
|
|
1532
1592
|
context?.reward
|
|
1533
1593
|
]);
|
|
1534
|
-
const
|
|
1594
|
+
const parsedProducts = useMemo(() => {
|
|
1595
|
+
const candidates = coerceProductCandidates(products);
|
|
1596
|
+
if (!candidates) return void 0;
|
|
1597
|
+
const sanitized = [];
|
|
1598
|
+
for (const candidate of candidates) {
|
|
1599
|
+
const entry = normalizeProductCandidate(candidate);
|
|
1600
|
+
if (entry) sanitized.push(entry);
|
|
1601
|
+
}
|
|
1602
|
+
return sanitized.length > 0 ? sanitized : void 0;
|
|
1603
|
+
}, [products]);
|
|
1604
|
+
const handleClick = useCallback(() => {
|
|
1605
|
+
if (!resolvedVariant) return;
|
|
1606
|
+
trackEvent(window.FrakSetup?.client, "post_purchase_clicked", {
|
|
1607
|
+
placement: placementId,
|
|
1608
|
+
variant: resolvedVariant
|
|
1609
|
+
});
|
|
1610
|
+
openSharingPage(void 0, placementId, {
|
|
1611
|
+
link: resolvedSharingUrl,
|
|
1612
|
+
products: parsedProducts
|
|
1613
|
+
});
|
|
1614
|
+
}, [
|
|
1615
|
+
resolvedVariant,
|
|
1616
|
+
placementId,
|
|
1617
|
+
resolvedSharingUrl,
|
|
1618
|
+
parsedProducts
|
|
1619
|
+
]);
|
|
1535
1620
|
if (!isPreview && (!shouldRender || isHidden)) return null;
|
|
1536
1621
|
if (!isPreview && (!context || !resolvedVariant)) return null;
|
|
1537
1622
|
if (!resolvedVariant) return null;
|
|
@@ -1557,14 +1642,7 @@ function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, plac
|
|
|
1557
1642
|
type: "button",
|
|
1558
1643
|
className: `${cta} button`,
|
|
1559
1644
|
disabled: !isPreview && !isClientReady,
|
|
1560
|
-
onClick: isPreview ? void 0 :
|
|
1561
|
-
if (!resolvedVariant) return;
|
|
1562
|
-
trackEvent(window.FrakSetup?.client, "post_purchase_clicked", {
|
|
1563
|
-
placement: placementId,
|
|
1564
|
-
variant: resolvedVariant
|
|
1565
|
-
});
|
|
1566
|
-
handleShare();
|
|
1567
|
-
},
|
|
1645
|
+
onClick: isPreview ? void 0 : handleClick,
|
|
1568
1646
|
children: [texts.cta, /* @__PURE__ */ jsx("svg", {
|
|
1569
1647
|
width: "16",
|
|
1570
1648
|
height: "16",
|
|
@@ -1588,10 +1666,10 @@ function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, plac
|
|
|
1588
1666
|
className: giftIcon,
|
|
1589
1667
|
width: 80,
|
|
1590
1668
|
height: 80
|
|
1591
|
-
}), /* @__PURE__ */ jsx(
|
|
1669
|
+
}), /* @__PURE__ */ jsx(LogoFrakWithName, {
|
|
1592
1670
|
className: frakLogo,
|
|
1593
|
-
width:
|
|
1594
|
-
height:
|
|
1671
|
+
width: 42,
|
|
1672
|
+
height: 24
|
|
1595
1673
|
})]
|
|
1596
1674
|
})
|
|
1597
1675
|
})]
|
|
@@ -1613,6 +1691,7 @@ registerWebComponent(PostPurchase, "frak-post-purchase", [
|
|
|
1613
1691
|
"referrerText",
|
|
1614
1692
|
"refereeText",
|
|
1615
1693
|
"ctaText",
|
|
1694
|
+
"products",
|
|
1616
1695
|
"preview",
|
|
1617
1696
|
"previewVariant"
|
|
1618
1697
|
], { shadow: false });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { displaySharingPage } from "@frak-labs/core-sdk/actions";
|
|
2
|
+
//#region src/utils/sharingPage.ts
|
|
3
|
+
async function openSharingPage(targetInteraction, placement, options) {
|
|
4
|
+
if (!window.FrakSetup?.client) {
|
|
5
|
+
console.error("Frak client not found");
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
await displaySharingPage(window.FrakSetup.client, {
|
|
9
|
+
...options?.link && { link: options.link },
|
|
10
|
+
...options?.products?.length && { products: options.products },
|
|
11
|
+
...targetInteraction && { metadata: { targetInteraction } }
|
|
12
|
+
}, placement);
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { openSharingPage as t };
|