@frak-labs/components 1.0.10 → 1.0.11-beta.7400608e

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.
Files changed (49) hide show
  1. package/cdn/Banner.B5D7REJq.js +167 -0
  2. package/cdn/ButtonShare.C8i7dA4y.js +1 -0
  3. package/cdn/ButtonWallet.DwFVkAGJ.js +40 -0
  4. package/cdn/GiftIcon.DT1i36Fm.js +1 -0
  5. package/cdn/OpenInAppButton.D1UzTuHx.js +1 -0
  6. package/cdn/PostPurchase.CuucvS-o.js +89 -0
  7. package/cdn/components.js +1 -1
  8. package/cdn/defaults.kWEdCyGS.js +58 -0
  9. package/cdn/embeddedWallet.CDUxjdX2.js +1 -0
  10. package/cdn/loader.js +1 -1
  11. package/cdn/replay-V6FXES7X.CNozpSRg.js +1 -0
  12. package/cdn/rewards.B6IAemE2.js +1 -0
  13. package/cdn/useGlobalComponents.BC6Jgzg5.js +1 -0
  14. package/cdn/useLightDomStyles.imPXuAtf.js +1 -0
  15. package/cdn/useReward.IXcdcQPk.js +1 -0
  16. package/dist/{GiftIcon-WEWSJ8kV.js → GiftIcon-BEzYdiux.js} +5 -5
  17. package/dist/banner.d.ts +1 -1
  18. package/dist/banner.js +21 -16
  19. package/dist/buttonShare.d.ts +3 -1
  20. package/dist/buttonShare.js +11 -9
  21. package/dist/buttonWallet.js +6 -4
  22. package/dist/i18n/defaults.d.ts +54 -0
  23. package/dist/i18n/defaults.js +70 -0
  24. package/dist/openInApp.d.ts +4 -2
  25. package/dist/openInApp.js +7 -5
  26. package/dist/postPurchase.d.ts +1 -1
  27. package/dist/postPurchase.js +27 -16
  28. package/dist/rewards-B5VmlrcI.js +145 -0
  29. package/dist/useGlobalComponents-CZ7Sh2tZ.js +17 -0
  30. package/dist/{useLightDomStyles-DPKMhggr.js → useLightDomStyles-DphuQhvx.js} +1 -1
  31. package/dist/{usePlacement-DTN6H6vW.js → usePlacement-4PoDbn2X.js} +54 -12
  32. package/dist/useReward-DD2mMUN0.js +39 -0
  33. package/package.json +9 -3
  34. package/cdn/Banner.De5nN3cX.js +0 -167
  35. package/cdn/ButtonShare.DHSekTjv.js +0 -1
  36. package/cdn/ButtonWallet.Cvp_1Tx2.js +0 -40
  37. package/cdn/GiftIcon.DnSW4dJQ.js +0 -1
  38. package/cdn/OpenInAppButton.CpO_E-l-.js +0 -1
  39. package/cdn/PostPurchase.CuO1kjaz.js +0 -89
  40. package/cdn/embeddedWallet.BS-9u4Be.js +0 -1
  41. package/cdn/formatReward.DNFkBh_E.js +0 -1
  42. package/cdn/replay-V6FXES7X.BoL9fAjx.js +0 -1
  43. package/cdn/useGlobalComponents.pjgSwLk9.js +0 -1
  44. package/cdn/useLightDomStyles.2RQE6Iby.js +0 -1
  45. package/cdn/usePlacement.CRfzWKVY.js +0 -58
  46. package/cdn/useReward.DSMOMYCh.js +0 -1
  47. package/dist/formatReward-D3CkH7It.js +0 -39
  48. package/dist/useGlobalComponents-mSs9unyN.js +0 -21
  49. package/dist/useReward-DSrQsn3r.js +0 -65
