@blocklet/payment-react 1.15.33 → 1.15.35

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 (40) hide show
  1. package/es/checkout/donate.d.ts +2 -1
  2. package/es/checkout/donate.js +119 -94
  3. package/es/components/pricing-table.d.ts +3 -1
  4. package/es/components/pricing-table.js +39 -16
  5. package/es/history/invoice/list.d.ts +1 -0
  6. package/es/history/invoice/list.js +15 -1
  7. package/es/hooks/scroll.d.ts +1 -0
  8. package/es/hooks/scroll.js +14 -0
  9. package/es/index.d.ts +1 -0
  10. package/es/index.js +1 -0
  11. package/es/libs/util.d.ts +7 -1
  12. package/es/libs/util.js +47 -0
  13. package/es/locales/en.js +11 -2
  14. package/es/locales/zh.js +12 -3
  15. package/es/payment/product-donation.js +2 -0
  16. package/lib/checkout/donate.d.ts +2 -1
  17. package/lib/checkout/donate.js +127 -108
  18. package/lib/components/pricing-table.d.ts +3 -1
  19. package/lib/components/pricing-table.js +33 -18
  20. package/lib/history/invoice/list.d.ts +1 -0
  21. package/lib/history/invoice/list.js +20 -1
  22. package/lib/hooks/scroll.d.ts +1 -0
  23. package/lib/hooks/scroll.js +22 -0
  24. package/lib/index.d.ts +1 -0
  25. package/lib/index.js +12 -0
  26. package/lib/libs/util.d.ts +7 -1
  27. package/lib/libs/util.js +57 -0
  28. package/lib/locales/en.js +11 -2
  29. package/lib/locales/zh.js +12 -3
  30. package/lib/payment/product-donation.js +2 -0
  31. package/package.json +3 -3
  32. package/src/checkout/donate.tsx +127 -95
  33. package/src/components/pricing-table.tsx +34 -14
  34. package/src/history/invoice/list.tsx +20 -1
  35. package/src/hooks/scroll.ts +16 -0
  36. package/src/index.ts +1 -0
  37. package/src/libs/util.ts +56 -0
  38. package/src/locales/en.tsx +9 -0
  39. package/src/locales/zh.tsx +10 -1
  40. package/src/payment/product-donation.tsx +2 -0
