@blocklet/payment-react 1.18.2 → 1.18.4

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 (43) hide show
  1. package/es/checkout/donate.d.ts +5 -3
  2. package/es/checkout/donate.js +109 -36
  3. package/es/contexts/donate.d.ts +41 -0
  4. package/es/contexts/donate.js +164 -0
  5. package/es/contexts/payment.js +3 -16
  6. package/es/index.d.ts +2 -0
  7. package/es/index.js +2 -0
  8. package/es/libs/cache.d.ts +14 -0
  9. package/es/libs/cache.js +23 -0
  10. package/es/libs/cached-request.d.ts +17 -0
  11. package/es/libs/cached-request.js +79 -0
  12. package/es/libs/util.d.ts +2 -0
  13. package/es/libs/util.js +13 -0
  14. package/es/locales/en.js +8 -1
  15. package/es/locales/zh.js +8 -1
  16. package/es/payment/skeleton/donation.js +1 -1
  17. package/lib/checkout/donate.d.ts +5 -3
  18. package/lib/checkout/donate.js +107 -36
  19. package/lib/contexts/donate.d.ts +41 -0
  20. package/lib/contexts/donate.js +181 -0
  21. package/lib/contexts/payment.js +7 -22
  22. package/lib/index.d.ts +2 -0
  23. package/lib/index.js +24 -0
  24. package/lib/libs/cache.d.ts +14 -0
  25. package/lib/libs/cache.js +29 -0
  26. package/lib/libs/cached-request.d.ts +17 -0
  27. package/lib/libs/cached-request.js +90 -0
  28. package/lib/libs/util.d.ts +2 -0
  29. package/lib/libs/util.js +12 -0
  30. package/lib/locales/en.js +8 -1
  31. package/lib/locales/zh.js +8 -1
  32. package/lib/payment/skeleton/donation.js +1 -1
  33. package/package.json +8 -8
  34. package/src/checkout/donate.tsx +135 -45
  35. package/src/contexts/donate.tsx +226 -0
  36. package/src/contexts/payment.tsx +5 -20
  37. package/src/index.ts +2 -0
  38. package/src/libs/cache.ts +33 -0
  39. package/src/libs/cached-request.ts +103 -0
  40. package/src/libs/util.ts +15 -0
  41. package/src/locales/en.tsx +7 -0
  42. package/src/locales/zh.tsx +7 -0
  43. package/src/payment/skeleton/donation.tsx +1 -1
@@ -7,12 +7,14 @@ export type DonateHistory = {
7
7
  method: TPaymentMethod;
8
8
  totalAmount: string;
9
9
  };
10
+ export type RequiredDonationSettings = Pick<DonationSettings, 'target' | 'title' | 'description' | 'reference' | 'beneficiaries'>;
11
+ type OptionalDonationSettings = Partial<Omit<DonationSettings, keyof RequiredDonationSettings>>;
10
12
  export interface ButtonType extends Omit<MUIButtonProps, 'text' | 'icon'> {
11
- text: string | React.ReactNode;
13
+ text?: string | React.ReactNode;
12
14
  icon: React.ReactNode;
13
15
  }
14
16
  export type DonateProps = Pick<CheckoutProps, 'onPaid' | 'onError'> & {
15
- settings: DonationSettings;
17
+ settings: RequiredDonationSettings & OptionalDonationSettings;
16
18
  livemode?: boolean;
17
19
  timeout?: number;
18
20
  mode?: 'inline' | 'default' | 'custom';
@@ -20,7 +22,7 @@ export type DonateProps = Pick<CheckoutProps, 'onPaid' | 'onError'> & {
20
22
  button?: ButtonType;
21
23
  };
22
24
  theme?: 'default' | 'inherit' | PaymentThemeOptions;
23
- children?: (openDialog: () => void, donateTotalAmount: string, supporters: DonateHistory, loading?: boolean) => React.ReactNode;
25
+ children?: (openDialog: () => void, donateTotalAmount: string, supporters: DonateHistory, loading?: boolean, donateSettings?: DonationSettings) => React.ReactNode;
24
26
  };
25
27
  declare function CheckoutDonate(props: DonateProps): import("react").JSX.Element;