@@ -0,0 +1,70 @@
1
+ //#region src/i18n/defaults.ts
2
+ /**
3
+ * Built-in, per-language default copy for the SDK Web Components.
4
+ *
5
+ * These strings are the last-resort fallback, used only when a merchant
6
+ * provides neither an HTML attribute override nor a backend-config override.
7
+ * They keep the components bilingual out of the box **without** pulling an
8
+ * i18n runtime (e.g. i18next) into the size-critical CDN bundle — resolution
9
+ * is a plain object lookup keyed by the active language (see `useLang`).
10
+ *
11
+ * Adding a new language is a matter of adding one entry here plus extending
12
+ * the `Language` union in `@frak-labs/core-sdk` — no new dependency, no
13
+ * merchant action, no SDK API change.
14
+ */
15
+ const componentDefaults = {
16
+ en: {
17
+ buttonShare: { text: "Share and earn!" },
18
+ postPurchase: {
19
+ refereeText: "You just earned {REWARD}! Share with friends to earn even more.",
20
+ refereeNoRewardText: "You just earned a reward! Share with friends to earn even more.",
21
+ referrerText: "Earn {REWARD} by sharing this with your friends!",
22
+ referrerNoRewardText: "Share this with your friends and earn rewards!",
23
+ ctaText: "Share & earn {REWARD}",
24
+ ctaNoRewardText: "Share & earn"
25
+ },
26
+ openInApp: {
27
+ text: "Open in App",
28
+ ariaLabel: "Open in Frak Wallet app"
29
+ },
30
+ buttonWallet: { ariaLabel: "Open wallet" },
31
+ banner: {
32
+ referralTitleReward: "Earn {REWARD} on purchases on this site",
33
+ referralTitle: "You've been referred!",
34
+ referralDescription: "Earn rewards after your purchase via the Frak partner app.",
35
+ referralCta: "Got it",
36
+ inappTitle: "Open in your browser",
37
+ inappDescription: "For a better experience and to earn your rewards, open this page in your default browser.",
38
+ inappCta: "Open browser",
39
+ dismissLabel: "Dismiss"
40
+ }
41
+ },
42
+ fr: {
43
+ buttonShare: { text: "Partagez et gagnez !" },
44
+ postPurchase: {
45
+ refereeText: "Vous venez de gagner {REWARD} ! Partagez avec vos amis pour gagner encore plus.",
46
+ refereeNoRewardText: "Vous venez de gagner une récompense ! Partagez avec vos amis pour gagner encore plus.",
47
+ referrerText: "Gagnez {REWARD} en partageant avec vos amis !",
48
+ referrerNoRewardText: "Partagez avec vos amis et gagnez des récompenses !",
49
+ ctaText: "Partagez et gagnez {REWARD}",
50
+ ctaNoRewardText: "Partagez et gagnez"
51
+ },
52
+ openInApp: {
53
+ text: "Ouvrir dans l'app",
54
+ ariaLabel: "Ouvrir dans l'app Frak Wallet"
55
+ },
56
+ buttonWallet: { ariaLabel: "Ouvrir le portefeuille" },
57
+ banner: {
58
+ referralTitleReward: "Gagnez {REWARD} sur vos achats sur ce site",
59
+ referralTitle: "Vous avez été parrainé !",
60
+ referralDescription: "Gagnez des récompenses après votre achat via l'application partenaire Frak.",
61
+ referralCta: "J'ai compris",
62
+ inappTitle: "Ouvrez dans votre navigateur",
63
+ inappDescription: "Pour une meilleure expérience et pour gagner vos récompenses, ouvrez cette page dans votre navigateur par défaut.",
64
+ inappCta: "Ouvrir le navigateur",
65
+ dismissLabel: "Fermer"
66
+ }
67
+ }
68
+ };
69
+ //#endregion
70
+ export { componentDefaults };
@@ -6,8 +6,10 @@
6
6
  type OpenInAppButtonProps = {
7
7
  placement?: string;
8
8
  /**
9
- * Text to display on the button
10
- * @defaultValue `"Open in App"`
9
+ * Text to display on the button.
10
+ *
11
+ * When omitted, a built-in localized default is used based on the
12
+ * resolved language (`"Open in App"` / `"Ouvrir dans l'app"`).
11
13
  */
12
14
  text?: string;
13
15
  /**
package/dist/openInApp.js CHANGED
@@ -1,5 +1,6 @@
1
- import { a as registerWebComponent, i as useClientReady, t as usePlacement } from "./usePlacement-DTN6H6vW.js";
2
- import { t as useLightDomStyles } from "./useLightDomStyles-DPKMhggr.js";
1
+ import { i as useLang, o as useClientReady, s as registerWebComponent, t as usePlacement } from "./usePlacement-4PoDbn2X.js";
2
+ import { t as useLightDomStyles } from "./useLightDomStyles-DphuQhvx.js";
3
+ import { componentDefaults } from "./i18n/defaults.js";
3
4
  import { DEEP_LINK_SCHEME, isMobile, trackEvent, triggerDeepLinkWithFallback } from "@frak-labs/core-sdk";
4
5
  import { useMemo } from "preact/hooks";
5
6
  import { jsx } from "preact/jsx-runtime";
@@ -58,12 +59,13 @@ function useIsMobile() {
58
59
  * <frak-open-in-app classname="button button-primary"></frak-open-in-app>
59
60
  * ```
60
61
  */
61
- function OpenInAppButton({ placement: placementId, text = "Open in App", classname = "" }) {
62
+ function OpenInAppButton({ placement: placementId, text, classname = "" }) {
63
+ const lang = useLang();
62
64
  const placement = usePlacement(placementId);
63
65
  const { shouldRender, isHidden, isClientReady } = useClientReady();
64
66
  const { isMobile } = useIsMobile();
65
67
  useLightDomStyles("frak-open-in-app", placementId, placement?.components?.openInApp?.css);
66
- const resolvedText = placement?.components?.openInApp?.text ?? text;
68
+ const resolvedText = placement?.components?.openInApp?.text ?? text ?? componentDefaults[lang].openInApp.text;
67
69
  if (!isMobile || !shouldRender || isHidden) return null;
68
70
  const handleClick = () => {
69
71
  openFrakWalletApp(void 0, placementId);
@@ -75,7 +77,7 @@ function OpenInAppButton({ placement: placementId, text = "Open in App", classna
75
77
  ].filter(Boolean).join(" ");
76
78
  return /* @__PURE__ */ jsx("button", {
77
79
  type: "button",
78
- "aria-label": "Open in Frak Wallet app",
80
+ "aria-label": componentDefaults[lang].openInApp.ariaLabel,
79
81
  disabled: !isClientReady,
80
82
  class: buttonClass,
81
83
  onClick: handleClick,
@@ -141,7 +141,7 @@ declare function PostPurchase({
141
141
  preview,
142
142
  previewVariant,
143
143
  products,
144
- imageUrl
144
+ imageUrl: propImageUrl
145
145
  }: PostPurchaseProps): import("preact").JSX.Element | null;
146
146
  //#endregion
147
147
  //#region src/components/PostPurchase/index.d.ts
@@ -1,8 +1,9 @@
1
- import { a as registerWebComponent, i as useClientReady, o as sanitizeProductList, s as openSharingPage, t as usePlacement } from "./usePlacement-DTN6H6vW.js";
2
- import { t as useGlobalComponents } from "./useGlobalComponents-mSs9unyN.js";
3
- import { t as useLightDomStyles } from "./useLightDomStyles-DPKMhggr.js";
4
- import { n as formatEstimatedReward, t as applyRewardPlaceholder } from "./formatReward-D3CkH7It.js";
5
- import { i as LogoFrakWithName, n as cssSource$2, t as GiftIcon } from "./GiftIcon-WEWSJ8kV.js";
1
+ import { c as sanitizeProductList, i as useLang, l as openSharingPage, o as useClientReady, s as registerWebComponent, t as usePlacement } from "./usePlacement-4PoDbn2X.js";
2
+ import { t as useGlobalComponents } from "./useGlobalComponents-CZ7Sh2tZ.js";
3
+ import { t as useLightDomStyles } from "./useLightDomStyles-DphuQhvx.js";
4
+ import { i as y, n as T, r as b } from "./rewards-B5VmlrcI.js";
5
+ import { componentDefaults } from "./i18n/defaults.js";
6
+ import { i as LogoFrakWithName, n as cssSource$2, t as GiftIcon } from "./GiftIcon-BEzYdiux.js";
6
7
  import { trackEvent } from "@frak-labs/core-sdk";
7
8
  import { getMerchantInformation, getUserReferralStatus, trackPurchaseStatus } from "@frak-labs/core-sdk/actions";
8
9
  import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks";
@@ -116,13 +117,19 @@ const cssSource = cssSource$1;
116
117
  * Given referral status and merchant info, compute the display variant
117
118
  * and pick the appropriate purchase reward.
118
119
  */
119
- function resolvePostPurchaseContext(referralStatus, merchantInfo) {
120
- const purchaseReward = merchantInfo.rewards.find((r) => r.interactionTypeKey === "purchase" && (r.referrer || r.referee));
121
- if (!purchaseReward) return null;
122
- const variant = referralStatus?.isReferred && purchaseReward.referee ? "referee" : "referrer";
120
+ function resolvePostPurchaseContext(referralStatus, merchantInfo, currency) {
121
+ const audience = referralStatus?.isReferred ? "referee" : "referrer";
122
+ const selected = T(merchantInfo.rewards, {
123
+ targetInteraction: "purchase",
124
+ currency,
125
+ audience
126
+ });
127
+ if (!selected) return null;
128
+ const { campaign } = selected;
129
+ const variant = referralStatus?.isReferred && campaign.referee ? "referee" : "referrer";
123
130
  return {
124
131
  variant,
125
- reward: variant === "referee" ? purchaseReward.referee : purchaseReward.referrer,
132
+ reward: variant === "referee" ? campaign.referee : campaign.referrer,
126
133
  merchantDomain: merchantInfo.onChainMetadata.domain
127
134
  };
128
135
  }
@@ -154,10 +161,11 @@ function resolvePostPurchaseContext(referralStatus, merchantInfo) {
154
161
  * ></frak-post-purchase>
155
162
  * ```
156
163
  */
157
- function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, placement: placementId, classname = "", variant: forcedVariant, badgeText: propBadgeText, referrerText: propReferrerText, refereeText: propRefereeText, ctaText: propCtaText, preview, previewVariant, products, imageUrl }) {
164
+ function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, placement: placementId, classname = "", variant: forcedVariant, badgeText: propBadgeText, referrerText: propReferrerText, refereeText: propRefereeText, ctaText: propCtaText, preview, previewVariant, products, imageUrl: propImageUrl }) {
158
165
  const isPreview = !!preview;
159
166
  const { shouldRender, isHidden, isClientReady } = useClientReady();
160
167
  const placement = usePlacement(placementId);
168
+ const lang = useLang();
161
169
  useLightDomStyles("frak-post-purchase", placementId, placement?.components?.postPurchase?.css, cssSource, cssSource$2);
162
170
  const [context, setContext] = useState(null);
163
171
  const [hasFetched, setHasFetched] = useState(false);
@@ -185,7 +193,7 @@ function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, plac
185
193
  if (!client) return;
186
194
  Promise.all([getUserReferralStatus(client), getMerchantInformation(client)]).then(([referralStatus, merchantInfo]) => {
187
195
  setHasFetched(true);
188
- setContext(resolvePostPurchaseContext(referralStatus, merchantInfo));
196
+ setContext(resolvePostPurchaseContext(referralStatus, merchantInfo, client.config.metadata?.currency));
189
197
  }).catch((e) => {
190
198
  if (e instanceof FrakRpcError && e.code === RpcErrorCodes.configError) {
191
199
  setHasFetched(true);
@@ -204,17 +212,20 @@ function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, plac
204
212
  const rewardText = useMemo(() => {
205
213
  if (!context?.reward) return void 0;
206
214
  const currency = window.FrakSetup?.client?.config?.metadata?.currency;
207
- return formatEstimatedReward(context.reward, currency);
215
+ return y(context.reward, currency);
208
216
  }, [context?.reward]);
209
217
  const globalComponents = useGlobalComponents();
210
218
  const postPurchaseConfig = placement?.components?.postPurchase ?? globalComponents?.postPurchase;
211
219
  const resolvedBadgeText = propBadgeText ?? postPurchaseConfig?.badgeText;
220
+ const imageUrl = propImageUrl ?? postPurchaseConfig?.imageUrl;
212
221
  const texts = useMemo(() => {
222
+ const defaults = componentDefaults[lang].postPurchase;
213
223
  return {
214
- message: resolvedVariant === "referee" ? rewardText ? applyRewardPlaceholder(propRefereeText ?? postPurchaseConfig?.refereeText ?? "You just earned {REWARD}! Share with friends to earn even more.", rewardText) : propRefereeText ?? postPurchaseConfig?.refereeNoRewardText ?? "You just earned a reward! Share with friends to earn even more." : rewardText ? applyRewardPlaceholder(propReferrerText ?? postPurchaseConfig?.referrerText ?? "Earn {REWARD} by sharing this with your friends!", rewardText) : propReferrerText ?? postPurchaseConfig?.referrerNoRewardText ?? "Share this with your friends and earn rewards!",
215
- cta: rewardText ? applyRewardPlaceholder(propCtaText ?? postPurchaseConfig?.ctaText ?? "Share & earn {REWARD}", rewardText) : propCtaText ?? postPurchaseConfig?.ctaNoRewardText ?? "Share & earn"
224
+ message: resolvedVariant === "referee" ? rewardText ? b(propRefereeText ?? postPurchaseConfig?.refereeText ?? defaults.refereeText, rewardText) : propRefereeText ?? postPurchaseConfig?.refereeNoRewardText ?? defaults.refereeNoRewardText : rewardText ? b(propReferrerText ?? postPurchaseConfig?.referrerText ?? defaults.referrerText, rewardText) : propReferrerText ?? postPurchaseConfig?.referrerNoRewardText ?? defaults.referrerNoRewardText,
225
+ cta: rewardText ? b(propCtaText ?? postPurchaseConfig?.ctaText ?? defaults.ctaText, rewardText) : propCtaText ?? postPurchaseConfig?.ctaNoRewardText ?? defaults.ctaNoRewardText
216
226
  };
217
227
  }, [
228
+ lang,
218
229
  resolvedVariant,
219
230
  rewardText,
220
231
  postPurchaseConfig,
@@ -289,7 +300,7 @@ function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, plac
289
300
  "aria-hidden": "true",
290
301
  className: `${icon} button`,
291
302
  children: /* @__PURE__ */ jsx("path", {
292
- d: "M13.8984 11.144C13.9864 11.052 14.1543 11.1114 14.1543 11.2388V11.644C14.1543 13.0509 12.6288 14.1918 10.7471 14.1919C8.86523 14.1919 7.33984 13.051 7.33984 11.644V11.2388C7.33984 11.1114 7.50675 11.052 7.59473 11.144C8.3452 11.9295 9.47906 12.4292 10.7471 12.4292C12.0149 12.4291 13.148 11.9293 13.8984 11.144ZM1.8457 9.64795C1.8457 9.51169 2.01094 9.44452 2.10254 9.54053C2.85304 10.3238 3.98586 10.8247 5.25293 10.8247C5.52246 10.8247 5.78608 10.8026 6.04102 10.7593C6.25744 10.7225 6.46582 10.8816 6.46582 11.1011V12.1704C6.46564 12.319 6.36769 12.4507 6.22266 12.4829C5.91535 12.5512 5.58981 12.5874 5.25293 12.5874C3.3711 12.5874 1.8457 11.4469 1.8457 10.0396V9.64795ZM10.7471 6.20654C12.6288 6.20666 14.1543 7.3475 14.1543 8.75439C14.1541 10.1612 12.6287 11.3012 10.7471 11.3013C8.86535 11.3013 7.34004 10.1612 7.33984 8.75439C7.33984 7.34743 8.86523 6.20654 10.7471 6.20654ZM1.8457 6.8501C1.84597 6.71385 2.01208 6.64848 2.10352 6.74365C2.85393 7.52827 3.98602 8.0278 5.25293 8.02783C5.52282 8.02783 5.78667 8.00448 6.04199 7.96143C6.258 7.92499 6.46582 8.08514 6.46582 8.3042V9.37256C6.46582 9.52127 6.36783 9.65378 6.22266 9.68604C5.91537 9.75429 5.58979 9.79053 5.25293 9.79053C3.3711 9.79048 1.8457 8.64863 1.8457 7.24268V6.8501ZM5.25293 1.80811C7.13481 1.80811 8.66016 2.94856 8.66016 4.35596C8.66008 5.76331 7.13476 6.90381 5.25293 6.90381C3.37115 6.90376 1.84578 5.76328 1.8457 4.35596C1.8457 2.94858 3.3711 1.80815 5.25293 1.80811Z",
303
+ d: "M13.9 11.14C13.99 11.05 14.15 11.11 14.15 11.24V11.64C14.15 13.05 12.63 14.19 10.75 14.19C8.87 14.19 7.34 13.05 7.34 11.64V11.24C7.34 11.11 7.51 11.05 7.59 11.14C8.35 11.93 9.48 12.43 10.75 12.43C12.01 12.43 13.15 11.93 13.9 11.14ZM1.85 9.65C1.85 9.51 2.01 9.44 2.1 9.54C2.85 10.32 3.99 10.82 5.25 10.82C5.52 10.82 5.79 10.8 6.04 10.76C6.26 10.72 6.47 10.88 6.47 11.1V12.17C6.47 12.32 6.37 12.45 6.22 12.48C5.92 12.55 5.59 12.59 5.25 12.59C3.37 12.59 1.85 11.45 1.85 10.04V9.65ZM10.75 6.21C12.63 6.21 14.15 7.35 14.15 8.75C14.15 10.16 12.63 11.3 10.75 11.3C8.87 11.3 7.34 10.16 7.34 8.75C7.34 7.35 8.87 6.21 10.75 6.21ZM1.85 6.85C1.85 6.71 2.01 6.65 2.1 6.74C2.85 7.53 3.99 8.03 5.25 8.03C5.52 8.03 5.79 8 6.04 7.96C6.26 7.92 6.47 8.09 6.47 8.3V9.37C6.47 9.52 6.37 9.65 6.22 9.69C5.92 9.75 5.59 9.79 5.25 9.79C3.37 9.79 1.85 8.65 1.85 7.24V6.85ZM5.25 1.81C7.13 1.81 8.66 2.95 8.66 4.36C8.66 5.76 7.13 6.9 5.25 6.9C3.37 6.9 1.85 5.76 1.85 4.36C1.85 2.95 3.37 1.81 5.25 1.81Z",
293
304
  fill: "currentColor"
294
305
  })
295
306
  })]
@@ -0,0 +1,145 @@
1
+ //#region ../core/dist/getCurrencyAmountKey-DQcwjJiB.js
2
+ const e = {
3
+ eur: `fr-FR`,
4
+ usd: `en-US`,
5
+ gbp: `en-GB`
6
+ };
7
+ function t(t) {
8
+ return t && t in e ? t : `eur`;
9
+ }
10
+ function n(t) {
11
+ return t ? e[t] ?? e.eur : e.eur;
12
+ }
13
+ function r$1(e, r) {
14
+ let i = n(r), a = t(r);
15
+ return e.toLocaleString(i, {
16
+ style: `currency`,
17
+ currency: a,
18
+ minimumFractionDigits: 0,
19
+ maximumFractionDigits: 2
20
+ });
21
+ }
22
+ function i$1(e) {
23
+ return e ? `${e}Amount` : `eurAmount`;
24
+ }
25
+ //#endregion
26
+ //#region ../core/dist/rewards.js
27
+ function r(e) {
28
+ return Array.isArray(e) ? e : e.conditions;
29
+ }
30
+ function i(e) {
31
+ if (typeof e == `number`) return e;
32
+ if (typeof e == `string` && e.trim() !== ``) {
33
+ let t = Number(e);
34
+ return Number.isFinite(t) ? t : void 0;
35
+ }
36
+ }
37
+ function a(e, t, n) {
38
+ let o = [];
39
+ for (let s of r(e)) {
40
+ if (`logic` in s) {
41
+ o.push(...a(s, t, n));
42
+ continue;
43
+ }
44
+ if (s.field === t && n.has(s.operator)) {
45
+ let e = i(s.value);
46
+ e != null && o.push(e);
47
+ }
48
+ }
49
+ return o;
50
+ }
51
+ const o = new Set([
52
+ `gt`,
53
+ `gte`,
54
+ `between`
55
+ ]), s = new Set([`gt`, `gte`]);
56
+ function c(e) {
57
+ let t = a(e, `purchase.amount`, o);
58
+ return t.length > 0 ? Math.min(...t) : void 0;
59
+ }
60
+ function l(e) {
61
+ let t = a(e, `time.timestamp`, s);
62
+ if (t.length !== 0) return /* @__PURE__ */ new Date(Math.min(...t) * 1e3);
63
+ }
64
+ function h(e, t) {
65
+ switch (e.payoutType) {
66
+ case `fixed`: return e.amount[t];
67
+ case `percentage`: return e.maxAmount?.[t] ?? 0;
68
+ case `tiered`: return e.tiers.reduce((e, n) => `amount` in n ? Math.max(e, n.amount[t]) : e, 0);
69
+ }
70
+ }
71
+ function g(e) {
72
+ return e.payoutType === `percentage` ? e.percent : e.payoutType === `tiered` ? e.tiers.reduce((e, t) => `percent` in t ? Math.max(e, t.percent) : e, 0) : 0;
73
+ }
74
+ function _(e, t) {
75
+ let n = h(e, t);
76
+ return n > 0 ? n : g(e) * 1e-6;
77
+ }
78
+ function v(r, i) {
79
+ let a = t(i), o = i$1(a);
80
+ switch (r.payoutType) {
81
+ case `fixed`: return r$1(Math.round(r.amount[o]), a);
82
+ case `percentage`: return `${r.percent} %`;
83
+ case `tiered`: {
84
+ let t = h(r, o);
85
+ if (t > 0) return r$1(Math.round(t), a);
86
+ let n = g(r);
87
+ return n > 0 ? `${n} %` : r$1(0, a);
88
+ }
89
+ }
90
+ }
91
+ function y(e, r) {
92
+ if (e && !(_(e, i$1(t(r))) <= 0)) return v(e, r);
93
+ }
94
+ function b(e, t) {
95
+ return t ? e.replaceAll(`{REWARD}`, t) : e.replaceAll(`{REWARD}`, ``);
96
+ }
97
+ function x(e, t) {
98
+ return e.expiresAt != null && new Date(e.expiresAt).getTime() <= t;
99
+ }
100
+ function S(e, t) {
101
+ let n = l(e.conditions);
102
+ return n == null || n.getTime() <= t;
103
+ }
104
+ function C(e, t) {
105
+ return t === `referee` ? e.referee : e.referrer;
106
+ }
107
+ function w(e, t, n) {
108
+ let r = C(e, n);
109
+ return r ? _(r, t) : 0;
110
+ }
111
+ function T(e, r = {}) {
112
+ let i = (r.now ?? /* @__PURE__ */ new Date()).getTime(), a = r.audience ?? `referrer`, o = i$1(t(r.currency)), s = (r.targetInteraction ? e.filter((e) => e.interactionTypeKey === r.targetInteraction) : e).filter((e) => !x(e, i)), c = s.filter((e) => S(e, i));
113
+ if (c.length > 0) return {
114
+ campaign: c.reduce((e, t) => w(t, o, a) > w(e, o, a) ? t : e),
115
+ status: `live`
116
+ };
117
+ let u = s.map((e) => ({
118
+ campaign: e,
119
+ startsAt: l(e.conditions)
120
+ })).filter((e) => e.startsAt != null);
121
+ if (u.length === 0) return;
122
+ let d = u.reduce((e, t) => t.startsAt.getTime() < e.startsAt.getTime() ? t : e);
123
+ return {
124
+ campaign: d.campaign,
125
+ status: `upcoming`,
126
+ startsAt: d.startsAt
127
+ };
128
+ }
129
+ function E(t, n = {}) {
130
+ let r = T(t, n);
131
+ if (!r) return;
132
+ let i = C(r.campaign, n.audience ?? `referrer`);
133
+ if (!i) return;
134
+ let a = y(i, n.currency);
135
+ if (!a) return;
136
+ let o = c(r.campaign.conditions), s = o == null ? void 0 : r$1(o, n.currency), l = r.campaign.defaultLockupSeconds, u = l && l > 0 ? Math.round(l / 86400) : void 0;
137
+ return {
138
+ formatted: a,
139
+ payoutType: i.payoutType,
140
+ minPurchaseAmount: s,
141
+ lockupDurationDays: u
142
+ };
143
+ }
144
+ //#endregion
145
+ export { y as i, T as n, b as r, E as t };
@@ -0,0 +1,17 @@
1
+ import { a as subscribeSdkConfig } from "./usePlacement-4PoDbn2X.js";
2
+ import { sdkConfigStore } from "@frak-labs/core-sdk";
3
+ import { useSyncExternalStore } from "preact/compat";
4
+ //#region src/hooks/useGlobalComponents.ts
5
+ /**
6
+ * Subscribe to the global component defaults from the SDK config store.
7
+ * These serve as fallbacks when no placement-level override exists.
8
+ *
9
+ * Backed by `useSyncExternalStore`: the snapshot is the stored `components`
10
+ * object reference (stable between `frak:config` dispatches), so the component
11
+ * only re-renders when the global components actually change.
12
+ */
13
+ function useGlobalComponents() {
14
+ return useSyncExternalStore(subscribeSdkConfig, () => sdkConfigStore.getConfig().components);
15
+ }
16
+ //#endregion
17
+ export { useGlobalComponents as t };
@@ -1,4 +1,4 @@
1
- import { r as lightDomBaseCss } from "./usePlacement-DTN6H6vW.js";
1
+ import { r as lightDomBaseCss } from "./usePlacement-4PoDbn2X.js";
2
2
  import { useEffect } from "preact/hooks";
3
3
  //#region src/styles/styleManager.ts
4
4
  function ensureStyle(id, css) {
@@ -3,7 +3,8 @@ import * as coreSdkIndex from "@frak-labs/core-sdk";
3
3
  import { decompressJsonFromB64, deleteQueryParamCaseInsensitive, getQueryParamCaseInsensitive, sdkConfigStore, setupClient, trackEvent, withCache } from "@frak-labs/core-sdk";
4
4
  import * as coreSdkActions from "@frak-labs/core-sdk/actions";
5
5
  import { displaySharingPage } from "@frak-labs/core-sdk/actions";
6
- import { useEffect, useMemo, useState } from "preact/hooks";
6
+ import { useCallback, useEffect, useState } from "preact/hooks";
7
+ import { useSyncExternalStore } from "preact/compat";
7
8
  //#region src/actions/sharingPage.ts
8
9
  async function openSharingPage(targetInteraction, placement, options) {
9
10
  if (!window.FrakSetup?.client) {
@@ -341,6 +342,49 @@ function useClientReady() {
341
342
  };
342
343
  }
343
344
  //#endregion
345
+ //#region src/hooks/sdkConfigSubscription.ts
346
+ /**
347
+ * Shared `useSyncExternalStore` plumbing for the SDK config store.
348
+ *
349
+ * The resolved merchant config lives on `window.__frakSdkConfig` and notifies
350
+ * via the `frak:config` CustomEvent (see `@frak-labs/core-sdk` `sdkConfigStore`).
351
+ * A single module-level `subscribe` keeps the reference stable across renders
352
+ * so `useSyncExternalStore` never re-subscribes, and lets every config-driven
353
+ * hook (`useLang`, `usePlacement`, `useGlobalComponents`) share one listener
354
+ * shape instead of each hand-rolling an effect + version counter.
355
+ */
356
+ /**
357
+ * Subscribe to resolved-config changes. Stable reference (module-level) so
358
+ * `useSyncExternalStore` treats it as a constant store subscription.
359
+ */
360
+ function subscribeSdkConfig(onStoreChange) {
361
+ if (typeof window === "undefined") return () => {};
362
+ window.addEventListener("frak:config", onStoreChange);
363
+ return () => window.removeEventListener("frak:config", onStoreChange);
364
+ }
365
+ //#endregion
366
+ //#region src/hooks/useLang.ts
367
+ /**
368
+ * Detect a supported language from the browser, defaulting to English.
369
+ * Only `en` / `fr` are recognised — anything else falls back to `en`.
370
+ */
371
+ function detectBrowserLang() {
372
+ if (typeof navigator === "undefined") return "en";
373
+ return navigator.language?.split("-")[0] === "fr" ? "fr" : "en";
374
+ }
375
+ /**
376
+ * Resolve the active display language for the Web Components.
377
+ *
378
+ * Precedence: resolved SDK/backend config `lang` (driven by `metadata.lang`
379
+ * or the backend `/resolve` response) → browser language → `en`. Backed by
380
+ * `useSyncExternalStore` with a bare-string snapshot, so a `frak:config`
381
+ * dispatch only re-renders the component when the resolved language actually
382
+ * changes (see `@/i18n/defaults`).
383
+ */
384
+ function useLang() {
385
+ return useSyncExternalStore(subscribeSdkConfig, () => sdkConfigStore.getConfig().lang ?? detectBrowserLang());
386
+ }
387
+ //#endregion
344
388
  //#region src/styles/sharedCss.ts
345
389
  const sharedCss = `
346
390
  :host {
@@ -409,17 +453,15 @@ const lightDomBaseCss = `
409
453
  function getPlacement(id) {
410
454
  return sdkConfigStore.getConfig().placements?.[id];
411
455
  }
456
+ /**
457
+ * Subscribe to a single resolved placement from the SDK config store.
458
+ *
459
+ * Backed by `useSyncExternalStore`: the snapshot is the stored placement
460
+ * object reference (stable between `frak:config` dispatches), so the component
461
+ * only re-renders when that placement actually changes.
462
+ */
412
463
  function usePlacement(placementId) {
413
- const [configVersion, setConfigVersion] = useState(0);
414
- useEffect(() => {
415
- const onConfig = (_e) => {
416
- setConfigVersion((v) => v + 1);
417
- };
418
- window.addEventListener("frak:config", onConfig);
419
- setConfigVersion((v) => v + 1);
420
- return () => window.removeEventListener("frak:config", onConfig);
421
- }, []);
422
- return useMemo(() => placementId ? getPlacement(placementId) : void 0, [placementId, configVersion]);
464
+ return useSyncExternalStore(subscribeSdkConfig, useCallback(() => placementId ? getPlacement(placementId) : void 0, [placementId]));
423
465
  }
424
466
  //#endregion
425
- export { registerWebComponent as a, useClientReady as i, buildStyleContent as n, sanitizeProductList as o, lightDomBaseCss as r, openSharingPage as s, usePlacement as t };
467
+ export { subscribeSdkConfig as a, sanitizeProductList as c, useLang as i, openSharingPage as l, buildStyleContent as n, useClientReady as o, lightDomBaseCss as r, registerWebComponent as s, usePlacement as t };
@@ -0,0 +1,39 @@
1
+ import { t as E } from "./rewards-B5VmlrcI.js";
2
+ import { getMerchantInformation } from "@frak-labs/core-sdk/actions";
3
+ import { useEffect, useState } from "preact/hooks";
4
+ //#region src/hooks/useReward.ts
5
+ /**
6
+ * Hook to fetch and format the best reward for a given interaction type.
7
+ *
8
+ * Calls `getMerchantInformation`, picks the highest-value reward across all
9
+ * matching live campaigns for the requested `audience` side, and returns it as
10
+ * a formatted string.
11
+ *
12
+ * @param shouldUseReward - Whether to fetch the reward at all
13
+ * @param targetInteraction - Optional filter by interaction type (e.g. "purchase")
14
+ * @param audience - Reward side to display: `"referrer"` (default) or `"referee"`
15
+ * @returns Object containing the formatted reward string, or undefined if unavailable
16
+ */
17
+ function useReward(shouldUseReward, targetInteraction, audience) {
18
+ const [reward, setReward] = useState(void 0);
19
+ useEffect(() => {
20
+ if (!shouldUseReward) return;
21
+ const client = window.FrakSetup?.client;
22
+ if (!client) return;
23
+ getMerchantInformation(client).then((merchantInfo) => {
24
+ const best = E(merchantInfo.rewards, {
25
+ currency: client.config.metadata?.currency,
26
+ targetInteraction,
27
+ audience
28
+ });
29
+ if (best && best.payoutType !== "percentage") setReward(best.formatted);
30
+ }).catch(() => {});
31
+ }, [
32
+ shouldUseReward,
33
+ targetInteraction,
34
+ audience
35
+ ]);
36
+ return { reward };
37
+ }
38
+ //#endregion
39
+ export { useReward as t };
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "url": "https://twitter.com/QNivelais"
12
12
  }
13
13
  ],
14
- "version": "1.0.10",
14
+ "version": "1.0.11-beta.7400608e",
15
15
  "description": "Frak Wallet components, helping any person to interact with the Frak wallet.",
16
16
  "repository": {
17
17
  "url": "https://github.com/frak-id/wallet",
@@ -66,6 +66,12 @@
66
66
  "import": "./dist/banner.js",
67
67
  "types": "./dist/banner.d.ts"
68
68
  },
69
+ "./i18n/defaults": {
70
+ "development": "./src/i18n/defaults.ts",
71
+ "production": "./src/i18n/defaults.ts",
72
+ "import": "./dist/i18n/defaults.js",
73
+ "types": "./dist/i18n/defaults.d.ts"
74
+ },
69
75
  "./cdn": {
70
76
  "import": "./cdn/components.js"
71
77
  },
@@ -86,8 +92,8 @@
86
92
  "publish": "echo 'Publishing components...'"
87
93
  },
88
94
  "dependencies": {
89
- "@frak-labs/core-sdk": "1.1.5",
90
- "@frak-labs/frame-connector": "0.2.0",
95
+ "@frak-labs/core-sdk": "1.1.6-beta.7400608e",
96
+ "@frak-labs/frame-connector": "0.2.0-beta.7400608e",
91
97
  "preact": "^10.29.0",
92
98
  "preact-custom-element": "^4.6.0",
93
99
  "@frak-labs/design-system": "0.0.0"