payment-kit 1.17.6 → 1.17.8

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/blocklet.yml CHANGED
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.17.6
17
+ version: 1.17.8
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.17.6",
3
+ "version": "1.17.8",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -53,7 +53,7 @@
53
53
  "@arcblock/validator": "^1.19.3",
54
54
  "@blocklet/js-sdk": "^1.16.37",
55
55
  "@blocklet/logger": "^1.16.37",
56
- "@blocklet/payment-react": "1.17.6",
56
+ "@blocklet/payment-react": "1.17.8",
57
57
  "@blocklet/sdk": "^1.16.37",
58
58
  "@blocklet/ui-react": "^2.11.27",
59
59
  "@blocklet/uploader": "^0.1.64",
@@ -115,12 +115,13 @@
115
115
  "ufo": "^1.5.4",
116
116
  "umzug": "^3.8.1",
117
117
  "use-bus": "^2.5.2",
118
- "validator": "^13.12.0"
118
+ "validator": "^13.12.0",
119
+ "web3": "^4.16.0"
119
120
  },
120
121
  "devDependencies": {
121
122
  "@abtnode/types": "^1.16.37",
122
123
  "@arcblock/eslint-config-ts": "^0.3.3",
123
- "@blocklet/payment-types": "1.17.6",
124
+ "@blocklet/payment-types": "1.17.8",
124
125
  "@types/cookie-parser": "^1.4.7",
125
126
  "@types/cors": "^2.8.17",
126
127
  "@types/debug": "^4.1.12",
@@ -166,5 +167,5 @@
166
167
  "parser": "typescript"
167
168
  }
168
169
  },
169
- "gitHead": "50e93ab4340be53c9d9bd18718c6db26048578c5"
170
+ "gitHead": "3a896417b3beb364f8c986ea0b5b968810da9ef7"
170
171
  }
@@ -1,28 +1,49 @@
1
1
  import { ExpandLessOutlined, ExpandMoreOutlined } from '@mui/icons-material';
2
2
  import { Box, Collapse, Stack } from '@mui/material';
3
- import { useState } from 'react';
3
+ import { useEffect, useState } from 'react';
4
4
 
5
5
  type Props = {
6
- trigger: string | Function | React.ReactNode;
7
- children: React.ReactNode;
6
+ trigger: string | ((expanded: boolean) => React.ReactNode) | React.ReactNode;
7
+ children?: React.ReactNode;
8
8
  expanded?: boolean;
9
9
  addons?: React.ReactNode;
10
10
  style?: Record<string, any>;
11
+ value?: string;
12
+ onChange?: (value: string, expanded: boolean) => void;
13
+ lazy?: boolean;
14
+ };
15
+
16
+ IconCollapse.defaultProps = {
17
+ value: '',
18
+ onChange: () => {},
19
+ children: null,
20
+ expanded: false,
21
+ addons: null,
22
+ style: {},
23
+ lazy: true,
11
24
  };
12
25
 
