@frak-labs/components 0.0.26-beta.06c52c98 → 0.0.26-beta.1c24fe7c
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.D-DB7VwI.js +1 -0
- package/cdn/ButtonShare.BM9Inhmf.js +1 -0
- package/cdn/ButtonWallet.B7UmJcbb.js +40 -0
- package/cdn/OpenInAppButton.B4mrsz6L.js +1 -0
- package/cdn/PostPurchase.CMa-59qR.js +1 -0
- package/cdn/components.js +1 -1
- package/cdn/formatReward.C8hlSKRj.js +1 -0
- package/cdn/jsxRuntime.module.5UNmmhNi.js +138 -0
- package/cdn/loader.js +26 -26
- package/cdn/useLightDomStyles.D895e4W1.js +1 -0
- package/cdn/useReward.DEU0AF3P.js +1 -0
- package/cdn/useShareModal.CN9_P7Sl.js +1 -0
- package/dist/banner.d.ts +73 -0
- package/dist/banner.js +171 -0
- package/dist/buttonShare.d.ts +1 -1
- package/dist/buttonShare.js +20 -54
- package/dist/buttonWallet.js +2 -2
- package/dist/formatReward-6JQldDEC.js +28 -0
- package/dist/openInApp.js +2 -2
- package/dist/postPurchase.d.ts +98 -0
- package/dist/postPurchase.js +140 -0
- package/dist/{useLightDomStyles-C029nIFk.js → useLightDomStyles-DukxuNnJ.js} +3 -3
- package/dist/{usePlacement-DD1NUBps.js → usePlacement-BbMuz8_A.js} +112 -65
- package/dist/useReward-CI2yRrCj.js +67 -0
- package/dist/useShareModal-DHlayNqk.js +55 -0
- package/package.json +13 -4
- package/cdn/ButtonShare.BG6DCGo6.js +0 -1
- package/cdn/ButtonWallet.BlV-eDcH.js +0 -40
- package/cdn/OpenInAppButton.Dq3J_3Ci.js +0 -1
- package/cdn/jsxRuntime.module.PHHpn0I_.js +0 -58
- package/dist/useReward-DAkT-7wT.js +0 -48
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./loader.js";import{a as t,u as n}from"./jsxRuntime.module.5UNmmhNi.js";function r(r,i,a,o){n(()=>{e.injectBase(r,o??t)},[r]),n(()=>{!i||!a||e.injectPlacement(r,i,a)},[r,i,a])}export{r as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as e,b as t,l as n}from"./loader.js";import{c as r,u as i}from"./jsxRuntime.module.5UNmmhNi.js";import{t as a}from"./formatReward.C8hlSKRj.js";function o(e,t){switch(e.payoutType){case`fixed`:return e.amount[t];case`tiered`:return e.tiers.reduce((e,n)=>Math.max(e,n.amount[t]),0);case`percentage`:return 0}}function s({rewards:n},r,i){let s=(i?n.filter(e=>e.interactionTypeKey===i):n).map(e=>e.referrer).filter(e=>e!==void 0);if(s.length===0)return;let c=t(e(r)),l=s[0],u=o(l,c);for(let e=1;e<s.length;e++){let t=o(s[e],c);t>u&&(l=s[e],u=t)}if(u<=0){let e=s.find(e=>e.payoutType===`percentage`);if(!e)return;l=e}return a(l,r)}function c(e,t){let[a,o]=r(void 0);return i(()=>{if(!e)return;let r=window.FrakSetup?.client;r&&n(r).then(e=>{let n=r.config.metadata?.currency,i=s(e,n,t);i&&o(i)}).catch(()=>{})},[e,t]),{reward:a}}export{c as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{C as e,S as t,u as n,v as r,w as i}from"./loader.js";import{c as a,l as o}from"./jsxRuntime.module.5UNmmhNi.js";function s(s,c,l){let[u,d]=a(void 0),[f,p]=a(!1);return{handleShare:o(async()=>{if(!window.FrakSetup?.client){console.error(`Frak client not found`),d(r.empty().formatDebugInfo(`Frak client not found`)),p(!0);return}let a=n(window.FrakSetup.client,{});try{await a.sharing(l?{link:l}:{}).display(e=>({...e,targetInteraction:s}),c)}catch(n){if(n instanceof i&&n.code===e.clientAborted){console.debug(`User aborted the modal`);return}let r=window.FrakSetup.client.debugInfo.formatDebugInfo(n);t(window.FrakSetup.client,`share_modal_error`,{error:n instanceof Object&&`message`in n?n.message:`Unknown error`,debugInfo:r}),d(r),p(!0),console.error(`Error while opening the modal`,n)}},[s,c,l]),isError:f,debugInfo:u}}export{s as t};
|
package/dist/banner.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { InteractionTypeKey } from "@frak-labs/core-sdk";
|
|
2
|
+
import * as preact from "preact";
|
|
3
|
+
|
|
4
|
+
//#region src/components/Banner/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The props type for {@link Banner}.
|
|
7
|
+
* @inline
|
|
8
|
+
*/
|
|
9
|
+
type BannerProps = {
|
|
10
|
+
/**
|
|
11
|
+
* Placement ID for backend-driven CSS customization.
|
|
12
|
+
*/
|
|
13
|
+
placement?: string;
|
|
14
|
+
/**
|
|
15
|
+
* CSS class names passed through to the root element (Light DOM).
|
|
16
|
+
*/
|
|
17
|
+
classname?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Filter rewards by interaction type (e.g. "purchase", "referral").
|
|
20
|
+
* When omitted, the best reward across all interaction types is shown.
|
|
21
|
+
*/
|
|
22
|
+
interaction?: InteractionTypeKey;
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/components/Banner/Banner.d.ts
|
|
26
|
+
/**
|
|
27
|
+
* Auto-detecting notification banner component.
|
|
28
|
+
*
|
|
29
|
+
* Renders an inline banner on the merchant page that auto-detects which
|
|
30
|
+
* message to display:
|
|
31
|
+
*
|
|
32
|
+
* - **Referral mode**: Shown after successful referral link processing.
|
|
33
|
+
* Displays reward info and a dismiss button ("Got it").
|
|
34
|
+
* - **In-app browser mode**: Shown when the page is opened inside a social
|
|
35
|
+
* media in-app browser (Instagram, Facebook). Offers a redirect to the
|
|
36
|
+
* default browser.
|
|
37
|
+
*
|
|
38
|
+
* In-app browser mode takes priority over referral mode.
|
|
39
|
+
* Uses Light DOM to inherit merchant page styles.
|
|
40
|
+
*
|
|
41
|
+
* @group components
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* Basic usage (auto-detects mode):
|
|
45
|
+
* ```html
|
|
46
|
+
* <frak-banner></frak-banner>
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* With a custom class:
|
|
51
|
+
* ```html
|
|
52
|
+
* <frak-banner classname="my-custom-banner"></frak-banner>
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare function Banner({
|
|
56
|
+
placement: placementId,
|
|
57
|
+
classname,
|
|
58
|
+
interaction
|
|
59
|
+
}: BannerProps): preact.JSX.Element | null;
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/components/Banner/index.d.ts
|
|
62
|
+
/**
|
|
63
|
+
* Custom element interface for `<frak-banner>`.
|
|
64
|
+
* Combines standard {@link HTMLElement} with {@link BannerProps}.
|
|
65
|
+
*/
|
|
66
|
+
interface BannerElement extends HTMLElement, BannerProps {}
|
|
67
|
+
declare global {
|
|
68
|
+
interface HTMLElementTagNameMap {
|
|
69
|
+
"frak-banner": BannerElement;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { Banner, BannerElement };
|
package/dist/banner.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { a as useClientReady, n as bannerBaseCss, o as registerWebComponent, t as usePlacement } from "./usePlacement-BbMuz8_A.js";
|
|
2
|
+
import { t as useLightDomStyles } from "./useLightDomStyles-DukxuNnJ.js";
|
|
3
|
+
import { t as useReward } from "./useReward-CI2yRrCj.js";
|
|
4
|
+
import { isInAppBrowser, redirectToExternalBrowser } from "@frak-labs/core-sdk";
|
|
5
|
+
import { REFERRAL_SUCCESS_EVENT } from "@frak-labs/core-sdk/actions";
|
|
6
|
+
import { useCallback, useEffect, useMemo, useState } from "preact/hooks";
|
|
7
|
+
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
8
|
+
|
|
9
|
+
//#region src/components/Banner/Banner.tsx
|
|
10
|
+
/**
|
|
11
|
+
* Reward/gift icon for referral mode.
|
|
12
|
+
*/
|
|
13
|
+
function RewardIcon() {
|
|
14
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
15
|
+
viewBox: "0 0 24 24",
|
|
16
|
+
fill: "none",
|
|
17
|
+
stroke: "currentColor",
|
|
18
|
+
"stroke-width": "1.5",
|
|
19
|
+
"stroke-linecap": "round",
|
|
20
|
+
"stroke-linejoin": "round",
|
|
21
|
+
"aria-hidden": "true",
|
|
22
|
+
children: [
|
|
23
|
+
/* @__PURE__ */ jsx("rect", {
|
|
24
|
+
x: "3",
|
|
25
|
+
y: "11",
|
|
26
|
+
width: "18",
|
|
27
|
+
height: "11",
|
|
28
|
+
rx: "1"
|
|
29
|
+
}),
|
|
30
|
+
/* @__PURE__ */ jsx("path", { d: "M12 11v11" }),
|
|
31
|
+
/* @__PURE__ */ jsx("rect", {
|
|
32
|
+
x: "5",
|
|
33
|
+
y: "7",
|
|
34
|
+
width: "14",
|
|
35
|
+
height: "4",
|
|
36
|
+
rx: "1"
|
|
37
|
+
}),
|
|
38
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7c0 0-1.5-4-4.5-4S5 5 7.5 7" }),
|
|
39
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7c0 0 1.5-4 4.5-4S19 5 16.5 7" })
|
|
40
|
+
]
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* External link icon for in-app browser mode.
|
|
45
|
+
*/
|
|
46
|
+
function BrowserIcon() {
|
|
47
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
48
|
+
viewBox: "0 0 24 24",
|
|
49
|
+
fill: "none",
|
|
50
|
+
stroke: "currentColor",
|
|
51
|
+
"stroke-width": "1.5",
|
|
52
|
+
"stroke-linecap": "round",
|
|
53
|
+
"stroke-linejoin": "round",
|
|
54
|
+
"aria-hidden": "true",
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ jsx("path", { d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" }),
|
|
57
|
+
/* @__PURE__ */ jsx("polyline", { points: "15 3 21 3 21 9" }),
|
|
58
|
+
/* @__PURE__ */ jsx("line", {
|
|
59
|
+
x1: "10",
|
|
60
|
+
y1: "14",
|
|
61
|
+
x2: "21",
|
|
62
|
+
y2: "3"
|
|
63
|
+
})
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Auto-detecting notification banner component.
|
|
69
|
+
*
|
|
70
|
+
* Renders an inline banner on the merchant page that auto-detects which
|
|
71
|
+
* message to display:
|
|
72
|
+
*
|
|
73
|
+
* - **Referral mode**: Shown after successful referral link processing.
|
|
74
|
+
* Displays reward info and a dismiss button ("Got it").
|
|
75
|
+
* - **In-app browser mode**: Shown when the page is opened inside a social
|
|
76
|
+
* media in-app browser (Instagram, Facebook). Offers a redirect to the
|
|
77
|
+
* default browser.
|
|
78
|
+
*
|
|
79
|
+
* In-app browser mode takes priority over referral mode.
|
|
80
|
+
* Uses Light DOM to inherit merchant page styles.
|
|
81
|
+
*
|
|
82
|
+
* @group components
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* Basic usage (auto-detects mode):
|
|
86
|
+
* ```html
|
|
87
|
+
* <frak-banner></frak-banner>
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* With a custom class:
|
|
92
|
+
* ```html
|
|
93
|
+
* <frak-banner classname="my-custom-banner"></frak-banner>
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
function Banner({ placement: placementId, classname = "", interaction }) {
|
|
97
|
+
const placement = usePlacement(placementId);
|
|
98
|
+
const { shouldRender, isHidden, isClientReady } = useClientReady();
|
|
99
|
+
useLightDomStyles("frak-banner", placementId, placement?.components?.banner?.css, bannerBaseCss);
|
|
100
|
+
const [dismissed, setDismissed] = useState(false);
|
|
101
|
+
const [mode, setMode] = useState(() => isInAppBrowser ? "inapp" : null);
|
|
102
|
+
const { reward } = useReward(mode === "referral" && isClientReady, interaction);
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
if (mode === "inapp") return;
|
|
105
|
+
const handler = () => setMode("referral");
|
|
106
|
+
window.addEventListener(REFERRAL_SUCCESS_EVENT, handler);
|
|
107
|
+
return () => window.removeEventListener(REFERRAL_SUCCESS_EVENT, handler);
|
|
108
|
+
}, [mode]);
|
|
109
|
+
const handleAction = useCallback(() => {
|
|
110
|
+
if (mode === "referral") setDismissed(true);
|
|
111
|
+
else redirectToExternalBrowser(window.location.href);
|
|
112
|
+
}, [mode]);
|
|
113
|
+
const bannerConfig = placement?.components?.banner;
|
|
114
|
+
const texts = useMemo(() => {
|
|
115
|
+
if (mode === "referral") {
|
|
116
|
+
const defaultTitle = reward ? `Earn ${reward} on purchases on this site` : "You've been referred!";
|
|
117
|
+
return {
|
|
118
|
+
title: bannerConfig?.referralTitle ?? defaultTitle,
|
|
119
|
+
description: bannerConfig?.referralDescription ?? "Earn rewards after your purchase via the Frak partner app.",
|
|
120
|
+
cta: bannerConfig?.referralCta ?? "Got it"
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
title: bannerConfig?.inappTitle ?? "Open in your browser",
|
|
125
|
+
description: bannerConfig?.inappDescription ?? "For a better experience and to earn your rewards, open this page in your default browser.",
|
|
126
|
+
cta: bannerConfig?.inappCta ?? "Open browser"
|
|
127
|
+
};
|
|
128
|
+
}, [
|
|
129
|
+
mode,
|
|
130
|
+
reward,
|
|
131
|
+
bannerConfig
|
|
132
|
+
]);
|
|
133
|
+
if (!shouldRender || isHidden || dismissed || !mode) return null;
|
|
134
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
135
|
+
class: [
|
|
136
|
+
"banner",
|
|
137
|
+
"banner__fadeIn",
|
|
138
|
+
classname
|
|
139
|
+
].filter(Boolean).join(" "),
|
|
140
|
+
role: "alert",
|
|
141
|
+
children: [
|
|
142
|
+
/* @__PURE__ */ jsx("div", {
|
|
143
|
+
class: "banner__icon",
|
|
144
|
+
children: mode === "referral" ? /* @__PURE__ */ jsx(RewardIcon, {}) : /* @__PURE__ */ jsx(BrowserIcon, {})
|
|
145
|
+
}),
|
|
146
|
+
/* @__PURE__ */ jsxs("div", {
|
|
147
|
+
class: "banner__content",
|
|
148
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
149
|
+
class: "banner__title",
|
|
150
|
+
children: texts.title
|
|
151
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
152
|
+
class: "banner__description",
|
|
153
|
+
children: texts.description
|
|
154
|
+
})]
|
|
155
|
+
}),
|
|
156
|
+
/* @__PURE__ */ jsx("button", {
|
|
157
|
+
type: "button",
|
|
158
|
+
class: "banner__cta",
|
|
159
|
+
onClick: handleAction,
|
|
160
|
+
children: texts.cta
|
|
161
|
+
})
|
|
162
|
+
]
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/components/Banner/index.ts
|
|
168
|
+
registerWebComponent(Banner, "frak-banner", ["placement", "classname"], { shadow: false });
|
|
169
|
+
|
|
170
|
+
//#endregion
|
|
171
|
+
export { Banner };
|
package/dist/buttonShare.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ type ButtonShareProps = {
|
|
|
35
35
|
* Which UI to open on click
|
|
36
36
|
* @defaultValue `"embedded-wallet"`
|
|
37
37
|
*/
|
|
38
|
-
clickAction?: "embedded-wallet" | "share-modal";
|
|
38
|
+
clickAction?: "embedded-wallet" | "share-modal" | "sharing-page";
|
|
39
39
|
};
|
|
40
40
|
//#endregion
|
|
41
41
|
//#region src/components/ButtonShare/ButtonShare.d.ts
|
package/dist/buttonShare.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { t as useLightDomStyles } from "./useLightDomStyles-
|
|
3
|
-
import { t as useReward } from "./useReward-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { a as useClientReady, c as openEmbeddedWallet, o as registerWebComponent, t as usePlacement } from "./usePlacement-BbMuz8_A.js";
|
|
2
|
+
import { t as useLightDomStyles } from "./useLightDomStyles-DukxuNnJ.js";
|
|
3
|
+
import { t as useReward } from "./useReward-CI2yRrCj.js";
|
|
4
|
+
import { t as useShareModal } from "./useShareModal-DHlayNqk.js";
|
|
5
|
+
import { trackEvent } from "@frak-labs/core-sdk";
|
|
6
|
+
import { displaySharingPage } from "@frak-labs/core-sdk/actions";
|
|
6
7
|
import { useCallback, useMemo, useState } from "preact/hooks";
|
|
7
8
|
import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
|
|
8
|
-
import { FrakRpcError, RpcErrorCodes } from "@frak-labs/frame-connector";
|
|
9
9
|
|
|
10
|
+
//#region src/utils/sharingPage.ts
|
|
11
|
+
async function openSharingPage(targetInteraction, placement) {
|
|
12
|
+
if (!window.FrakSetup?.client) {
|
|
13
|
+
console.error("Frak client not found");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
await displaySharingPage(window.FrakSetup.client, { metadata: { ...targetInteraction && { targetInteraction } } }, placement);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
10
20
|
//#region src/hooks/useCopyToClipboard.ts
|
|
11
21
|
function useCopyToClipboard(options = {}) {
|
|
12
22
|
const { successDuration = 2e3 } = options;
|
|
@@ -159,51 +169,6 @@ function ErrorMessage({ debugInfo }) {
|
|
|
159
169
|
});
|
|
160
170
|
}
|
|
161
171
|
|
|
162
|
-
//#endregion
|
|
163
|
-
//#region src/components/ButtonShare/hooks/useShareModal.ts
|
|
164
|
-
/**
|
|
165
|
-
* Open the share modal
|
|
166
|
-
*
|
|
167
|
-
* @description
|
|
168
|
-
* This function will open the share modal, lazily creating a modal builder on demand.
|
|
169
|
-
*/
|
|
170
|
-
function useShareModal(targetInteraction, placement) {
|
|
171
|
-
const [debugInfo, setDebugInfo] = useState(void 0);
|
|
172
|
-
const [isError, setIsError] = useState(false);
|
|
173
|
-
return {
|
|
174
|
-
handleShare: useCallback(async () => {
|
|
175
|
-
if (!window.FrakSetup?.client) {
|
|
176
|
-
console.error("Frak client not found");
|
|
177
|
-
setDebugInfo(DebugInfoGatherer.empty().formatDebugInfo("Frak client not found"));
|
|
178
|
-
setIsError(true);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const builder = modalBuilder(window.FrakSetup.client, {});
|
|
182
|
-
try {
|
|
183
|
-
await builder.sharing({}).display((metadata) => ({
|
|
184
|
-
...metadata,
|
|
185
|
-
targetInteraction
|
|
186
|
-
}), placement);
|
|
187
|
-
} catch (e) {
|
|
188
|
-
if (e instanceof FrakRpcError && e.code === RpcErrorCodes.clientAborted) {
|
|
189
|
-
console.debug("User aborted the modal");
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
const debugInfo = window.FrakSetup.client.debugInfo.formatDebugInfo(e);
|
|
193
|
-
trackEvent(window.FrakSetup.client, "share_modal_error", {
|
|
194
|
-
error: e instanceof Object && "message" in e ? e.message : "Unknown error",
|
|
195
|
-
debugInfo
|
|
196
|
-
});
|
|
197
|
-
setDebugInfo(debugInfo);
|
|
198
|
-
setIsError(true);
|
|
199
|
-
console.error("Error while opening the modal", e);
|
|
200
|
-
}
|
|
201
|
-
}, [targetInteraction, placement]),
|
|
202
|
-
isError,
|
|
203
|
-
debugInfo
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
172
|
//#endregion
|
|
208
173
|
//#region src/components/ButtonShare/ButtonShare.tsx
|
|
209
174
|
/**
|
|
@@ -255,7 +220,7 @@ function ButtonShare({ placement: placementId, text = "Share and earn!", classna
|
|
|
255
220
|
const resolvedText = componentConfig?.text ?? text;
|
|
256
221
|
const resolvedNoRewardText = componentConfig?.noRewardText ?? noRewardText;
|
|
257
222
|
const shouldUseReward = useMemo(() => componentConfig?.useReward ?? rawUseReward === true, [componentConfig?.useReward, rawUseReward]);
|
|
258
|
-
const resolvedClickAction = useMemo(() => componentConfig?.clickAction ?? rawClickAction ?? "
|
|
223
|
+
const resolvedClickAction = useMemo(() => componentConfig?.clickAction ?? rawClickAction ?? "sharing-page", [componentConfig?.clickAction, rawClickAction]);
|
|
259
224
|
const { shouldRender, isHidden, isClientReady } = useClientReady();
|
|
260
225
|
const { reward } = useReward(shouldUseReward && isClientReady, resolvedTargetInteraction);
|
|
261
226
|
const { handleShare, isError, debugInfo } = useShareModal(resolvedTargetInteraction, placementId);
|
|
@@ -271,8 +236,9 @@ function ButtonShare({ placement: placementId, text = "Share and earn!", classna
|
|
|
271
236
|
]);
|
|
272
237
|
const onClick = useCallback(async () => {
|
|
273
238
|
trackEvent(window.FrakSetup.client, "share_button_clicked");
|
|
274
|
-
if (resolvedClickAction === "
|
|
275
|
-
else
|
|
239
|
+
if (resolvedClickAction === "embedded-wallet") openEmbeddedWallet(resolvedTargetInteraction, placementId);
|
|
240
|
+
else if (resolvedClickAction === "share-modal") await handleShare();
|
|
241
|
+
else openSharingPage(resolvedTargetInteraction, placementId);
|
|
276
242
|
}, [
|
|
277
243
|
resolvedClickAction,
|
|
278
244
|
handleShare,
|
package/dist/buttonWallet.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { t as useReward } from "./useReward-
|
|
1
|
+
import { a as useClientReady, o as registerWebComponent, r as buildStyleContent, s as openWalletModal, t as usePlacement } from "./usePlacement-BbMuz8_A.js";
|
|
2
|
+
import { t as useReward } from "./useReward-CI2yRrCj.js";
|
|
3
3
|
import { trackEvent } from "@frak-labs/core-sdk";
|
|
4
4
|
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { formatAmount, getCurrencyAmountKey, getSupportedCurrency } from "@frak-labs/core-sdk";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/formatReward.ts
|
|
4
|
+
/**
|
|
5
|
+
* Format an {@link EstimatedReward} into a human-readable string.
|
|
6
|
+
*
|
|
7
|
+
* - `fixed` → e.g. `"5 €"`
|
|
8
|
+
* - `percentage` → if `basketAmount` is provided, computes the actual value
|
|
9
|
+
* (e.g. `"10 €"`), otherwise returns `"10 %"`
|
|
10
|
+
* - `tiered` → max tier value, e.g. `"50 €"`
|
|
11
|
+
*/
|
|
12
|
+
function formatEstimatedReward(reward, currency, basketAmount) {
|
|
13
|
+
const supportedCurrency = getSupportedCurrency(currency);
|
|
14
|
+
const key = getCurrencyAmountKey(supportedCurrency);
|
|
15
|
+
switch (reward.payoutType) {
|
|
16
|
+
case "fixed": return formatAmount(Math.round(reward.amount[key]), supportedCurrency);
|
|
17
|
+
case "percentage":
|
|
18
|
+
if (basketAmount !== void 0) return formatAmount(Math.round(reward.percent * basketAmount / 100), supportedCurrency);
|
|
19
|
+
return `${reward.percent} %`;
|
|
20
|
+
case "tiered": {
|
|
21
|
+
const max = reward.tiers.reduce((acc, tier) => Math.max(acc, tier.amount[key]), 0);
|
|
22
|
+
return formatAmount(Math.round(max), supportedCurrency);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { formatEstimatedReward as t };
|
package/dist/openInApp.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { t as useLightDomStyles } from "./useLightDomStyles-
|
|
1
|
+
import { a as useClientReady, o as registerWebComponent, t as usePlacement } from "./usePlacement-BbMuz8_A.js";
|
|
2
|
+
import { t as useLightDomStyles } from "./useLightDomStyles-DukxuNnJ.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";
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as preact from "preact";
|
|
2
|
+
|
|
3
|
+
//#region src/components/PostPurchase/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Props for the {@link PostPurchase} component.
|
|
6
|
+
* @inline
|
|
7
|
+
*/
|
|
8
|
+
type PostPurchaseProps = {
|
|
9
|
+
/**
|
|
10
|
+
* Merchant customer ID for purchase tracking fallback.
|
|
11
|
+
* All three tracking props (`customerId`, `orderId`, `token`) must be
|
|
12
|
+
* present for tracking to fire.
|
|
13
|
+
*/
|
|
14
|
+
customerId?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Merchant order ID for purchase tracking fallback.
|
|
17
|
+
*/
|
|
18
|
+
orderId?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Checkout token for purchase tracking fallback.
|
|
21
|
+
*/
|
|
22
|
+
token?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Base URL to share. Falls back to the merchant domain returned by
|
|
25
|
+
* the backend when omitted.
|
|
26
|
+
*/
|
|
27
|
+
sharingUrl?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Override the merchant ID resolved from the SDK config.
|
|
30
|
+
*/
|
|
31
|
+
merchantId?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Placement ID for backend-driven CSS customization.
|
|
34
|
+
*/
|
|
35
|
+
placement?: string;
|
|
36
|
+
/**
|
|
37
|
+
* CSS class names passed through to the root element (Light DOM).
|
|
38
|
+
*/
|
|
39
|
+
classname?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Force a display variant instead of relying on the backend evaluation.
|
|
42
|
+
*/
|
|
43
|
+
variant?: "referrer" | "referee";
|
|
44
|
+
};
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/components/PostPurchase/PostPurchase.d.ts
|
|
47
|
+
/**
|
|
48
|
+
* Post-purchase card component.
|
|
49
|
+
*
|
|
50
|
+
* Renders an inline card on the merchant's thank-you / order-status page
|
|
51
|
+
* that either congratulates a referee or invites a referrer to share.
|
|
52
|
+
*
|
|
53
|
+
* Fetches referral status and merchant information via two independent
|
|
54
|
+
* RPC calls, then computes the display variant locally.
|
|
55
|
+
*
|
|
56
|
+
* @group components
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* Minimal — just show the card:
|
|
60
|
+
* ```html
|
|
61
|
+
* <frak-post-purchase></frak-post-purchase>
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* With purchase tracking fallback and custom sharing URL:
|
|
66
|
+
* ```html
|
|
67
|
+
* <frak-post-purchase
|
|
68
|
+
* customer-id="cust_123"
|
|
69
|
+
* order-id="ord_456"
|
|
70
|
+
* token="checkout_abc"
|
|
71
|
+
* sharing-url="https://merchant.com/product/shoes"
|
|
72
|
+
* ></frak-post-purchase>
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
declare function PostPurchase({
|
|
76
|
+
customerId,
|
|
77
|
+
orderId,
|
|
78
|
+
token,
|
|
79
|
+
sharingUrl,
|
|
80
|
+
merchantId,
|
|
81
|
+
placement: placementId,
|
|
82
|
+
classname,
|
|
83
|
+
variant: forcedVariant
|
|
84
|
+
}: PostPurchaseProps): preact.JSX.Element | null;
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/components/PostPurchase/index.d.ts
|
|
87
|
+
/**
|
|
88
|
+
* Custom element interface for `<frak-post-purchase>`.
|
|
89
|
+
* Combines standard {@link HTMLElement} with {@link PostPurchaseProps}.
|
|
90
|
+
*/
|
|
91
|
+
interface PostPurchaseElement extends HTMLElement, PostPurchaseProps {}
|
|
92
|
+
declare global {
|
|
93
|
+
interface HTMLElementTagNameMap {
|
|
94
|
+
"frak-post-purchase": PostPurchaseElement;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { PostPurchase, PostPurchaseElement };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { a as useClientReady, o as registerWebComponent, t as usePlacement } from "./usePlacement-BbMuz8_A.js";
|
|
2
|
+
import { t as useLightDomStyles } from "./useLightDomStyles-DukxuNnJ.js";
|
|
3
|
+
import { t as formatEstimatedReward } from "./formatReward-6JQldDEC.js";
|
|
4
|
+
import { t as useShareModal } from "./useShareModal-DHlayNqk.js";
|
|
5
|
+
import { getMerchantInformation, getUserReferralStatus, trackPurchaseStatus } from "@frak-labs/core-sdk/actions";
|
|
6
|
+
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
7
|
+
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
8
|
+
import { FrakRpcError, RpcErrorCodes } from "@frak-labs/frame-connector";
|
|
9
|
+
|
|
10
|
+
//#region src/components/PostPurchase/PostPurchase.tsx
|
|
11
|
+
/**
|
|
12
|
+
* Given referral status and merchant info, compute the display variant
|
|
13
|
+
* and pick the appropriate purchase reward.
|
|
14
|
+
*/
|
|
15
|
+
function resolvePostPurchaseContext(referralStatus, merchantInfo) {
|
|
16
|
+
const purchaseReward = merchantInfo.rewards.find((r) => r.interactionTypeKey === "purchase" && (r.referrer || r.referee));
|
|
17
|
+
if (!purchaseReward) return null;
|
|
18
|
+
const variant = referralStatus?.isReferred && purchaseReward.referee ? "referee" : "referrer";
|
|
19
|
+
return {
|
|
20
|
+
variant,
|
|
21
|
+
reward: variant === "referee" ? purchaseReward.referee : purchaseReward.referrer,
|
|
22
|
+
merchantDomain: merchantInfo.onChainMetadata.domain
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Post-purchase card component.
|
|
27
|
+
*
|
|
28
|
+
* Renders an inline card on the merchant's thank-you / order-status page
|
|
29
|
+
* that either congratulates a referee or invites a referrer to share.
|
|
30
|
+
*
|
|
31
|
+
* Fetches referral status and merchant information via two independent
|
|
32
|
+
* RPC calls, then computes the display variant locally.
|
|
33
|
+
*
|
|
34
|
+
* @group components
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* Minimal — just show the card:
|
|
38
|
+
* ```html
|
|
39
|
+
* <frak-post-purchase></frak-post-purchase>
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* With purchase tracking fallback and custom sharing URL:
|
|
44
|
+
* ```html
|
|
45
|
+
* <frak-post-purchase
|
|
46
|
+
* customer-id="cust_123"
|
|
47
|
+
* order-id="ord_456"
|
|
48
|
+
* token="checkout_abc"
|
|
49
|
+
* sharing-url="https://merchant.com/product/shoes"
|
|
50
|
+
* ></frak-post-purchase>
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, placement: placementId, classname = "", variant: forcedVariant }) {
|
|
54
|
+
const { shouldRender, isHidden, isClientReady } = useClientReady();
|
|
55
|
+
const placement = usePlacement(placementId);
|
|
56
|
+
useLightDomStyles("frak-post-purchase", placementId, placement?.components?.postPurchase?.css);
|
|
57
|
+
const [context, setContext] = useState(null);
|
|
58
|
+
const [hasFetched, setHasFetched] = useState(false);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!isClientReady || !customerId || !orderId || !token) return;
|
|
61
|
+
trackPurchaseStatus({
|
|
62
|
+
customerId,
|
|
63
|
+
orderId,
|
|
64
|
+
token,
|
|
65
|
+
merchantId
|
|
66
|
+
}).catch(() => {});
|
|
67
|
+
}, [
|
|
68
|
+
isClientReady,
|
|
69
|
+
customerId,
|
|
70
|
+
orderId,
|
|
71
|
+
token,
|
|
72
|
+
merchantId
|
|
73
|
+
]);
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (!isClientReady || hasFetched) return;
|
|
76
|
+
const client = window.FrakSetup?.client;
|
|
77
|
+
if (!client) return;
|
|
78
|
+
setHasFetched(true);
|
|
79
|
+
Promise.all([getUserReferralStatus(client), getMerchantInformation(client)]).then(([referralStatus, merchantInfo]) => {
|
|
80
|
+
setContext(resolvePostPurchaseContext(referralStatus, merchantInfo));
|
|
81
|
+
}).catch((e) => {
|
|
82
|
+
if (e instanceof FrakRpcError && e.code === RpcErrorCodes.configError) return;
|
|
83
|
+
console.warn("[Frak] Post-purchase context error", e);
|
|
84
|
+
});
|
|
85
|
+
}, [isClientReady, hasFetched]);
|
|
86
|
+
const resolvedVariant = forcedVariant ?? context?.variant;
|
|
87
|
+
const resolvedSharingUrl = sharingUrl ?? context?.merchantDomain;
|
|
88
|
+
const rewardText = useMemo(() => {
|
|
89
|
+
if (!context?.reward) return void 0;
|
|
90
|
+
const currency = window.FrakSetup?.client?.config?.metadata?.currency;
|
|
91
|
+
return formatEstimatedReward(context.reward, currency);
|
|
92
|
+
}, [context?.reward]);
|
|
93
|
+
const postPurchaseConfig = placement?.components?.postPurchase;
|
|
94
|
+
const texts = useMemo(() => {
|
|
95
|
+
const applyReward = (text) => rewardText ? text.replace("{REWARD}", rewardText) : text;
|
|
96
|
+
return {
|
|
97
|
+
message: resolvedVariant === "referee" ? rewardText ? applyReward(postPurchaseConfig?.refereeText ?? "You just earned {REWARD}! Share with friends to earn even more.") : postPurchaseConfig?.refereeNoRewardText ?? "You just earned a reward! Share with friends to earn even more." : rewardText ? applyReward(postPurchaseConfig?.referrerText ?? "Earn {REWARD} by sharing this with your friends!") : postPurchaseConfig?.referrerNoRewardText ?? "Share this with your friends and earn rewards!",
|
|
98
|
+
cta: rewardText ? applyReward(postPurchaseConfig?.ctaText ?? "Share & earn {REWARD}") : postPurchaseConfig?.ctaNoRewardText ?? "Share & earn"
|
|
99
|
+
};
|
|
100
|
+
}, [
|
|
101
|
+
resolvedVariant,
|
|
102
|
+
rewardText,
|
|
103
|
+
postPurchaseConfig
|
|
104
|
+
]);
|
|
105
|
+
const { handleShare } = useShareModal(void 0, placementId, resolvedSharingUrl);
|
|
106
|
+
if (!shouldRender || isHidden) return null;
|
|
107
|
+
if (!context || !resolvedVariant) return null;
|
|
108
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
109
|
+
class: ["post-purchase", classname].filter(Boolean).join(" "),
|
|
110
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
111
|
+
class: "post-purchase__content",
|
|
112
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
113
|
+
class: "post-purchase__message",
|
|
114
|
+
children: texts.message
|
|
115
|
+
})
|
|
116
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
117
|
+
type: "button",
|
|
118
|
+
class: "post-purchase__cta button",
|
|
119
|
+
disabled: !isClientReady,
|
|
120
|
+
onClick: handleShare,
|
|
121
|
+
children: texts.cta
|
|
122
|
+
})]
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/components/PostPurchase/index.ts
|
|
128
|
+
registerWebComponent(PostPurchase, "frak-post-purchase", [
|
|
129
|
+
"customerId",
|
|
130
|
+
"orderId",
|
|
131
|
+
"token",
|
|
132
|
+
"sharingUrl",
|
|
133
|
+
"merchantId",
|
|
134
|
+
"placement",
|
|
135
|
+
"classname",
|
|
136
|
+
"variant"
|
|
137
|
+
], { shadow: false });
|
|
138
|
+
|
|
139
|
+
//#endregion
|
|
140
|
+
export { PostPurchase };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as lightDomBaseCss } from "./usePlacement-BbMuz8_A.js";
|
|
2
2
|
import { useEffect } from "preact/hooks";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/styleManager.ts
|
|
@@ -26,9 +26,9 @@ const styleManager = {
|
|
|
26
26
|
|
|
27
27
|
//#endregion
|
|
28
28
|
//#region src/hooks/useLightDomStyles.ts
|
|
29
|
-
function useLightDomStyles(tag, placementId, placementCss) {
|
|
29
|
+
function useLightDomStyles(tag, placementId, placementCss, baseCss) {
|
|
30
30
|
useEffect(() => {
|
|
31
|
-
styleManager.injectBase(tag, lightDomBaseCss);
|
|
31
|
+
styleManager.injectBase(tag, baseCss ?? lightDomBaseCss);
|
|
32
32
|
}, [tag]);
|
|
33
33
|
useEffect(() => {
|
|
34
34
|
if (!placementId || !placementCss) return;
|