package/lib/locales/zh.js CHANGED
@@ -95,7 +95,9 @@ module.exports = (0, _flat.default)({
95
95
  quantityLimitPerCheckout: "\u8D85\u51FA\u8D2D\u4E70\u9650\u5236",
96
96
  quantityNotEnough: "\u5E93\u5B58\u4E0D\u8DB3",
97
97
  amountPrecisionLimit: "\u91D1\u989D\u5C0F\u6570\u4F4D\u6570\u5FC5\u987B\u5728 {precision} \u4F4D\u4EE5\u5185",
98
- saveAsDefaultPriceSuccess: "\u8BBE\u7F6E\u9ED8\u8BA4\u4EF7\u683C\u6210\u529F"
98
+ saveAsDefaultPriceSuccess: "\u8BBE\u7F6E\u9ED8\u8BA4\u4EF7\u683C\u6210\u529F",
99
+ stakeAmount: "\u8D28\u62BC\u91D1\u989D",
100
+ slashStakeAmount: "\u7F5A\u6CA1\u91D1\u989D"
99
101
  },
100
102
  payment: {
101
103
  checkout: {
@@ -305,14 +307,21 @@ module.exports = (0, _flat.default)({
305
307
  reason: {
306
308
  creation: "\u8BA2\u9605\u521B\u5EFA",
307
309
  cycle: "\u81EA\u52A8\u6263\u8D39",
308
- staking: "\u8BA2\u9605\u521B\u5EFA\uFF08\u8D28\u62BC\uFF09",
310
+ staking: "\u8BA2\u9605\u521B\u5EFA",
309
311
  update: "\u8BA2\u9605\u66F4\u65B0",
310
312
  recover: "\u8BA2\u9605\u6062\u590D",
311
313
  threshold: "\u7528\u91CF\u8D26\u5355",
312
314
  cancel: "\u8BA2\u9605\u53D6\u6D88",
313
315
  manual: "\u4EBA\u5DE5\u8D26\u5355",
314
316
  upcoming: "\u672A\u6765\u8D26\u5355",
315
- slashStake: "\u7F5A\u6CA1\u8D28\u62BC"
317
+ slashStake: "\u7F5A\u6CA1\u8D28\u62BC",
318
+ stake: "\u62BC\u91D1",
319
+ payment: "\u4ED8\u6B3E",
320
+ returnStake: "\u9000\u62BC\u91D1",
321
+ stakeForChangePlan: "\u8BA2\u9605\u5957\u9910\u66F4\u65B0",
322
+ stakeForChangePayment: "\u8BA2\u9605\u652F\u4ED8\u65B9\u5F0F\u66F4\u65B0",
323
+ recharge: "\u5145\u503C",
324
+ rechargeForSubscription: "\u8BA2\u9605\u5145\u503C"
316
325
  }
317
326
  }
318
327
  },
@@ -12,6 +12,7 @@ var _react = require("react");
12
12
  var _switchButton = _interopRequireDefault(require("../components/switch-button"));
13
13
  var _util = require("../libs/util");
14
14
  var _payment = require("../contexts/payment");
15
+ var _scroll = require("../hooks/scroll");
15
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
17
  function ProductDonation({
17
18
  item,
@@ -26,6 +27,7 @@ function ProductDonation({
26
27
  const {
27
28
  setPayable
28
29
  } = (0, _payment.usePaymentContext)();
30
+ (0, _scroll.usePreventWheel)();
29
31
  const preset = settings.amount.preset || settings.amount.presets?.[0] || "0";
30
32
  const [state, setState] = (0, _ahooks.useSetState)({
31
33
  selected: preset,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/payment-react",
3
- "version": "1.15.33",
3
+ "version": "1.15.35",
4
4
  "description": "Reusable react components for payment kit v2",
5
5
  "keywords": [
6
6
  "react",
@@ -92,7 +92,7 @@
92
92
  "@babel/core": "^7.25.2",
93
93
  "@babel/preset-env": "^7.25.2",
94
94
  "@babel/preset-react": "^7.24.7",
95
- "@blocklet/payment-types": "1.15.33",
95
+ "@blocklet/payment-types": "1.15.35",
96
96
  "@storybook/addon-essentials": "^7.6.20",
97
97
  "@storybook/addon-interactions": "^7.6.20",
98
98
  "@storybook/addon-links": "^7.6.20",
@@ -122,5 +122,5 @@
122
122
  "vite-plugin-babel": "^1.2.0",
123
123
  "vite-plugin-node-polyfills": "^0.21.0"
124
124
  },
125
- "gitHead": "88e0986e379d9ed673435ea96305f16b4a54097e"
125
+ "gitHead": "279b6f2fee57a20cffdc78c0c1cf3a140cd142c4"
126
126
  }
@@ -57,11 +57,12 @@ export type DonateProps = Pick<CheckoutProps, 'onPaid' | 'onError'> & {
57
57
  settings: DonationSettings;
58
58
  livemode?: boolean;
59
59
  timeout?: number;
60
- mode?: 'inline' | 'default';
60
+ mode?: 'inline' | 'default' | 'custom';
61
61
  inlineOptions?: {
62
62
  button?: ButtonType;
63
63
  };
64
64
  theme?: 'default' | 'inherit' | PaymentThemeOptions;
65
+ children?: (openDialog: () => void, donateTotalAmount: string, supporters: DonateHistory) => React.ReactNode;
65
66
  };
66
67
 
67
68
  const donationCache: { [key: string]: Promise<TPaymentLink> } = {};
@@ -315,6 +316,7 @@ function CheckoutDonateInner({
315
316
  mode,
316
317
  inlineOptions = {},
317
318
  theme,
319
+ children,
318
320
  }: DonateProps) {
319
321
  // eslint-disable-line
320
322
  const { state, setState, donation, supporters } = useDonation(settings, livemode, mode);
@@ -350,105 +352,135 @@ function CheckoutDonateInner({
350
352
  setPopoverOpen(false);
351
353
  };
352
354
 
353
- return (
355
+ const startDonate = () => {
356
+ setState({ open: true });
357
+ };
358
+
359
+ const inlineRender = (
354
360
  <>
355
- {mode === 'inline' ? (
356
- <>
357
- <Button
358
- size={(settings.appearance?.button?.size || 'medium') as any}
359
- color={(settings.appearance?.button?.color || 'primary') as any}
360
- variant={(settings.appearance?.button?.variant || 'contained') as any}
361
- {...settings.appearance?.button}
362
- onClick={handlePopoverOpen}>
363
- <Stack direction="row" alignItems="center" spacing={0.5}>
364
- {settings.appearance.button.icon}
365
- {typeof settings.appearance.button.text === 'string' ? (
366
- <Typography sx={{ whiteSpace: 'nowrap' }}>{settings.appearance.button.text}</Typography>
367
- ) : (
368
- settings.appearance.button.text
369
- )}
370
- </Stack>
371
- </Button>
372
- <Popover
373
- id="mouse-over-popper"
374
- open={popoverOpen}
375
- anchorEl={anchorEl}
376
- onClose={handlePopoverClose}
377
- anchorOrigin={{
378
- vertical: 'top',
379
- horizontal: 'center',
380
- }}
381
- transformOrigin={{
382
- vertical: 'bottom',
383
- horizontal: 'center',
384
- }}>
385
- <Box
386
- sx={{
387
- minWidth: 320,
388
- padding: '20px',
389
- }}>
390
- {supporters.loading && (
391
- <div
392
- style={{
393
- position: 'absolute',
394
- top: 0,
395
- left: 0,
396
- right: 0,
397
- bottom: 0,
398
- display: 'flex',
399
- justifyContent: 'center',
400
- alignItems: 'center',
401
- backgroundColor: 'rgba(255, 255, 255, 0.7)',
402
- }}>
403
- <CircularProgress />
404
- </div>
405
- )}
406
- <Box display="flex" alignItems="center" flexDirection="column" gap={2}>
407
- <Button {...inlineOptions.button} onClick={() => setState({ open: true })}>
408
- <Stack direction="row" alignItems="center" spacing={0.5}>
409
- {inlineOptions?.button?.icon}
410
- {typeof inlineOptions?.button?.text === 'string' ? (
411
- <Typography sx={{ whiteSpace: 'nowrap' }}>{inlineOptions?.button?.text}</Typography>
412
- ) : (
413
- inlineOptions?.button?.text
414
- )}
415
- </Stack>
416
- </Button>
417
- <SupporterSimple {...(supporters.data as DonateHistory)} />
418
- </Box>
419
- </Box>
420
- </Popover>
421
- </>
422
- ) : (
423
- <Box
424
- sx={{ width: '100%', minWidth: 300, maxWidth: 720 }}
425
- display="flex"
426
- flexDirection="column"
427
- alignItems="center"
428
- gap={{ xs: 1, sm: 2 }}>
429
- <Button
430
- size={(settings.appearance?.button?.size || 'medium') as any}
431
- color={(settings.appearance?.button?.color || 'primary') as any}
432
- variant={(settings.appearance?.button?.variant || 'contained') as any}
433
- {...settings.appearance?.button}
434
- onClick={() => setState({ open: true })}>
435
- <Stack direction="row" alignItems="center" spacing={0.5}>
436
- {settings.appearance.button.icon}
437
- {typeof settings.appearance.button.text === 'string' ? (
438
- <Typography>{settings.appearance.button.text}</Typography>
439
- ) : (
440
- settings.appearance.button.text
441
- )}
442
- </Stack>
443
- </Button>
444
- {supporters.data && settings.appearance.history.variant === 'avatar' && (
445
- <SupporterAvatar {...(supporters.data as DonateHistory)} />
361
+ <Button
362
+ size={(settings.appearance?.button?.size || 'medium') as any}
363
+ color={(settings.appearance?.button?.color || 'primary') as any}
364
+ variant={(settings.appearance?.button?.variant || 'contained') as any}
365
+ {...settings.appearance?.button}
366
+ onClick={handlePopoverOpen}>
367
+ <Stack direction="row" alignItems="center" spacing={0.5}>
368
+ {settings.appearance.button.icon}
369
+ {typeof settings.appearance.button.text === 'string' ? (
370
+ <Typography sx={{ whiteSpace: 'nowrap' }}>{settings.appearance.button.text}</Typography>
371
+ ) : (
372
+ settings.appearance.button.text
446
373
  )}
447
- {supporters.data && settings.appearance.history.variant === 'table' && (
448
- <SupporterTable {...(supporters.data as DonateHistory)} />
374
+ </Stack>
375
+ </Button>
376
+ <Popover
377
+ id="mouse-over-popper"
378
+ open={popoverOpen}
379
+ anchorEl={anchorEl}
380
+ onClose={handlePopoverClose}
381
+ anchorOrigin={{
382
+ vertical: 'top',
383
+ horizontal: 'center',
384
+ }}
385
+ transformOrigin={{
386
+ vertical: 'bottom',
387
+ horizontal: 'center',
388
+ }}>
389
+ <Box
390
+ sx={{
391
+ minWidth: 320,
392
+ padding: '20px',
393
+ }}>
394
+ {supporters.loading && (
395
+ <div
396
+ style={{
397
+ position: 'absolute',
398
+ top: 0,
399
+ left: 0,
400
+ right: 0,
401
+ bottom: 0,
402
+ display: 'flex',
403
+ justifyContent: 'center',
404
+ alignItems: 'center',
405
+ backgroundColor: 'rgba(255, 255, 255, 0.7)',
406
+ }}>
407
+ <CircularProgress />
408
+ </div>
449
409
  )}
410
+ <Box display="flex" alignItems="center" flexDirection="column" gap={2}>
411
+ <Button {...inlineOptions.button} onClick={() => startDonate()}>
412
+ <Stack direction="row" alignItems="center" spacing={0.5}>
413
+ {inlineOptions?.button?.icon}
414
+ {typeof inlineOptions?.button?.text === 'string' ? (
415
+ <Typography sx={{ whiteSpace: 'nowrap' }}>{inlineOptions?.button?.text}</Typography>
416
+ ) : (
417
+ inlineOptions?.button?.text
418
+ )}
419
+ </Stack>
420
+ </Button>
421
+ <SupporterSimple {...(supporters.data as DonateHistory)} />
422
+ </Box>
450
423
  </Box>
424
+ </Popover>
425
+ </>
426
+ );
427
+ const defaultRender = (
428
+ <Box
429
+ sx={{ width: '100%', minWidth: 300, maxWidth: 720 }}
430
+ display="flex"
431
+ flexDirection="column"
432
+ alignItems="center"
433
+ gap={{ xs: 1, sm: 2 }}>
434
+ <Button
435
+ size={(settings.appearance?.button?.size || 'medium') as any}
436
+ color={(settings.appearance?.button?.color || 'primary') as any}
437
+ variant={(settings.appearance?.button?.variant || 'contained') as any}
438
+ {...settings.appearance?.button}
439
+ onClick={() => startDonate()}>
440
+ <Stack direction="row" alignItems="center" spacing={0.5}>
441
+ {settings.appearance.button.icon}
442
+ {typeof settings.appearance.button.text === 'string' ? (
443
+ <Typography>{settings.appearance.button.text}</Typography>
444
+ ) : (
445
+ settings.appearance.button.text
446
+ )}
447
+ </Stack>
448
+ </Button>
449
+ {supporters.data && settings.appearance.history.variant === 'avatar' && (
450
+ <SupporterAvatar {...(supporters.data as DonateHistory)} />
451
451
  )}
452
+ {supporters.data && settings.appearance.history.variant === 'table' && (
453
+ <SupporterTable {...(supporters.data as DonateHistory)} />
454
+ )}
455
+ </Box>
456
+ );
457
+ const renderInnerView = () => {
458
+ if (mode === 'inline') {
459
+ return inlineRender;
460
+ }
461
+ if (mode === 'custom') {
462
+ return children && typeof children === 'function' ? (
463
+ <>
464
+ {children(
465
+ startDonate,
466
+ `${formatAmount(
467
+ (supporters.data as DonateHistory)?.totalAmount || '0',
468
+ (supporters.data as DonateHistory)?.currency?.decimal
469
+ )} ${(supporters.data as DonateHistory)?.currency?.symbol}`,
470
+ (supporters.data as DonateHistory) || {}
471
+ )}
472
+ </>
473
+ ) : (
474
+ <Typography>
475
+ Please provide a valid render function <pre>{'(openDonate, donateTotalAmount, supporters) => ReactNode'}</pre>
476
+ </Typography>
477
+ );
478
+ }
479
+ return defaultRender;
480
+ };
481
+ return (
482
+ <>
483
+ {renderInnerView()}
452
484
  {donation.data && (
453
485
  <Dialog
454
486
  open={state.open}
@@ -33,7 +33,6 @@ import {
33
33
  getPriceUintAmountByCurrency,
34
34
  isMobileSafari,
35
35
  } from '../libs/util';
36
- import Amount from '../payment/amount';
37
36
  import { useMobile } from '../hooks/mobile';
38
37
  import TruncatedText from './truncated-text';
39
38
 
@@ -72,15 +71,17 @@ type Props = {
72
71
  alignItems?: 'center' | 'left';
73
72
  mode?: 'checkout' | 'select';
74
73
  interval?: string;
74
+ hideCurrency?: boolean;
75
75
  };
76
76
 
77
77
  PricingTable.defaultProps = {
78
78
  alignItems: 'center',
79
79
  mode: 'checkout',
80
80
  interval: '',
81
+ hideCurrency: false,
81
82
  };
82
83
 
83
- export default function PricingTable({ table, alignItems, interval, mode, onSelect }: Props) {
84
+ export default function PricingTable({ table, alignItems, interval, mode, onSelect, hideCurrency }: Props) {
84
85
  const { t, locale } = useLocaleContext();
85
86
  const { isMobile } = useMobile();
86
87
  const {
@@ -201,10 +202,10 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
201
202
  max-width: 360px !important;
202
203
  }
203
204
  .price-table-wrap:has(> div:nth-child(2)) {
204
- max-width: 720px !important;
205
+ max-width: 780px !important;
205
206
  }
206
207
  .price-table-wrap:has(> div:nth-child(3)) {
207
- max-width: 1080px !important;
208
+ max-width: 1200px !important;
208
209
  }
209
210
  }
210
211
  `;
@@ -291,7 +292,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
291
292
  )}
292
293
  </Box>
293
294
  )}
294
- {currencyList.length > 0 && (
295
+ {currencyList.length > 0 && !hideCurrency && (
295
296
  <Select
296
297
  value={currency?.id}
297
298
  onChange={(e) => setCurrency(currencyList.find((v) => v?.id === e.target.value))}
@@ -325,6 +326,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
325
326
  if (mode === 'select') {
326
327
  action = x.is_selected ? t('payment.checkout.selected') : t('payment.checkout.select');
327
328
  }
329
+ const [amount, unit] = formatPriceAmount(x.price, currency, x.product.unit_label).split('/');
328
330
  return (
329
331
  <Stack
330
332
  key={x?.price_id}
@@ -349,14 +351,13 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
349
351
  },
350
352
  width: {
351
353
  xs: '100%',
352
- md: '320px',
354
+ md: '360px',
353
355
  },
354
356
  maxWidth: '360px',
355
357
  minWidth: '300px',
356
358
 
357
359
  padding: '20px',
358
360
  position: 'relative',
359
- height: 'fit-content',
360
361
  }}>
361
362
  <Box textAlign="center">
362
363
  <Stack
@@ -367,7 +368,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
367
368
  sx={{ gap: '12px' }}>
368
369
  <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
369
370
  <Typography
370
- color="text.primary"
371
+ color="text.secondary"
371
372
  fontWeight={600}
372
373
  sx={{
373
374
  fontSize: '18px !important',
@@ -378,7 +379,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
378
379
  {x.is_highlight && (
379
380
  <Chip
380
381
  label={x.highlight_text}
381
- color="default"
382
+ color="primary"
382
383
  size="small"
383
384
  sx={{
384
385
  position: 'absolute',
@@ -388,10 +389,29 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
388
389
  />
389
390
  )}
390
391
  </Box>
391
- <Amount
392
- amount={formatPriceAmount(x.price, currency, x.product.unit_label)}
393
- sx={{ my: 0, marginTop: '0px !important', fontSize: '48px', fontWeight: 'bold' }}
394
- />
392
+ <Typography
393
+ component="div"
394
+ sx={{
395
+ my: 0,
396
+ fontWeight: '700',
397
+ fontSize: '32px',
398
+ letterSpacing: '-0.03rem',
399
+ fontVariantNumeric: 'tabular-nums',
400
+ display: 'flex',
401
+ alignItems: 'baseline',
402
+ gap: '4px',
403
+ flexWrap: 'wrap',
404
+ }}>
405
+ {amount}
406
+ {unit ? (
407
+ <Typography
408
+ sx={{ fontSize: '16px', fontWeight: '400', color: 'text.secondary', textAlign: 'left' }}>
409
+ / {unit}
410
+ </Typography>
411
+ ) : (
412
+ ''
413
+ )}
414
+ </Typography>
395
415
  <Typography
396
416
  color="text.secondary"
397
417
  sx={{
@@ -471,7 +491,7 @@ function Subscribe({ x, action, onSelect, currencyId }: any) {
471
491
  fullWidth
472
492
  size="medium"
473
493
  variant="contained"
474
- color={x.is_highlight || x.is_selected ? 'info' : 'primary'}
494
+ color="primary"
475
495
  sx={{
476
496
  fontSize: '16px',
477
497
  padding: '10px 20px',
@@ -23,6 +23,7 @@ import {
23
23
  formatError,
24
24
  formatToDate,
25
25
  formatToDatetime,
26
+ getInvoiceDescriptionAndReason,
26
27
  getInvoiceStatusColor,
27
28
  getPrefix,
28
29
  getTxLink,
@@ -58,6 +59,7 @@ type Props = {
58
59
  subscription_id?: string;
59
60
  currency_id?: string;
60
61
  include_staking?: boolean;
62
+ include_return_staking?: boolean;
61
63
  include_recovered_from?: boolean;
62
64
  status?: string;
63
65
  pageSize?: number;
@@ -97,6 +99,7 @@ const InvoiceTable = React.memo((props: Props & { onPay: (invoiceId: string) =>
97
99
  currency_id,
98
100
  subscription_id,
99
101
  include_staking,
102
+ include_return_staking,
100
103
  include_recovered_from,
101
104
  onTableDataChange,
102
105
  } = props;
@@ -120,6 +123,7 @@ const InvoiceTable = React.memo((props: Props & { onPay: (invoiceId: string) =>
120
123
  currency_id,
121
124
  subscription_id,
122
125
  include_staking,
126
+ include_return_staking,
123
127
  include_recovered_from,
124
128
  ignore_zero: true,
125
129
  }),
@@ -169,6 +173,21 @@ const InvoiceTable = React.memo((props: Props & { onPay: (invoiceId: string) =>
169
173
  },
170
174
  },
171
175
  },
176
+ {
177
+ label: t('common.type'),
178
+ name: 'billing_reason',
179
+ options: {
180
+ customBodyRenderLite: (_: string, index: number) => {
181
+ const invoice = data.list[index] as TInvoiceExpanded;
182
+ const link = getInvoiceLink(invoice, action);
183
+ return (
184
+ <a href={link.url} target={link.external ? '_blank' : target} rel="noreferrer">
185
+ <Status label={getInvoiceDescriptionAndReason(invoice, locale)?.type} />
186
+ </a>
187
+ );
188
+ },
189
+ },
190
+ },
172
191
  {
173
192
  label: t('payment.customer.invoice.invoiceNumber'),
174
193
  name: 'number',
@@ -209,7 +228,7 @@ const InvoiceTable = React.memo((props: Props & { onPay: (invoiceId: string) =>
209
228
  const link = getInvoiceLink(invoice, action);
210
229
  return (
211
230
  <a href={link.url} target={link.external ? '_blank' : target} rel="noreferrer">
212
- {invoice.description || invoice.id}
231
+ {getInvoiceDescriptionAndReason(invoice, locale)?.description || invoice.id}
213
232
  </a>
214
233
  );
215
234
  },
@@ -0,0 +1,16 @@
1
+ import { useEffect } from 'react';
2
+
3
+ export function usePreventWheel() {
4
+ useEffect(() => {
5
+ const handleWheel = (e: any) => {
6
+ // @ts-ignore
7
+ if (document.activeElement?.type === 'number') {
8
+ e.preventDefault();
9
+ }
10
+ };
11
+ window.addEventListener('wheel', handleWheel, { passive: false });
12
+ return () => {
13
+ window.removeEventListener('wheel', handleWheel);
14
+ };
15
+ }, []);
16
+ }
package/src/index.ts CHANGED
@@ -38,6 +38,7 @@ export * from './contexts/payment';
38
38
  export * from './hooks/subscription';
39
39
  export * from './hooks/mobile';
40
40
  export * from './hooks/table';
41
+ export * from './hooks/scroll';
41
42
 
42
43
  export { translations, createTranslator } from './locales';
43
44
 
package/src/libs/util.ts CHANGED
@@ -4,6 +4,7 @@ import type {
4
4
  PaymentDetails,
5
5
  PriceCurrency,
6
6
  PriceRecurring,
7
+ TInvoiceExpanded,
7
8
  TLineItemExpanded,
8
9
  TPaymentCurrency,
9
10
  TPaymentCurrencyExpanded,
@@ -890,6 +891,9 @@ export const flattenPaymentMethods = (methods: TPaymentMethodExpanded[] = []) =>
890
891
  };
891
892
 
892
893
  export const getTxLink = (method: TPaymentMethod, details: PaymentDetails) => {
894
+ if (!details) {
895
+ return { text: 'N/A', link: '', gas: '' };
896
+ }
893
897
  if (method.type === 'arcblock' && details.arcblock?.tx_hash) {
894
898
  return {
895
899
  link: joinURL(method.settings.arcblock?.explorer_host as string, '/txs', details.arcblock?.tx_hash as string),
@@ -1092,3 +1096,55 @@ export function getCustomerAvatar(
1092
1096
  const updated = typeof updated_at === 'number' ? updated_at : dayjs(updated_at).unix();
1093
1097
  return `/.well-known/service/user/avatar/${did}?imageFilter=resize&w=${imageSize}&h=${imageSize}&updateAt=${updated || dayjs().unix()}`;
1094
1098
  }
1099
+
1100
+ // 判断是否存在txHash
1101
+ export function hasDelegateTxHash(details: PaymentDetails, paymentMethod: TPaymentMethod) {
1102
+ return (
1103
+ paymentMethod?.type &&
1104
+ ['arcblock', 'ethereum'].includes(paymentMethod?.type) &&
1105
+ // @ts-ignore
1106
+ details?.[paymentMethod?.type]?.tx_hash
1107
+ );
1108
+ }
1109
+
1110
+ export function getInvoiceDescriptionAndReason(invoice: TInvoiceExpanded, locale = 'en') {
1111
+ const { billing_reason: reason, description } = invoice;
1112
+ const reasonMap = {
1113
+ subscription_create: t('payment.invoice.reason.creation', locale),
1114
+ subscription_cycle: t('payment.invoice.reason.cycle', locale),
1115
+ subscription_update: t('payment.invoice.reason.update', locale),
1116
+ subscription_recover: t('payment.invoice.reason.recover', locale),
1117
+ subscription_threshold: t('payment.invoice.reason.threshold', locale),
1118
+ subscription_cancel: t('payment.invoice.reason.cancel', locale),
1119
+ manual: t('payment.invoice.reason.manual', locale),
1120
+ upcoming: t('payment.invoice.reason.upcoming', locale),
1121
+ slash_stake: t('payment.invoice.reason.slashStake', locale),
1122
+ stake: t('payment.invoice.reason.stake', locale),
1123
+ return_stake: t('payment.invoice.reason.returnStake', locale),
1124
+ recharge: t('payment.invoice.reason.recharge', locale),
1125
+ };
1126
+ let invoiceType = t('payment.invoice.reason.payment', locale);
1127
+ if (reason.includes('stake') || reason.includes('recharge')) {
1128
+ invoiceType = reasonMap[reason as keyof typeof reasonMap];
1129
+ }
1130
+ if (description?.startsWith('Subscription ') || description?.startsWith('Slash stake')) {
1131
+ return {
1132
+ description: reasonMap[reason as keyof typeof reasonMap],
1133
+ reason: reasonMap[reason as keyof typeof reasonMap],
1134
+ type: invoiceType,
1135
+ };
1136
+ }
1137
+ const descMap = {
1138
+ 'Stake for subscription plan change': t('payment.invoice.reason.stakeForChangePlan', locale),
1139
+ 'Stake for subscription payment change': t('payment.invoice.reason.stakeForChangePayment', locale),
1140
+ 'Stake for subscription': t('payment.invoice.reason.staking', locale),
1141
+ 'Return Subscription staking': t('payment.invoice.reason.returnStake', locale),
1142
+ 'Recharge for subscription': t('payment.invoice.reason.rechargeForSubscription', locale),
1143
+ };
1144
+
1145
+ return {
1146
+ description: descMap[description as keyof typeof descMap] || description,
1147
+ reason: reasonMap[reason as keyof typeof reasonMap] || reason,
1148
+ type: invoiceType,
1149
+ };
1150
+ }
@@ -91,6 +91,8 @@ export default flat({
91
91
  quantityNotEnough: 'Exceed inventory',
92
92
  amountPrecisionLimit: 'Amount decimal places must be less than or equal to {precision}',
93
93
  saveAsDefaultPriceSuccess: 'Set default price successfully',
94
+ stakeAmount: 'Stake Amount',
95
+ slashStakeAmount: 'Slash Stake Amount',
94
96
  },
95
97
  payment: {
96
98
  checkout: {
@@ -319,6 +321,13 @@ export default flat({
319
321
  manual: 'Manual invoice',
320
322
  upcoming: 'Upcoming invoice',
321
323
  slashStake: 'Slash stake',
324
+ stake: 'Stake',
325
+ payment: 'Payment',
326
+ returnStake: 'Return stake',
327
+ stakeForChangePlan: 'Subscription plan update',
328
+ stakeForChangePayment: 'Subscription payment method update',
329
+ recharge: 'Recharge',
330
+ rechargeForSubscription: 'Subscription recharge',
322
331
  },
323
332
  },
324
333
  },
@@ -91,6 +91,8 @@ export default flat({
91
91
  quantityNotEnough: '库存不足',
92
92
  amountPrecisionLimit: '金额小数位数必须在 {precision} 位以内',
93
93
  saveAsDefaultPriceSuccess: '设置默认价格成功',
94
+ stakeAmount: '质押金额',
95
+ slashStakeAmount: '罚没金额',
94
96
  },
95
97
  payment: {
96
98
  checkout: {
@@ -301,7 +303,7 @@ export default flat({
301
303
  reason: {
302
304
  creation: '订阅创建',
303
305
  cycle: '自动扣费',
304
- staking: '订阅创建(质押)',
306
+ staking: '订阅创建',
305
307
  update: '订阅更新',
306
308
  recover: '订阅恢复',
307
309
  threshold: '用量账单',
@@ -309,6 +311,13 @@ export default flat({
309
311
  manual: '人工账单',
310
312
  upcoming: '未来账单',
311
313
  slashStake: '罚没质押',
314
+ stake: '押金',
315
+ payment: '付款',
316
+ returnStake: '退押金',
317
+ stakeForChangePlan: '订阅套餐更新',
318
+ stakeForChangePayment: '订阅支付方式更新',
319
+ recharge: '充值',
320
+ rechargeForSubscription: '订阅充值',
312
321
  },
313
322
  },
314
323
  },