13
26
  export default function IconCollapse(props: Props) {
14
27
  const [expanded, setExpanded] = useState(props.expanded || false);
15
28
  const toggleExpanded = () => {
16
- setExpanded((prev) => !prev);
29
+ const newExpanded = !expanded;
30
+ setExpanded(newExpanded);
17
31
  };
18
32
 
33
+ useEffect(() => {
34
+ setExpanded(props.expanded || false);
35
+ }, [props.expanded]);
36
+
19
37
  return (
20
38
  <>
21
39
  <Stack
22
40
  direction="row"
23
41
  alignItems="center"
24
42
  justifyContent="space-between"
25
- onClick={toggleExpanded}
43
+ onClick={() => {
44
+ props.onChange?.(props.value || '', !expanded);
45
+ toggleExpanded();
46
+ }}
26
47
  sx={{
27
48
  width: 1,
28
49
  cursor: 'pointer',
@@ -31,20 +52,12 @@ export default function IconCollapse(props: Props) {
31
52
  '& :hover': { color: 'primary.main' },
32
53
  ...props.style,
33
54
  }}>
34
- <Box>{typeof props.trigger === 'function' ? props.trigger(expanded) : props.trigger} </Box>
55
+ <Box>{typeof props.trigger === 'function' ? props.trigger(expanded) : props.trigger}</Box>
35
56
  <Stack direction="row" alignItems="center" spacing={2}>
36
57
  {props.addons} {expanded ? <ExpandLessOutlined /> : <ExpandMoreOutlined />}
37
58
  </Stack>
38
59
  </Stack>
39
- <Collapse in={expanded}>
40
- <Box>{props.children}</Box>
41
- </Collapse>
60
+ <Collapse in={expanded}>{expanded || props.lazy ? props.children : null}</Collapse>
42
61
  </>
43
62
  );
44
63
  }
45
-
46
- IconCollapse.defaultProps = {
47
- expanded: false,
48
- addons: null,
49
- style: {},
50
- };
@@ -62,12 +62,7 @@ export default function EditCustomer({
62
62
  hideLiveMode
63
63
  onClose={onCancel}
64
64
  footer={
65
- <Button
66
- variant="contained"
67
- color="primary"
68
- disabled={loading}
69
- onClick={onSubmit}
70
- sx={{ width: '100%', mt: 2.5 }}>
65
+ <Button variant="contained" color="primary" disabled={loading} onClick={onSubmit} sx={{ width: '100%' }}>
71
66
  {loading && <CircularProgress size="small" />} {t('common.save')}
72
67
  </Button>
73
68
  }>
@@ -9,7 +9,7 @@ import useBus from 'use-bus';
9
9
  type Props = {
10
10
  icon: React.ReactNode;
11
11
  text: string;
12
- addons: React.ReactNode;
12
+ addons?: React.ReactNode;
13
13
  children: React.ReactNode;
14
14
  width?: number;
15
15
  open?: boolean;
@@ -26,6 +26,7 @@ DrawerForm.defaultProps = {
26
26
  onClose: noop,
27
27
  hideLiveMode: false,
28
28
  footer: null,
29
+ addons: null,
29
30
  };
30
31
 
31
32
  export default function DrawerForm(props: Props) {
@@ -60,26 +61,52 @@ export default function DrawerForm(props: Props) {
60
61
  alignItems="center"
61
62
  justifyContent="space-between"
62
63
  className="drawer-form-header-wrapper"
63
- sx={{ pl: 3, pr: 3, pb: 2, pt: 2, borderBottom: '1px solid var(--stroke-sep, #EFF1F5)' }}>
64
- <Stack direction="row" alignItems="center">
65
- <Typography className="drawer-form-header" variant="h6" sx={{ fontWeight: 600 }}>
66
- {props.text}
67
- </Typography>
68
- {!settings.livemode && !props.hideLiveMode && <Livemode />}
69
- </Stack>
70
- <Stack direction="row" alignItems="center" justifyContent="space-between">
71
- <Close
72
- sx={{ mr: props.addons ? 1 : 0, color: 'text.secondary', cursor: 'pointer' }}
73
- onClick={handleClose}
74
- />
75
- {props.addons && <Divider orientation="vertical" flexItem sx={{ mr: 2 }} />}
64
+ flexWrap="wrap"
65
+ sx={{
66
+ pl: 3,
67
+ pr: 3,
68
+ pb: 2,
69
+ pt: 2,
70
+ gap: 1,
71
+ borderBottom: '1px solid var(--stroke-sep, #EFF1F5)',
72
+ '@media (max-width: 600px)': {
73
+ '& > .addons-wrapper': {
74
+ width: '100%',
75
+ justifyContent: 'flex-end',
76
+ '& > .addons-divider': {
77
+ display: 'none',
78
+ },
79
+ },
80
+ },
81
+ }}>
82
+ <Box flex={1} display="flex" alignItems="center" justifyContent="space-between">
83
+ <Stack direction="row" alignItems="center">
84
+ <Typography className="drawer-form-header" variant="h6" sx={{ fontWeight: 600 }}>
85
+ {props.text}
86
+ </Typography>
87
+ {!settings.livemode && !props.hideLiveMode && <Livemode />}
88
+ </Stack>
89
+ <Stack direction="row" alignItems="center" justifyContent="space-between">
90
+ <Close
91
+ sx={{ mr: props.addons ? 1 : 0, color: 'text.secondary', cursor: 'pointer' }}
92
+ onClick={handleClose}
93
+ />
94
+ </Stack>
95
+ </Box>
96
+ <Box display="flex" alignItems="center" className="addons-wrapper">
97
+ {props.addons && <Divider orientation="vertical" flexItem sx={{ mr: 2 }} className="addons-divider" />}
76
98
  {props.addons}
77
- </Stack>
99
+ </Box>
78
100
  </Stack>
79
101
  <Box className="drawer-form-body" sx={{ mx: 3, my: 2 }}>
80
102
  {props.children}
81
103
  </Box>
82
- <Box className="drawer-form-footer">{props.footer}</Box>
104
+ {props.footer && (
105
+ <Box className="drawer-form-footer">
106
+ <Divider sx={{ mb: 2 }} />
107
+ {props.footer}
108
+ </Box>
109
+ )}
83
110
  </Container>
84
111
  </>
85
112
  );
@@ -33,6 +33,7 @@ export default function InfoRow(props: Props) {
33
33
  alignItems={props.alignItems}
34
34
  justifyContent="space-between"
35
35
  flexWrap="wrap"
36
+ gap={1}
36
37
  sx={{
37
38
  mb: 2,
38
39
  ...props.sx,
@@ -1,20 +1,57 @@
1
- /* eslint-disable no-nested-ternary */
2
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
2
  import Toast from '@arcblock/ux/lib/Toast';
4
3
  import { api, formatError } from '@blocklet/payment-react';
5
- import type { TPaymentCurrency, TPaymentMethod } from '@blocklet/payment-types';
4
+ import type { TPaymentCurrency, TPaymentMethodExpanded } from '@blocklet/payment-types';
6
5
  import { AddOutlined } from '@mui/icons-material';
7
- import { Button, CircularProgress } from '@mui/material';
6
+ import {
7
+ Button,
8
+ CircularProgress,
9
+ TextField,
10
+ Autocomplete,
11
+ Box,
12
+ Typography,
13
+ Avatar,
14
+ Stack,
15
+ Divider,
16
+ } from '@mui/material';
8
17
  import { useSetState } from 'ahooks';
9
18
  import { FormProvider, useForm } from 'react-hook-form';
10
19
  import { dispatch } from 'use-bus';
11
-
20
+ import { useEffect } from 'react';
12
21
  import DrawerForm from '../drawer-form';
13
22
  import PaymentCurrencyForm from './form';
14
23
 
15
- export default function PaymentCurrencyAdd({ method, onClose }: { method: TPaymentMethod; onClose: () => void }) {
24
+ const loadTokenList = () =>
25
+ import(
26
+ /* webpackChunkName: "payment-token-list" */
27
+ './tokenList.json'
28
+ );
29
+ interface TokenInfo {
30
+ address: string;
31
+ chainId: number;
32
+ decimals: number;
33
+ name: string;
34
+ symbol: string;
35
+ logoURI?: string;
36
+ }
37
+
38
+ export default function PaymentCurrencyAdd({
39
+ method,
40
+ onClose,
41
+ }: {
42
+ method: TPaymentMethodExpanded;
43
+ onClose: () => void;
44
+ }) {
16
45
  const { t } = useLocaleContext();
17
- const [state, setState] = useSetState({ loading: false });
46
+ const [state, setState] = useSetState<{
47
+ loading: boolean;
48
+ tokenListLoading: boolean;
49
+ availableTokens: TokenInfo[];
50
+ }>({
51
+ loading: false,
52
+ tokenListLoading: false,
53
+ availableTokens: [],
54
+ });
18
55
 
19
56
  const methods = useForm<TPaymentCurrency>({
20
57
  defaultValues: {
@@ -25,7 +62,39 @@ export default function PaymentCurrencyAdd({ method, onClose }: { method: TPayme
25
62
  contract: '',
26
63
  },
27
64
  });
28
- const { handleSubmit } = methods;
65
+ const { handleSubmit, setValue } = methods;
66
+
67
+ const showTokenSelect = ['ethereum', 'base'].includes(method.type);
68
+
69
+ // @ts-ignore
70
+ const chainId = method?.settings?.[method.type]?.chain_id || '';
71
+
72
+ useEffect(() => {
73
+ if (showTokenSelect && !state.availableTokens.length) {
74
+ setState({ tokenListLoading: true });
75
+ loadTokenList().then((module) => {
76
+ const availableTokens = showTokenSelect
77
+ ? // @ts-ignore
78
+ ((module.default?.[chainId] as TokenInfo[]) || []).filter(
79
+ (token: TokenInfo) => !(method.payment_currencies || []).find((c: any) => c.contract === token.address)
80
+ )
81
+ : [];
82
+ setState({
83
+ availableTokens,
84
+ tokenListLoading: false,
85
+ });
86
+ });
87
+ }
88
+ }, [showTokenSelect, chainId]);
89
+
90
+ const handleTokenSelect = (token: TokenInfo | null) => {
91
+ if (token) {
92
+ setValue('name', token.name);
93
+ setValue('description', `${token.name} (${token.symbol})`);
94
+ setValue('logo', token.logoURI || '');
95
+ setValue('contract', token.address);
96
+ }
97
+ };
29
98
 
30
99
  const onSubmit = (data: TPaymentCurrency) => {
31
100
  setState({ loading: true });
@@ -52,12 +121,71 @@ export default function PaymentCurrencyAdd({ method, onClose }: { method: TPayme
52
121
  onClose={onClose}
53
122
  text={t('admin.paymentCurrency.add')}
54
123
  width={640}
55
- addons={
56
- <Button variant="contained" size="small" onClick={handleSubmit(onSubmit)} disabled={state.loading}>
57
- {state.loading ? <CircularProgress size="small" /> : t('admin.paymentCurrency.save')}
124
+ footer={
125
+ <Button
126
+ variant="contained"
127
+ size="large"
128
+ onClick={handleSubmit(onSubmit)}
129
+ disabled={state.loading}
130
+ sx={{ width: '100%' }}>
131
+ {state.loading && <CircularProgress size={20} />} {t('common.save')}
58
132
  </Button>
59
133
  }>
60
134
  <FormProvider {...methods}>
135
+ {showTokenSelect && (
136
+ <Box sx={{ mb: 3 }}>
137
+ <Autocomplete
138
+ disablePortal
139
+ options={state.availableTokens}
140
+ loading={state.tokenListLoading}
141
+ getOptionLabel={(option: TokenInfo) => option.symbol}
142
+ onChange={(_, value) => handleTokenSelect(value)}
143
+ renderInput={(params) => (
144
+ <TextField
145
+ {...params}
146
+ label={t('admin.paymentCurrency.quickAdd')}
147
+ placeholder={t('admin.paymentCurrency.searchToken')}
148
+ fullWidth
149
+ />
150
+ )}
151
+ renderOption={(props, option) => (
152
+ <Box
153
+ component="li"
154
+ {...props}
155
+ sx={{
156
+ display: 'flex',
157
+ alignItems: 'center',
158
+ gap: 1,
159
+ cursor: 'pointer',
160
+ '&:hover': {
161
+ backgroundColor: 'action.hover',
162
+ },
163
+ }}>
164
+ <Avatar
165
+ src={option.logoURI || option.symbol}
166
+ alt={option.symbol}
167
+ sx={{
168
+ width: 24,
169
+ height: 24,
170
+ }}
171
+ />
172
+ <Stack>
173
+ <Typography fontWeight={500}>{option.name}</Typography>
174
+ <Typography variant="caption" color="text.secondary">
175
+ {option.symbol}
176
+ </Typography>
177
+ </Stack>
178
+ </Box>
179
+ )}
180
+ isOptionEqualToValue={(option, value) => option.address === value.address}
181
+ />
182
+ <Divider sx={{ my: 3 }}>
183
+ <Typography variant="caption" color="text.secondary">
184
+ {t('admin.paymentCurrency.orManualInput')}
185
+ </Typography>
186
+ </Divider>
187
+ </Box>
188
+ )}
61
189
  <PaymentCurrencyForm />
62
190
  </FormProvider>
63
191
  </DrawerForm>
@@ -60,9 +60,14 @@ export default function PaymentCurrencyEdit({
60
60
  onClose={onClose}
61
61
  text={t('admin.paymentCurrency.edit')}
62
62
  width={640}
63
- addons={
64
- <Button variant="contained" size="small" onClick={handleSubmit(onSubmit)} disabled={state.loading}>
65
- {state.loading ? <CircularProgress size="small" /> : t('admin.paymentCurrency.save')}
63
+ footer={
64
+ <Button
65
+ variant="contained"
66
+ size="large"
67
+ onClick={handleSubmit(onSubmit)}
68
+ disabled={state.loading}
69
+ sx={{ width: '100%' }}>
70
+ {state.loading && <CircularProgress size={20} />} {t('common.save')}
66
71
  </Button>
67
72
  }>
68
73
  <FormProvider {...methods}>