@arcblock/did-connect-react 3.3.3 → 3.3.5

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.
@@ -1,4 +1,4 @@
1
- const o = "3.3.3", s = {
1
+ const o = "3.3.5", s = {
2
2
  version: o
3
3
  };
4
4
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcblock/did-connect-react",
3
- "version": "3.3.3",
3
+ "version": "3.3.5",
4
4
  "description": "Client side library to work with DID Connect by ArcBlock.",
5
5
  "keywords": [
6
6
  "react",
@@ -32,10 +32,10 @@
32
32
  "url": "https://github.com/ArcBlock/ux/issues"
33
33
  },
34
34
  "dependencies": {
35
- "@arcblock/bridge": "3.3.3",
35
+ "@arcblock/bridge": "3.3.5",
36
36
  "@arcblock/did": "^1.27.16",
37
- "@arcblock/icons": "3.3.3",
38
- "@arcblock/react-hooks": "3.3.3",
37
+ "@arcblock/icons": "3.3.5",
38
+ "@arcblock/react-hooks": "3.3.5",
39
39
  "@arcblock/ws": "^1.27.16",
40
40
  "@blocklet/constant": "^1.17.6",
41
41
  "@fontsource/lexend": "^5.2.9",
@@ -81,5 +81,5 @@
81
81
  "eslint-plugin-react-hooks": "^4.6.2",
82
82
  "jest": "^29.7.0"
83
83
  },
84
- "gitHead": "915561d073369e7a4e78a45349ae286a05b8c253"
84
+ "gitHead": "757fbba540967c8552f9c30fc64fdce00a8be222"
85
85
  }
@@ -4,7 +4,7 @@ import translations from '../assets/locale';
4
4
  import { useStateContext } from '../contexts/state';
5
5
  import ActionButton from './action-button';
6
6
 