26
28
  declare namespace CheckoutDonate {
@@ -9,26 +9,38 @@ import {
9
9
  Button,
10
10
  CircularProgress,
11
11
  Hidden,
12
+ IconButton,
12
13
  Popover,
13
14
  Stack,
14
15
  Table,
15
16
  TableBody,
16
17
  TableCell,
17
18
  TableRow,
18
- Typography
19
+ Typography,
20
+ Tooltip
19
21
  } from "@mui/material";
20
- import { useMount, useRequest, useSetState } from "ahooks";
22
+ import { useRequest, useSetState } from "ahooks";
21
23
  import omit from "lodash/omit";
22
24
  import uniqBy from "lodash/unionBy";
23
- import { useEffect, useState } from "react";
25
+ import { useEffect, useRef, useState } from "react";
26
+ import { Settings } from "@mui/icons-material";
24
27
  import TxLink from "../components/blockchain/tx.js";
25
28
  import api from "../libs/api.js";
26
- import { formatAmount, formatBNStr, formatDateTime, formatError, getCustomerAvatar, lazyLoad } from "../libs/util.js";
29
+ import {
30
+ formatAmount,
31
+ formatBNStr,
32
+ formatDateTime,
33
+ formatError,
34
+ getCustomerAvatar,
35
+ lazyLoad,
36
+ openDonationSettings
37
+ } from "../libs/util.js";
27
38
  import CheckoutForm from "./form.js";
28
39
  import { PaymentThemeProvider } from "../theme/index.js";
29
40
  import { usePaymentContext } from "../contexts/payment.js";
30
41
  import Livemode from "../components/livemode.js";
31
42
  import { useMobile } from "../hooks/mobile.js";
43
+ import { useDonateContext } from "../contexts/donate.js";
32
44
  const donationCache = {};
33
45
  const createOrUpdateDonation = (settings, livemode = true) => {
34
46
  const donationKey = `${settings.target}-${livemode}`;
@@ -184,13 +196,39 @@ function SupporterSimple({ supporters = [], totalAmount = "0", currency, method
184
196
  }
185
197
  );
186
198
  }
199
+ const defaultDonateAmount = {
200
+ presets: ["1", "5", "10"],
201
+ preset: "1",
202
+ minimum: "0.01",
203
+ maximum: "100",
204
+ custom: true
205
+ };
187
206
  function useDonation(settings, livemode, mode = "default") {
188
207
  const [state, setState] = useSetState({
189
208
  open: false,
190
209
  supporterLoaded: false,
191
210
  exist: false
192
211
  });
193
- const donation = useRequest(() => createOrUpdateDonation(settings, livemode), {
212
+ const donateContext = useDonateContext();
213
+ const { isMobile } = useMobile();
214
+ const { settings: donateConfig = {} } = donateContext || {};
215
+ const donateSettings = {
216
+ ...settings,
217
+ amount: settings.amount || donateConfig?.settings?.amount || defaultDonateAmount,
218
+ appearance: {
219
+ button: {
220
+ ...settings?.appearance?.button || {},
221
+ text: settings?.appearance?.button?.text || donateConfig?.settings?.btnText || "Donate",
222
+ icon: settings?.appearance?.button?.icon || donateConfig?.settings?.icon || null
223
+ },
224
+ history: {
225
+ variant: settings?.appearance?.history?.variant || donateConfig?.settings?.historyType || "avatar"
226
+ }
227
+ }
228
+ };
229
+ const hasRequestedRef = useRef(false);
230
+ const containerRef = useRef(null);
231
+ const donation = useRequest(() => createOrUpdateDonation(donateSettings, livemode), {
194
232
  manual: true,
195
233
  loadingDelay: 300
196
234
  });
@@ -201,14 +239,28 @@ function useDonation(settings, livemode, mode = "default") {
201
239
  loadingDelay: 300
202
240
  }
203
241
  );
204
- useMount(() => {
205
- if (mode !== "inline") {
206
- lazyLoad(() => {
207
- donation.run();
208
- supporters.run();
209
- });
210
- }
211
- });
242
+ const rootMargin = isMobile ? "50px" : `${Math.min(window.innerHeight / 2, 300)}px`;
243
+ useEffect(() => {
244
+ if (mode === "inline")
245
+ return;
246
+ const element = containerRef.current;
247
+ if (!element)
248
+ return;
249
+ const observer = new IntersectionObserver(
250
+ ([entry]) => {
251
+ if (entry.isIntersecting && !hasRequestedRef.current) {
252
+ hasRequestedRef.current = true;
253
+ lazyLoad(() => {
254
+ donation.run();
255
+ supporters.run();
256
+ });
257
+ }
258
+ },
259
+ { threshold: 0, rootMargin }
260
+ );
261
+ observer.observe(element);
262
+ return () => observer.unobserve(element);
263
+ }, [mode]);
212
264
  useEffect(() => {
213
265
  if (donation.data && state.supporterLoaded === false) {
214
266
  setState({ supporterLoaded: true });
@@ -216,10 +268,13 @@ function useDonation(settings, livemode, mode = "default") {
216
268
  }
217
269
  }, [donation.data]);
218
270
  return {
271
+ containerRef,
219
272
  donation,
220
273
  supporters,
221
274
  state,
222
- setState
275
+ setState,
276
+ donateSettings,
277
+ supportUpdateSettings: !!donateContext
223
278
  };
224
279
  }
225
280
  function CheckoutDonateInner({
@@ -233,13 +288,17 @@ function CheckoutDonateInner({
233
288
  theme,
234
289
  children
235
290
  }) {
236
- const { state, setState, donation, supporters } = useDonation(settings, livemode, mode);
291
+ const { containerRef, state, setState, donation, supporters, donateSettings, supportUpdateSettings } = useDonation(
292
+ settings,
293
+ livemode,
294
+ mode
295
+ );
237
296
  const customers = uniqBy(supporters?.data?.supporters || [], "customer_did");
238
297
  const { t } = useLocaleContext();
239
298
  const [anchorEl, setAnchorEl] = useState(null);
240
299
  const [popoverOpen, setPopoverOpen] = useState(false);
241
300
  const { isMobile } = useMobile();
242
- const { connect } = usePaymentContext();
301
+ const { connect, session } = usePaymentContext();
243
302
  const handlePaid = (...args) => {
244
303
  if (onPaid) {
245
304
  onPaid(...args);
@@ -264,18 +323,19 @@ function CheckoutDonateInner({
264
323
  const startDonate = () => {
265
324
  setState({ open: true });
266
325
  };
326
+ const inlineText = inlineOptions?.button?.text || donateSettings.appearance.button.text;
267
327
  const inlineRender = /* @__PURE__ */ jsxs(Fragment, { children: [
268
328
  /* @__PURE__ */ jsx(
269
329
  Button,
270
330
  {
271
- size: settings.appearance?.button?.size || "medium",
272
- color: settings.appearance?.button?.color || "primary",
273
- variant: settings.appearance?.button?.variant || "contained",
274
- ...settings.appearance?.button,
331
+ size: donateSettings.appearance?.button?.size || "medium",
332
+ color: donateSettings.appearance?.button?.color || "primary",
333
+ variant: donateSettings.appearance?.button?.variant || "contained",
334
+ ...donateSettings.appearance?.button,
275
335
  onClick: handlePopoverOpen,
276
336
  children: /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [
277
- settings.appearance.button.icon,
278
- typeof settings.appearance.button.text === "string" ? /* @__PURE__ */ jsx(Typography, { sx: { whiteSpace: "nowrap" }, children: settings.appearance.button.text }) : settings.appearance.button.text
337
+ donateSettings.appearance.button.icon,
338
+ typeof donateSettings.appearance.button.text === "string" ? /* @__PURE__ */ jsx(Typography, { sx: { whiteSpace: "nowrap" }, children: donateSettings.appearance.button.text }) : donateSettings.appearance.button.text
279
339
  ] })
280
340
  }
281
341
  ),
@@ -322,7 +382,7 @@ function CheckoutDonateInner({
322
382
  /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", flexDirection: "column", gap: 2, children: [
323
383
  /* @__PURE__ */ jsx(Button, { ...inlineOptions.button, onClick: () => startDonate(), children: /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [
324
384
  inlineOptions?.button?.icon,
325
- typeof inlineOptions?.button?.text === "string" ? /* @__PURE__ */ jsx(Typography, { sx: { whiteSpace: "nowrap" }, children: inlineOptions?.button?.text }) : inlineOptions?.button?.text
385
+ typeof inlineText === "string" ? /* @__PURE__ */ jsx(Typography, { sx: { whiteSpace: "nowrap" }, children: inlineText }) : inlineText
326
386
  ] }) }),
327
387
  /* @__PURE__ */ jsx(SupporterSimple, { ...supporters.data })
328
388
  ] })
@@ -344,19 +404,19 @@ function CheckoutDonateInner({
344
404
  /* @__PURE__ */ jsx(
345
405
  Button,
346
406
  {
347
- size: settings.appearance?.button?.size || "medium",
348
- color: settings.appearance?.button?.color || "primary",
349
- variant: settings.appearance?.button?.variant || "contained",
350
- ...settings.appearance?.button,
407
+ size: donateSettings.appearance?.button?.size || "medium",
408
+ color: donateSettings.appearance?.button?.color || "primary",
409
+ variant: donateSettings.appearance?.button?.variant || "contained",
410
+ ...donateSettings.appearance?.button,
351
411
  onClick: () => startDonate(),
352
412
  children: /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [
353
- settings.appearance.button.icon,
354
- typeof settings.appearance.button.text === "string" ? /* @__PURE__ */ jsx(Typography, { children: settings.appearance.button.text }) : settings.appearance.button.text
413
+ donateSettings.appearance.button.icon,
414
+ typeof donateSettings.appearance.button.text === "string" ? /* @__PURE__ */ jsx(Typography, { children: donateSettings.appearance.button.text }) : donateSettings.appearance.button.text
355
415
  ] })
356
416
  }
357
417
  ),
358
- supporters.data && settings.appearance.history.variant === "avatar" && /* @__PURE__ */ jsx(SupporterAvatar, { ...supporters.data }),
359
- supporters.data && settings.appearance.history.variant === "table" && /* @__PURE__ */ jsx(SupporterTable, { ...supporters.data })
418
+ supporters.data && donateSettings.appearance.history.variant === "avatar" && /* @__PURE__ */ jsx(SupporterAvatar, { ...supporters.data }),
419
+ supporters.data && donateSettings.appearance.history.variant === "table" && /* @__PURE__ */ jsx(SupporterTable, { ...supporters.data })
360
420
  ]
361
421
  }
362
422
  );
@@ -372,7 +432,8 @@ function CheckoutDonateInner({
372
432
  supporters.data?.currency?.decimal
373
433
  )} ${supporters.data?.currency?.symbol}`,
374
434
  supporters.data || {},
375
- !!supporters.loading
435
+ !!supporters.loading,
436
+ donateSettings
376
437
  ) }) : /* @__PURE__ */ jsxs(Typography, { children: [
377
438
  "Please provide a valid render function ",
378
439
  /* @__PURE__ */ jsx("pre", { children: "(openDonate, donateTotalAmount, supporters) => ReactNode" })
@@ -380,15 +441,27 @@ function CheckoutDonateInner({
380
441
  }
381
442
  return defaultRender;
382
443
  };
383
- return /* @__PURE__ */ jsxs(Fragment, { children: [
444
+ const isAdmin = ["owner", "admin"].includes(session?.user?.role);
445
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, children: [
384
446
  renderInnerView(),
385
447
  donation.data && /* @__PURE__ */ jsx(
386
448
  Dialog,
387
449
  {
388
450
  open: state.open,
389
451
  title: /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", gap: 0.5, children: [
390
- /* @__PURE__ */ jsx(Typography, { variant: "h3", sx: { maxWidth: 320, textOverflow: "ellipsis", overflow: "hidden" }, children: settings.title }),
391
- !donation.data.livemode && /* @__PURE__ */ jsx(Livemode, { sx: { width: "fit-content" } })
452
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", sx: { maxWidth: 320, textOverflow: "ellipsis", overflow: "hidden" }, children: donateSettings.title }),
453
+ supportUpdateSettings && isAdmin && /* @__PURE__ */ jsx(Tooltip, { title: t("payment.checkout.donation.configTip"), placement: "bottom", children: /* @__PURE__ */ jsx(
454
+ IconButton,
455
+ {
456
+ size: "small",
457
+ onClick: (e) => {
458
+ e.stopPropagation();
459
+ openDonationSettings(true);
460
+ },
461
+ children: /* @__PURE__ */ jsx(Settings, { fontSize: "small", sx: { ml: -0.5 } })
462
+ }
463
+ ) }),
464
+ !donation.data.livemode && /* @__PURE__ */ jsx(Livemode, { sx: { width: "fit-content", ml: 0.5 } })
392
465
  ] }),
393
466
  maxWidth: "md",
394
467
  toolbar: isMobile ? null : /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", gap: 1, sx: { color: "text.secondary" }, children: [
@@ -430,7 +503,7 @@ function CheckoutDonateInner({
430
503
  id: donation.data?.id,
431
504
  onPaid: handlePaid,
432
505
  onError,
433
- action: settings.appearance?.button?.text,
506
+ action: donateSettings.appearance?.button?.text,
434
507
  mode: "inline",
435
508
  theme,
436
509
  formType: "donation",
@@ -0,0 +1,41 @@
1
+ import type { TSetting } from '@blocklet/payment-types';
2
+ import type { Axios } from 'axios';
3
+ export interface DonateConfigSettings {
4
+ amount?: {
5
+ presets?: string[];
6
+ preset?: string;
7
+ custom: boolean;
8
+ minimum?: string;
9
+ maximum?: string;
10
+ };
11
+ btnText?: string;
12
+ historyType?: 'table' | 'avatar';
13
+ }
14
+ export type DonateContextType = {
15
+ settings: TSetting;
16
+ refresh: (forceRefresh?: boolean) => void;
17
+ updateSettings: (newSettings: DonateConfigSettings) => Promise<void>;
18
+ api: Axios;
19
+ };
20
+ export type DonateContextProps = {
21
+ mountLocation: string;
22
+ description: string;
23
+ defaultSettings?: DonateConfigSettings;
24
+ children: any;
25
+ active?: boolean;
26
+ enableDonate?: boolean;
27
+ };
28
+ declare const DonateContext: import("react").Context<DonateContextType>;
29
+ declare const Consumer: import("react").Consumer<DonateContextType>;
30
+ declare function DonateProvider({ mountLocation, description, defaultSettings, children, active, enableDonate, }: DonateContextProps): import("react").JSX.Element | null;
31
+ declare namespace DonateProvider {
32
+ var defaultProps: {
33
+ defaultSettings: {};
34
+ active: boolean;
35
+ enableDonate: boolean;
36
+ };
37
+ }
38
+ declare function useDonateContext(): DonateContextType;
39
+ export declare const clearDonateCache: (mountLocation: string) => void;
40
+ export declare const clearDonateSettings: (mountLocation: string) => Promise<void>;
41
+ export { DonateContext, DonateProvider, Consumer as DonateConsumer, useDonateContext };
@@ -0,0 +1,164 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { useRequest } from "ahooks";
3
+ import { createContext, useContext, useState } from "react";
4
+ import Toast from "@arcblock/ux/lib/Toast";
5
+ import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
6
+ import { Button, Stack, Typography } from "@mui/material";
7
+ import api from "../libs/api.js";
8
+ import { formatError, getPaymentKitComponent, openDonationSettings } from "../libs/util.js";
9
+ import { CachedRequest } from "../libs/cached-request.js";
10
+ import ConfirmDialog from "../components/confirm.js";
11
+ const DonateContext = createContext({ api });
12
+ const { Provider, Consumer } = DonateContext;
13
+ const fetchDonateSetting = (params, forceRefresh = false) => {
14
+ const livemode = localStorage.getItem("livemode") !== "false";
15
+ const cacheKey = `donate-settings-${params.mountLocation}-${livemode}`;
16
+ const cachedRequest = new CachedRequest(
17
+ cacheKey,
18
+ () => api.post("/api/settings", {
19
+ ...params,
20
+ type: "donate",
21
+ livemode,
22
+ settings: params.defaultSettings
23
+ }),
24
+ {
25
+ ttl: 1e3 * 60 * 60,
26
+ strategy: "local"
27
+ }
28
+ );
29
+ return cachedRequest.fetch(forceRefresh);
30
+ };
31
+ function DonateProvider({
32
+ mountLocation,
33
+ description,
34
+ defaultSettings = {},
35
+ children,
36
+ active = true,
37
+ enableDonate = false
38
+ }) {
39
+ const { t } = useLocaleContext();
40
+ const [showConfirm, setShowConfirm] = useState(false);
41
+ const {
42
+ data = {
43
+ settings: {},
44
+ active: true
45
+ },
46
+ error,
47
+ run,
48
+ loading
49
+ } = useRequest(
50
+ (forceRender) => fetchDonateSetting(
51
+ {
52
+ mountLocation,
53
+ description,
54
+ defaultSettings,
55
+ active,
56
+ componentDid: window.blocklet?.componentId?.split("/").pop()
57
+ },
58
+ forceRender
59
+ ),
60
+ {
61
+ refreshDeps: [mountLocation],
62
+ onError: (err) => {
63
+ Toast.error(formatError(err));
64
+ }
65
+ }
66
+ );
67
+ const updateSettings = async (newSettings) => {
68
+ try {
69
+ const livemode = localStorage.getItem("livemode") !== "false";
70
+ await api.put(`/api/settings/${mountLocation}`, {
71
+ livemode,
72
+ settings: newSettings
73
+ });
74
+ run(true);
75
+ Toast.success(t("common.saved"));
76
+ } catch (err) {
77
+ Toast.error(formatError(err));
78
+ throw err;
79
+ }
80
+ };
81
+ const supportPaymentKit = getPaymentKitComponent();
82
+ const handleEnable = async () => {
83
+ if (!enableDonate || !data || data?.active)
84
+ return;
85
+ try {
86
+ await api.put(`/api/settings/${data.id}`, { active: true });
87
+ if (supportPaymentKit) {
88
+ setShowConfirm(true);
89
+ } else {
90
+ Toast.success(t("payment.checkout.donation.enableSuccess"));
91
+ run(true);
92
+ }
93
+ } catch (err) {
94
+ Toast.error(formatError(err));
95
+ }
96
+ };
97
+ if (loading || error) {
98
+ return null;
99
+ }
100
+ return /* @__PURE__ */ jsx(
101
+ Provider,
102
+ {
103
+ value: {
104
+ settings: data,
105
+ refresh: run,
106
+ updateSettings,
107
+ api
108
+ },
109
+ children: data?.active === false ? /* @__PURE__ */ jsxs(Fragment, { children: [
110
+ enableDonate && /* @__PURE__ */ jsxs(Stack, { spacing: 1, sx: { p: 2, bgcolor: "background.neutral", borderRadius: 1 }, children: [
111
+ /* @__PURE__ */ jsx(Typography, { color: "text.secondary", children: t("payment.checkout.donation.inactive") }),
112
+ /* @__PURE__ */ jsx(Button, { size: "small", variant: "outlined", color: "primary", onClick: handleEnable, children: t("payment.checkout.donation.enable") })
113
+ ] }),
114
+ showConfirm && /* @__PURE__ */ jsx(
115
+ ConfirmDialog,
116
+ {
117
+ title: t("payment.checkout.donation.enableSuccess"),
118
+ message: t("payment.checkout.donation.configPrompt"),
119
+ cancel: t("payment.checkout.donation.later"),
120
+ confirm: t("payment.checkout.donation.configNow"),
121
+ onCancel: () => {
122
+ setShowConfirm(false);
123
+ run(true);
124
+ },
125
+ color: "primary",
126
+ onConfirm: () => {
127
+ run(true);
128
+ openDonationSettings(false);
129
+ setShowConfirm(false);
130
+ }
131
+ }
132
+ )
133
+ ] }) : children
134
+ }
135
+ );
136
+ }
137
+ function useDonateContext() {
138
+ const context = useContext(DonateContext);
139
+ return context;
140
+ }
141
+ DonateProvider.defaultProps = {
142
+ defaultSettings: {},
143
+ active: true,
144
+ enableDonate: false
145
+ };
146
+ export const clearDonateCache = (mountLocation) => {
147
+ const livemode = localStorage.getItem("livemode") !== "false";
148
+ const cacheKey = `donate-settings-${mountLocation}-${livemode}`;
149
+ localStorage.removeItem(cacheKey);
150
+ };
151
+ export const clearDonateSettings = async (mountLocation) => {
152
+ try {
153
+ const livemode = localStorage.getItem("livemode") !== "false";
154
+ await api.delete(`/api/settings/${mountLocation}`, {
155
+ params: {
156
+ livemode
157
+ }
158
+ });
159
+ clearDonateCache(mountLocation);
160
+ } catch (err) {
161
+ Toast.error(formatError(err));
162
+ }
163
+ };
164
+ export { DonateContext, DonateProvider, Consumer as DonateConsumer, useDonateContext };
@@ -4,27 +4,14 @@ import { useLocalStorageState, useRequest } from "ahooks";
4
4
  import { createContext, useContext, useState } from "react";
5
5
  import api from "../libs/api.js";
6
6
  import { getPrefix } from "../libs/util.js";
7
+ import { CachedRequest } from "../libs/cached-request.js";
7
8
  const PaymentContext = createContext({ api });
8
9
  const { Provider, Consumer } = PaymentContext;
9
- let settingsPromise = null;
10
10
  const getSettings = (forceRefresh = false) => {
11
11
  const livemode = localStorage.getItem("livemode") !== "false";
12
12
  const cacheKey = `payment-settings-${window.location.pathname}-${livemode}`;
13
- const cachedData = sessionStorage.getItem(cacheKey);
14
- if (cachedData && !forceRefresh) {
15
- return JSON.parse(cachedData);
16
- }
17
- if (!settingsPromise) {
18
- settingsPromise = api.get("/api/settings", { params: { livemode } }).then(({ data }) => {
19
- sessionStorage.setItem(cacheKey, JSON.stringify(data));
20
- return data;
21
- }).catch((error) => {
22
- throw error;
23
- }).finally(() => {
24
- settingsPromise = null;
25
- });
26
- }
27
- return settingsPromise;
13
+ const cachedRequest = new CachedRequest(cacheKey, () => api.get("/api/settings", { params: { livemode } }));
14
+ return cachedRequest.fetch(forceRefresh);
28
15
  };
29
16
  const getCurrency = (currencyId, methods) => {
30
17
  const currencies = methods.reduce((acc, x) => acc.concat(x.payment_currencies), []);
package/es/index.d.ts CHANGED
@@ -34,7 +34,9 @@ export { PaymentThemeProvider } from './theme';
34
34
  export * from './libs/util';
35
35
  export * from './libs/connect';
36
36
  export * from './libs/phone-validator';
37
+ export * from './libs/cached-request';
37
38
  export * from './contexts/payment';
39
+ export * from './contexts/donate';
38
40
  export * from './hooks/subscription';
39
41
  export * from './hooks/mobile';
40
42
  export * from './hooks/table';
package/es/index.js CHANGED
@@ -34,7 +34,9 @@ export { PaymentThemeProvider } from "./theme/index.js";
34
34
  export * from "./libs/util.js";
35
35
  export * from "./libs/connect.js";
36
36
  export * from "./libs/phone-validator.js";
37
+ export * from "./libs/cached-request.js";
37
38
  export * from "./contexts/payment.js";
39
+ export * from "./contexts/donate.js";
38
40
  export * from "./hooks/subscription.js";
39
41
  export * from "./hooks/mobile.js";
40
42
  export * from "./hooks/table.js";
@@ -0,0 +1,14 @@
1
+ type CacheItem = {
2
+ promise: Promise<any> | null;
3
+ };
4
+ declare class GlobalCacheManager {
5
+ private static instance;
6
+ private cacheMap;
7
+ private constructor();
8
+ static getInstance(): GlobalCacheManager;
9
+ get(cacheKey: string): CacheItem | undefined;
10
+ set(cacheKey: string, item: CacheItem): void;
11
+ delete(cacheKey: string): void;
12
+ }
13
+ export declare const globalCache: GlobalCacheManager;
14
+ export {};
@@ -0,0 +1,23 @@
1
+ class GlobalCacheManager {
2
+ static instance;
3
+ cacheMap;
4
+ constructor() {
5
+ this.cacheMap = /* @__PURE__ */ new Map();
6
+ }
7
+ static getInstance() {
8
+ if (!GlobalCacheManager.instance) {
9
+ GlobalCacheManager.instance = new GlobalCacheManager();
10
+ }
11
+ return GlobalCacheManager.instance;
12
+ }
13
+ get(cacheKey) {
14
+ return this.cacheMap.get(cacheKey);
15
+ }
16
+ set(cacheKey, item) {
17
+ this.cacheMap.set(cacheKey, item);
18
+ }
19
+ delete(cacheKey) {
20
+ this.cacheMap.delete(cacheKey);
21
+ }
22
+ }
23
+ export const globalCache = GlobalCacheManager.getInstance();
@@ -0,0 +1,17 @@
1
+ type CacheStrategy = 'session' | 'local' | 'memory';
2
+ interface CacheOptions {
3
+ strategy?: CacheStrategy;
4
+ ttl?: number;
5
+ }
6
+ export declare class CachedRequest {
7
+ private cacheKey;
8
+ private fetchData;
9
+ private options;
10
+ constructor(cacheKey: string, fetchData: () => Promise<any>, options?: CacheOptions);
11
+ private getCache;
12
+ private getCachedData;
13
+ private setCachedData;
14
+ private clearCache;
15
+ fetch(forceRefresh?: boolean): Promise<any>;
16
+ }
17
+ export {};