7
- export default function BackButton({ onClick }) {
7
+ export default function BackButton({ onClick, ...rest }) {
8
8
  const { locale } = useStateContext();
9
9
  const translation = translations[locale] || translations.en;
10
10
  return (
@@ -12,7 +12,8 @@ export default function BackButton({ onClick }) {
12
12
  onClick={onClick}
13
13
  sx={{
14
14
  color: 'text.secondary',
15
- }}>
15
+ }}
16
+ {...rest}>
16
17
  {translation.back}
17
18
  </ActionButton>
18
19
  );
@@ -1,11 +1,11 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import { Box } from '@mui/material';
3
3
  import { LOGIN_PROVIDER, LOGIN_PROVIDER_NAME, LOGIN_PROVIDER_ICON_SIZE } from '@arcblock/ux/lib/Util/constant';
4
- import { checkSameProtocol, getWebWalletUrl } from '@arcblock/ux/lib/Util/wallet';
4
+ import { getWebWalletUrl } from '@arcblock/ux/lib/Util/wallet';
5
5
  import noop from 'lodash/noop';
6
6
  import { useMemoizedFn } from 'ahooks';
7
7
  import Cookie from 'js-cookie';
8
- import { detectWalletExtension, getCookieOptions } from '@arcblock/ux/lib/Util';
8
+ import { getCookieOptions } from '@arcblock/ux/lib/Util';
9
9
  import { useEffect, useRef } from 'react';
10
10
  import { mergeSx } from '@arcblock/ux/lib/Util/style';
11
11
  import ProviderIcon from '@arcblock/ux/lib/DIDConnect/provider-icon';
@@ -17,6 +17,7 @@ import { useBrowser } from '@arcblock/react-hooks';
17
17
  import MobileLoginItem from './mobile-login-item';
18
18
  import WebLoginItem from './web-login-item';
19
19
  import LoginMethodItem from './login-method-item';
20
+ import WalletLoginOptions from './wallet-login-options';
20
21
  import { useOAuth } from '../../../OAuth';
21
22
  import { useStateContext } from '../../contexts/state';
22
23
  import { getApiErrorMessage, getAppId, logger } from '../../../utils';
@@ -41,18 +42,27 @@ export default function ConnectProviderList({
41
42
  magicToken = undefined,
42
43
  baseUrl = '/',
43
44
  customItems = [],
45
+ qrcode = null,
44
46
  }) {
45
47
  const walletLoginRef = useRef(null);
46
48
  const webLoginRef = useRef(null);
47
49
  const passkeyLoginRef = useRef(null);
48
50
 
49
51
  const browser = useBrowser();
50
- const isSameProtocol = checkSameProtocol(webWalletUrl);
51
- const extension = detectWalletExtension();
52
52
 
53
53
  const { loginOAuth, logoutOAuth, t: oauthT, oauthState } = useOAuth();
54
54
  const { passkeyState } = usePasskey();
55
- const { extraParams, locale, connectState, plugins, setPlugins, setSelectedPlugin, getPlugin } = useStateContext();
55
+ const {
56
+ extraParams,
57
+ locale,
58
+ connectState,
59
+ plugins,
60
+ setPlugins,
61
+ setSelectedPlugin,
62
+ getPlugin,
63
+ showWalletOptions,
64
+ setShowWalletOptions,
65
+ } = useStateContext();
56
66
 
57
67
  const t = useMemoizedFn((key, data = {}) => {
58
68
  return translate(translations, key, locale, 'en', data);
@@ -113,6 +123,36 @@ export default function ConnectProviderList({
113
123
  connectState.chooseMethod = 'wallet';
114
124
  });
115
125
 
126
+ const handleMobileLoginClick = useMemoizedFn(async () => {
127
+ localStorage.setItem(GA_LAST_LOGIN_METHOD, 'wallet');
128
+ tokenState.reset();
129
+ await onReset();
130
+ tokenState.status = 'created';
131
+ connectState.chooseMethod = 'wallet';
132
+ const connectFn = walletLoginRef.current?.connect;
133
+ connectState.retryConnect = () => {
134
+ connectFn(defaultRetryConnect);
135
+ };
136
+ });
137
+
138
+ const handleWebLoginClick = useMemoizedFn(() => {
139
+ localStorage.setItem(GA_LAST_LOGIN_METHOD, 'wallet');
140
+ // HACK: 在点击插件登录时,直接将状态置为扫码中,避免插件登录时的状态切换
141
+ tokenState.status = 'scanned';
142
+ connectState.chooseMethod = 'wallet-web';
143
+ const connectFn = webLoginRef.current.connect;
144
+ connectState.retryConnect = async () => {
145
+ await onReset();
146
+ tokenState.error = '';
147
+ tokenState.status = 'scanned';
148
+ connectFn();
149
+ };
150
+ });
151
+
152
+ const handleWalletLoginBack = useMemoizedFn(() => {
153
+ setShowWalletOptions(false);
154
+ });
155
+
116
156
  const showEmailLogin = providerList.some((p) => p.provider === LOGIN_PROVIDER.EMAIL);
117
157
 
118
158
  const emailPlugin = useEmailPlugin({ baseUrl });
@@ -159,7 +199,7 @@ export default function ConnectProviderList({
159
199
  const renderProviderList = providerList
160
200
  .map((item) => {
161
201
  if (item.provider === LOGIN_PROVIDER.WALLET) {
162
- if (browser.mobile.any) {
202
+ if (browser.mobile.any && !showWalletOptions) {
163
203
  return (
164
204
  <MobileLoginItem
165
205
  key={LOGIN_PROVIDER.WALLET}
@@ -169,46 +209,24 @@ export default function ConnectProviderList({
169
209
  locale={locale}
170
210
  tokenKey={tokenKey}
171
211
  disableSwitchApp={disableSwitchApp}
172
- onClick={async () => {
173
- localStorage.setItem(GA_LAST_LOGIN_METHOD, 'wallet');
174
- tokenState.reset();
175
- await onReset();
176
- tokenState.status = 'created';
177
- connectState.chooseMethod = 'wallet';
178
- const connectFn = walletLoginRef.current?.connect;
179
- connectState.retryConnect = () => {
180
- connectFn(defaultRetryConnect);
181
- };
182
- }}
183
- />
184
- );
185
- }
186
-
187
- if ((isSameProtocol || extension) && (!browser.mobile.any || extension)) {
188
- return (
189
- <WebLoginItem
190
- key={LOGIN_PROVIDER.WALLET}
191
- ref={webLoginRef}
192
- tokenState={tokenState}
193
- webWalletUrl={webWalletUrl}
194
- sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
195
- disableSwitchApp={disableSwitchApp}
196
- onClick={() => {
197
- localStorage.setItem(GA_LAST_LOGIN_METHOD, 'wallet');
198
- // HACK: 在点击插件登录时,直接将状态置为扫码中,避免插件登录时的状态切换
199
- tokenState.status = 'scanned';
200
- connectState.chooseMethod = 'wallet-web';
201
- const connectFn = webLoginRef.current.connect;
202
- connectState.retryConnect = async () => {
203
- await onReset();
204
- tokenState.error = '';
205
- tokenState.status = 'scanned';
206
- connectFn();
207
- };
208
- }}
212
+ onClick={handleMobileLoginClick}
209
213
  />
210
214
  );
211
215
  }
216
+ return (
217
+ <WebLoginItem
218
+ key={LOGIN_PROVIDER.WALLET}
219
+ ref={webLoginRef}
220
+ tokenState={tokenState}
221
+ webWalletUrl={webWalletUrl}
222
+ sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
223
+ disableSwitchApp={disableSwitchApp}
224
+ autoConnect={false}
225
+ onClick={() => {
226
+ setShowWalletOptions(true);
227
+ }}
228
+ />
229
+ );
212
230
  }
213
231
 
214
232
  if (item.provider === LOGIN_PROVIDER.PASSKEY) {
@@ -306,70 +324,87 @@ export default function ConnectProviderList({
306
324
  {
307
325
  display: 'flex',
308
326
  flexDirection: 'column',
309
- gap: 1.5,
327
+ ...(showWalletOptions && !browser.mobile.any ? { alignItems: 'center', gap: 0 } : { gap: 1.5 }),
310
328
  },
311
329
  ]}>
312
- <Box
313
- sx={{
314
- display: 'grid',
315
- gridTemplateColumns: 'repeat(12, 1fr)' /* 灵活的12列基础网格 */,
316
- gap: 1.5,
317
- '.arc-login-item:nth-child(-n+3)': {
318
- gridColumn: 'span 12',
319
- },
320
- // 当登录项大于等于 5 时,第三个登录项增加一个 marginBottom,用于分割独占一行的和一行多个的登录项
321
- '.arc-login-item:nth-child(3):nth-last-child(n+3)': {
322
- mb: 1,
323
- },
324
- '.arc-login-item:nth-child(4):last-child': {
325
- gridColumn: 'span 12',
326
- },
327
- '.arc-login-item:nth-child(4):nth-last-child(2), .arc-login-item:nth-child(4):nth-last-child(2), .arc-login-item:nth-child(4):nth-last-child(2) ~ .arc-login-item':
328
- {
329
- gridColumn: 'span 6',
330
- justifyContent: 'center',
331
- '.arc-login-item__body, .other-item-icon': {
332
- display: 'none',
333
- },
330
+ {showWalletOptions && !browser.mobile.any ? (
331
+ <WalletLoginOptions
332
+ qrcode={qrcode}
333
+ tokenState={tokenState}
334
+ webWalletUrl={webWalletUrl}
335
+ size={size}
336
+ disableSwitchApp={disableSwitchApp}
337
+ tokenKey={tokenKey}
338
+ walletLoginRef={walletLoginRef}
339
+ webLoginRef={webLoginRef}
340
+ onMobileLoginClick={handleMobileLoginClick}
341
+ onWebLoginClick={handleWebLoginClick}
342
+ onBack={handleWalletLoginBack}
343
+ />
344
+ ) : (
345
+ <Box
346
+ sx={{
347
+ display: 'grid',
348
+ gridTemplateColumns: 'repeat(12, 1fr)' /* 灵活的12列基础网格 */,
349
+ gap: 1.5,
350
+ '.arc-login-item:nth-child(-n+3)': {
351
+ gridColumn: 'span 12',
334
352
  },
335
- '.arc-login-item:nth-child(4):nth-last-child(3), .arc-login-item:nth-child(4):nth-last-child(3) ~ .arc-login-item':
336
- {
337
- justifyContent: 'center',
338
- gridColumn: 'span 4',
339
- '.arc-login-item__body, .other-item-icon': {
340
- display: 'none',
341
- },
353
+ // 当登录项大于等于 5 时,第三个登录项增加一个 marginBottom,用于分割独占一行的和一行多个的登录项
354
+ '.arc-login-item:nth-child(3):nth-last-child(n+3)': {
355
+ mb: 1,
342
356
  },
343
- '.arc-login-item:nth-child(4):nth-last-child(4), .arc-login-item:nth-child(4):nth-last-child(4) ~ .arc-login-item':
344
- {
357
+ '.arc-login-item:nth-child(4):last-child': {
358
+ gridColumn: 'span 12',
359
+ },
360
+ '.arc-login-item:nth-child(4):nth-last-child(2), .arc-login-item:nth-child(4):nth-last-child(2), .arc-login-item:nth-child(4):nth-last-child(2) ~ .arc-login-item':
361
+ {
362
+ gridColumn: 'span 6',
363
+ justifyContent: 'center',
364
+ '.arc-login-item__body, .other-item-icon': {
365
+ display: 'none',
366
+ },
367
+ },
368
+ '.arc-login-item:nth-child(4):nth-last-child(3), .arc-login-item:nth-child(4):nth-last-child(3) ~ .arc-login-item':
369
+ {
370
+ justifyContent: 'center',
371
+ gridColumn: 'span 4',
372
+ '.arc-login-item__body, .other-item-icon': {
373
+ display: 'none',
374
+ },
375
+ },
376
+ '.arc-login-item:nth-child(4):nth-last-child(4), .arc-login-item:nth-child(4):nth-last-child(4) ~ .arc-login-item':
377
+ {
378
+ gridColumn: 'span 3',
379
+ justifyContent: 'center',
380
+ '.arc-login-item__body, .other-item-icon': {
381
+ display: 'none',
382
+ },
383
+ },
384
+ '&:has(.arc-login-item:nth-child(8)) .arc-login-item:nth-child(n+4)': {
345
385
  gridColumn: 'span 3',
346
386
  justifyContent: 'center',
347
387
  '.arc-login-item__body, .other-item-icon': {
348
388
  display: 'none',
349
389
  },
350
390
  },
351
- '&:has(.arc-login-item:nth-child(8)) .arc-login-item:nth-child(n+4)': {
352
- gridColumn: 'span 3',
353
- justifyContent: 'center',
354
- '.arc-login-item__body, .other-item-icon': {
355
- display: 'none',
356
- },
357
- },
358
- }}>
359
- {renderProviderList.length > 0 ? (
360
- renderProviderList
361
- ) : (
362
- <Empty
363
- className="arc-login-item"
364
- sx={{
365
- '.empty-content': {
366
- textAlign: 'center',
367
- },
368
- }}>
369
- {t('noAuthenticationProvider')}
370
- </Empty>
371
- )}
372
- </Box>
391
+ }}>
392
+ {renderProviderList.length > 0 ? (
393
+ renderProviderList
394
+ ) : (
395
+ <Empty
396
+ className="arc-login-item"
397
+ sx={{
398
+ '.empty-content': {
399
+ textAlign: 'center',
400
+ },
401
+ }}>
402
+ {t('noAuthenticationProvider')}
403
+ </Empty>
404
+ )}
405
+ </Box>
406
+ )}
407
+
373
408
  {customItems.map((item) => (!item ? null : item))}
374
409
  </Box>
375
410
  </Box>
@@ -393,4 +428,5 @@ ConnectProviderList.propTypes = {
393
428
  magicToken: PropTypes.string,
394
429
  baseUrl: PropTypes.string,
395
430
  customItems: PropTypes.arrayOf(PropTypes.node),
431
+ qrcode: PropTypes.node,
396
432
  };
@@ -15,6 +15,7 @@ import { logger } from '../../../utils';
15
15
  import useAuthUrl from '../../hooks/auth-url';
16
16
 
17
17
  export default function MobileLoginItem({
18
+ isTablet = false,
18
19
  ref = null,
19
20
  tokenState,
20
21
  locale,
@@ -120,7 +121,7 @@ export default function MobileLoginItem({
120
121
 
121
122
  if (isWalletWebview) {
122
123
  handleWalletConnect();
123
- } else if (browser.mobile.any) {
124
+ } else if (browser.mobile.any || isTablet) {
124
125
  handleOpenDeeplink();
125
126
  } else {
126
127
  fallback();
@@ -176,4 +177,5 @@ MobileLoginItem.propTypes = {
176
177
  onClick: PropTypes.func,
177
178
  disableSwitchApp: PropTypes.bool,
178
179
  ref: PropTypes.any,
180
+ isTablet: PropTypes.bool,
179
181
  };
@@ -0,0 +1,116 @@
1
+ import PropTypes from 'prop-types';
2
+ import { Box, Divider } from '@mui/material';
3
+ import { LOGIN_PROVIDER } from '@arcblock/ux/lib/Util/constant';
4
+ import { checkSameProtocol, getWebWalletUrl } from '@arcblock/ux/lib/Util/wallet';
5
+ import { detectWalletExtension } from '@arcblock/ux/lib/Util';
6
+ import { useCreation } from 'ahooks';
7
+ import { useBrowser } from '@arcblock/react-hooks';
8
+
9
+ import MobileLoginItem from './mobile-login-item';
10
+ import WebLoginItem from './web-login-item';
11
+ import BackButton from '../back-button';
12
+ import { useStateContext } from '../../contexts/state';
13
+
14
+ export default function WalletLoginOptions({
15
+ qrcode,
16
+ tokenState,
17
+ webWalletUrl = getWebWalletUrl(),
18
+ size = 'small',
19
+ disableSwitchApp = false,
20
+ tokenKey,
21
+ walletLoginRef = null,
22
+ webLoginRef = null,
23
+ onMobileLoginClick,
24
+ onWebLoginClick,
25
+ onBack,
26
+ }) {
27
+ const { locale } = useStateContext();
28
+ const browser = useBrowser();
29
+ const isSameProtocol = checkSameProtocol(webWalletUrl);
30
+ const extension = detectWalletExtension();
31
+
32
+ const isTablet = useCreation(() => {
33
+ return browser.mobile.tablet || (!browser.mobile.any && window?.navigator?.maxTouchPoints > 0);
34
+ }, [browser.mobile.tablet, browser.mobile.any]);
35
+
36
+ return (
37
+ <>
38
+ {qrcode}
39
+
40
+ <Box sx={{ width: '100%', display: 'flex', flexDirection: 'column', gap: 1.5 }}>
41
+ <Box sx={{ width: '100%' }}>
42
+ <Divider
43
+ orientation="horizontal"
44
+ sx={{
45
+ fontSize: 12,
46
+ color: 'text.hint',
47
+ '&::before, &::after': {
48
+ borderColor: 'divider',
49
+ },
50
+ }}>
51
+ or
52
+ </Divider>
53
+ </Box>
54
+ <Box
55
+ sx={{
56
+ width: '100%',
57
+ '.arc-login-item': {
58
+ width: '100%',
59
+ justifyContent: 'center',
60
+ '& > .arc-login-item__body': {
61
+ flex: 'unset',
62
+ },
63
+ '& > .other-item-icon': {
64
+ display: 'none !important',
65
+ },
66
+ },
67
+ }}>
68
+ {isTablet ? (
69
+ <MobileLoginItem
70
+ key={LOGIN_PROVIDER.WALLET}
71
+ ref={walletLoginRef}
72
+ tokenState={tokenState}
73
+ isTablet={isTablet}
74
+ sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
75
+ locale={locale}
76
+ tokenKey={tokenKey}
77
+ disableSwitchApp={disableSwitchApp}
78
+ onClick={onMobileLoginClick}
79
+ />
80
+ ) : (
81
+ // eslint-disable-next-line react/jsx-no-useless-fragment
82
+ <>
83
+ {(isSameProtocol || extension) && (!browser.mobile.any || extension) ? (
84
+ <WebLoginItem
85
+ key={LOGIN_PROVIDER.WALLET}
86
+ ref={webLoginRef}
87
+ tokenState={tokenState}
88
+ webWalletUrl={webWalletUrl}
89
+ sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
90
+ disableSwitchApp={disableSwitchApp}
91
+ onClick={onWebLoginClick}
92
+ />
93
+ ) : null}
94
+ </>
95
+ )}
96
+ </Box>
97
+
98
+ <BackButton variant="text" onClick={onBack} />
99
+ </Box>
100
+ </>
101
+ );
102
+ }
103
+
104
+ WalletLoginOptions.propTypes = {
105
+ qrcode: PropTypes.node.isRequired,
106
+ tokenState: PropTypes.object.isRequired,
107
+ webWalletUrl: PropTypes.string,
108
+ size: PropTypes.oneOf(['small', 'normal', 'large']),
109
+ disableSwitchApp: PropTypes.bool,
110
+ tokenKey: PropTypes.string.isRequired,
111
+ walletLoginRef: PropTypes.object,
112
+ webLoginRef: PropTypes.object,
113
+ onMobileLoginClick: PropTypes.func.isRequired,
114
+ onWebLoginClick: PropTypes.func.isRequired,
115
+ onBack: PropTypes.func.isRequired,
116
+ };
@@ -21,6 +21,7 @@ export default function WebLoginItem({
21
21
  webWalletUrl,
22
22
  onClick = noop,
23
23
  disableSwitchApp = false,
24
+ autoConnect = true,
24
25
  ...rest
25
26
  }) {
26
27
  const { browserBrand } = useStateContext();
@@ -32,6 +33,9 @@ export default function WebLoginItem({
32
33
  const urlWithParams = useAuthUrl({ disableSwitchApp, tokenState });
33
34
 
34
35
  const handleConnectFn = useMemoizedFn(() => {
36
+ if (!autoConnect) {
37
+ return;
38
+ }
35
39
  currentState.loading = true;
36
40
  const openResult = openWebWallet({
37
41
  webWalletUrl,
@@ -75,9 +79,9 @@ export default function WebLoginItem({
75
79
 
76
80
  const buttonProps = useCreation(() => {
77
81
  const result = {};
78
- if (extension) {
82
+ if (extension || !autoConnect) {
79
83
  result.onClick = handleConnect;
80
- } else if (extDownloadUrl) {
84
+ } else if (extDownloadUrl && autoConnect) {
81
85
  result.component = 'a';
82
86
  result.href = extDownloadUrl;
83
87
  result.target = '_blank';
@@ -95,6 +99,9 @@ export default function WebLoginItem({
95
99
  }, [handleConnect, tokenState.url]);
96
100
 
97
101
  const title = useCreation(() => {
102
+ if (!autoConnect) {
103
+ return 'DID Wallet';
104
+ }
98
105
  if (extension || extDownloadUrl) {
99
106
  return 'DID Wallet (Extension)';
100
107
  }
@@ -145,5 +152,6 @@ WebLoginItem.propTypes = {
145
152
  webWalletUrl: PropTypes.string.isRequired,
146
153
  onClick: PropTypes.func,
147
154
  disableSwitchApp: PropTypes.bool,
155
+ autoConnect: PropTypes.bool, // 点击后是否自动连接,默认true
148
156
  ref: PropTypes.any,
149
157
  };
@@ -35,6 +35,7 @@ function StateProvider({
35
35
  }) {
36
36
  const forceUpdate = useUpdate();
37
37
  const [plugins, setPlugins] = useState([]);
38
+ const [showWalletOptions, setShowWalletOptions] = useState(false);
38
39
  const [selectedPlugin, setSelectedPlugin] = useState('');
39
40
  const pluginsMap = useCreation(() => {
40
41
  return new Map(plugins.map((plugin) => [plugin.name, plugin]));
@@ -175,6 +176,10 @@ function StateProvider({
175
176
  selectedPlugin,
176
177
  setSelectedPlugin,
177
178
  forceUpdate,
179
+
180
+ // 控制钱包登录的显示
181
+ showWalletOptions,
182
+ setShowWalletOptions,
178
183
  };
179
184
  }, [
180
185
  browser.wallet,