@arcblock/did-connect-react 3.3.9 → 3.4.0

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.
@@ -0,0 +1,616 @@
1
+ import { use, useEffect, useRef, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Box, Divider, Skeleton } from '@mui/material';
4
+ import {
5
+ useCreation,
6
+ useDebounceFn,
7
+ useMemoizedFn,
8
+ useMount,
9
+ usePrevious,
10
+ useSize,
11
+ useUpdate,
12
+ useUpdateEffect,
13
+ } from 'ahooks';
14
+ import noop from 'lodash/noop';
15
+ import isUndefined from 'lodash/isUndefined';
16
+
17
+ import CloseButton from '@arcblock/ux/lib/CloseButton';
18
+ import {
19
+ LOGIN_PROVIDER,
20
+ LOGIN_PROVIDER_NAME,
21
+ OAUTH_PROVIDER,
22
+ DID_CONNECT_MEDIUM_WIDTH,
23
+ DID_CONNECT_SMALL_WIDTH,
24
+ } from '@arcblock/ux/lib/Util/constant';
25
+ import QRCode from '@arcblock/ux/lib/QRCode';
26
+ import { DIDConnectFooter } from '@arcblock/ux/lib/DIDConnect';
27
+ import { useTheme } from '@arcblock/ux/lib/Theme';
28
+ import ProviderIcon from '@arcblock/ux/lib/DIDConnect/provider-icon';
29
+
30
+ import '@fontsource/lexend/400.css';
31
+ import '@fontsource/lexend/600.css';
32
+
33
+ import { SessionContext } from '../Session/context';
34
+ import { StateProvider, useStateContext } from './contexts/state';
35
+ import ConnectProviderList from './components/login-item/connect-provider-list';
36
+ import AutoHeight from './components/auto-height';
37
+ import useToken from './hooks/token';
38
+ import { useOAuth } from '../OAuth';
39
+ import ConnectStatus from './components/connect-status';
40
+ import { usePasskey } from '../Passkey/context';
41
+ import { API_DID_PREFIX, DEFAULT_TIMEOUT, BUSY_STATUS, CHECK_STATUS_INTERVAL } from '../constant';
42
+ import { getWebWalletUrl } from '../utils';
43
+ import DIDConnectTitle from './components/did-connect-title';
44
+ import DownloadTips from './components/download-tips';
45
+ import useProviderList from './hooks/provider-list';
46
+ import useAuthUrl from './hooks/auth-url';
47
+ import { getWalletDid } from '../User/use-did';
48
+ import FallbackConnect from './fallback-connect';
49
+
50
+ function Connect({
51
+ hideCloseButton = false,
52
+ mode = 'dialog',
53
+ action,
54
+ baseUrl = '',
55
+ checkFn,
56
+ checkInterval = CHECK_STATUS_INTERVAL,
57
+ checkTimeout = DEFAULT_TIMEOUT * 1000,
58
+ prefix = API_DID_PREFIX,
59
+ tokenKey = '_t_',
60
+ locale = 'en',
61
+ encKey = '_ek_',
62
+ autoConnect = true,
63
+ forceConnected = true,
64
+ saveConnect = true,
65
+ useSocket = true,
66
+ allowWallet = true,
67
+ provider = '',
68
+ messages = {},
69
+ passkeyBehavior = 'none',
70
+ webWalletUrl = getWebWalletUrl(),
71
+ enabledConnectTypes = ['web', 'mobile', ...Object.keys(OAUTH_PROVIDER)],
72
+ extraContent = null,
73
+ disableSwitchApp = false,
74
+ magicToken = undefined,
75
+ customItems = [],
76
+ onClose = noop,
77
+ onError = noop,
78
+ onSuccess = noop,
79
+ onRecreateSession = noop,
80
+ setColor = noop,
81
+ }) {
82
+ const theme = useTheme();
83
+ const forceUpdate = useUpdate();
84
+ const sessionCtx = use(SessionContext);
85
+ const currentUserDid = getWalletDid(sessionCtx?.session?.user);
86
+
87
+ const {
88
+ t,
89
+ staticState,
90
+ connectState,
91
+ extraParams,
92
+ currentAppInfo,
93
+ currentAppColor,
94
+ // 插件相关
95
+ selectedPlugin,
96
+ blocklet,
97
+ masterBlocklet,
98
+ showWalletOptions,
99
+ setShowWalletOptions,
100
+ } = useStateContext();
101
+ const { state, generate, cancelWhenScanned } = useToken({
102
+ action,
103
+ baseUrl,
104
+ checkFn,
105
+ checkInterval,
106
+ checkTimeout,
107
+ extraParams,
108
+ prefix,
109
+ onError,
110
+ onSuccess,
111
+ locale,
112
+ tokenKey,
113
+ encKey,
114
+ autoConnect,
115
+ forceConnected: forceConnected === true ? currentUserDid || true : forceConnected,
116
+ saveConnect,
117
+ useSocket,
118
+ allowWallet,
119
+ provider,
120
+ });
121
+ const refreshingToken = useRef(false);
122
+ const rootRef = useRef(null);
123
+ const size = useSize(rootRef);
124
+ const isSmallView = useCreation(() => {
125
+ if (size) {
126
+ return size.width < DID_CONNECT_MEDIUM_WIDTH - 50;
127
+ }
128
+ return true;
129
+ }, [size, size?.width]);
130
+
131
+ const [isInitSmallView, setIsInitSmallView] = useState(false);
132
+
133
+ useMount(() => {
134
+ setIsInitSmallView(size?.width < DID_CONNECT_MEDIUM_WIDTH - 50);
135
+ });
136
+
137
+ const { oauthState, setBaseUrl } = useOAuth();
138
+ const { passkeyState, setTargetAppPid } = usePasskey();
139
+
140
+ useMount(() => {
141
+ setBaseUrl(baseUrl);
142
+ setTargetAppPid(blocklet?.appPid);
143
+ // 每次打开 did-connect,都重置状态
144
+ state.reset();
145
+ oauthState.reset();
146
+ passkeyState.reset();
147
+ });
148
+
149
+ useEffect(() => {
150
+ setColor(currentAppColor);
151
+ // eslint-disable-next-line react-hooks/exhaustive-deps
152
+ }, [currentAppColor]);
153
+
154
+ const statusMessages = useCreation(() => {
155
+ return {
156
+ confirm: messages.confirm,
157
+ success: messages.success,
158
+ error: selectedPlugin?.state?.error || state.error || passkeyState.error || oauthState.error || '',
159
+ };
160
+ }, [
161
+ messages.confirm,
162
+ messages.success,
163
+ state.error,
164
+ oauthState.error,
165
+ passkeyState.error,
166
+ selectedPlugin?.state?.error,
167
+ ]);
168
+
169
+ const showStatus = useCreation(() => {
170
+ return (
171
+ BUSY_STATUS.includes(passkeyState.status) ||
172
+ BUSY_STATUS.includes(oauthState.status) ||
173
+ BUSY_STATUS.includes(state.status) ||
174
+ BUSY_STATUS.includes(selectedPlugin?.state?.computedStatus)
175
+ );
176
+ }, [state.status, oauthState.status, passkeyState.status, selectedPlugin?.state?.computedStatus]);
177
+
178
+ const handleReset = useMemoizedFn(async () => {
179
+ onRecreateSession();
180
+ oauthState.reset();
181
+ passkeyState.reset();
182
+ await generate(false);
183
+ });
184
+ const handleRetry = useMemoizedFn(() => {
185
+ connectState?.retryConnect();
186
+ });
187
+ const handleCancel = useMemoizedFn(() => {
188
+ onRecreateSession();
189
+ oauthState.reset();
190
+ passkeyState.reset();
191
+ selectedPlugin?.state?.reset();
192
+ staticState.current.cancelCount++;
193
+ cancelWhenScanned();
194
+ });
195
+
196
+ const { run: debounceHandleTimeout } = useDebounceFn(
197
+ () => {
198
+ if (refreshingToken.current) return;
199
+ if (state.status === 'timeout') {
200
+ refreshingToken.current = true;
201
+ state.reset();
202
+ handleReset();
203
+ refreshingToken.current = false;
204
+ }
205
+ },
206
+ { leading: true, trailing: false }
207
+ );
208
+ // eslint-disable-next-line react-hooks/exhaustive-deps
209
+ useEffect(debounceHandleTimeout, [state.status]);
210
+
211
+ const chooseMethod = useCreation(() => {
212
+ return LOGIN_PROVIDER_NAME[connectState.chooseMethod] || 'DID Wallet';
213
+ }, [connectState.chooseMethod]);
214
+
215
+ const { providerList, hideQRCode, hideChooseList, loadingProviderList } = useProviderList({
216
+ action: state.action,
217
+ sourceAppPid: connectState?.sourceAppPid,
218
+ allowWallet,
219
+ passkeyBehavior,
220
+ webWalletUrl,
221
+ mode,
222
+ blocklet: connectState?.sourceAppPid ? masterBlocklet : blocklet,
223
+ isSmallView,
224
+ });
225
+
226
+ const prevSourceAppPid = usePrevious(connectState?.sourceAppPid);
227
+
228
+ // 切换了当前展示的应用后,需要重新生成二维码
229
+ useUpdateEffect(() => {
230
+ // HACK: connectState.sourceAppPid 是用户在页面中执行操作修改的值,最开始的时候就是 undefined,并在 did-connect 页面加载后,会由 app-info 触发一次更新(无论如何都会触发,这一次更新不应该触发 generate)
231
+ if (isUndefined(prevSourceAppPid)) {
232
+ return;
233
+ }
234
+ generate();
235
+ }, [connectState?.sourceAppPid]);
236
+
237
+ const getSpacingNumber = (value) => {
238
+ const spacingValue = theme.spacing(value);
239
+ return parseInt(spacingValue, 10);
240
+ };
241
+ const closeButton = useCreation(() => {
242
+ return hideCloseButton ? null : (
243
+ <CloseButton
244
+ onClose={onClose}
245
+ sx={{
246
+ position: 'absolute',
247
+ right: 14,
248
+ top: 14,
249
+ }}
250
+ />
251
+ );
252
+ }, [hideCloseButton, onClose]);
253
+
254
+ let content = null;
255
+ if (showStatus) {
256
+ content = (
257
+ <Box
258
+ sx={{
259
+ flex: 1,
260
+ display: 'flex',
261
+ alignItems: 'center',
262
+ justifyContent: 'center',
263
+ }}>
264
+ <Box>
265
+ <ConnectStatus
266
+ status={selectedPlugin?.state?.computedStatus || oauthState.status || passkeyState.status || state.status}
267
+ nextWorkflow={state.nextWorkflow}
268
+ mfaCode={state.mfaCode}
269
+ onCancel={handleCancel}
270
+ onRetry={handleRetry}
271
+ onClose={onClose}
272
+ messages={statusMessages}
273
+ locale={locale}
274
+ className="did-connect__auth-status auth-status"
275
+ loadingIcon={
276
+ connectState.chooseMethod ? (
277
+ <ProviderIcon provider={connectState.chooseMethod} sx={{ color: 'text.primary' }} />
278
+ ) : null
279
+ }
280
+ chooseMethod={chooseMethod}
281
+ hideRetry={connectState.chooseMethod === 'email'}
282
+ hideBack={!!magicToken}
283
+ />
284
+ </Box>
285
+ </Box>
286
+ );
287
+ }
288
+
289
+ const urlWithParams = useAuthUrl({ disableSwitchApp, tokenState: state });
290
+
291
+ // 防止二维码抖动,使用 useCreation 进行缓存
292
+ const qrcode = useCreation(() => {
293
+ const backgroundColor = theme.mode === 'dark' ? theme.palette.grey[600] : 'white';
294
+
295
+ let qrcodeSize = hideChooseList ? 240 - getSpacingNumber(3) * 2 : 196 - getSpacingNumber(2) * 2;
296
+ let padding = hideChooseList ? 3 : 2;
297
+ // 如果显示钱包登录,将 download tips 移动到顶部,调整间距,增加二维码的尺寸和内边距
298
+ if (showWalletOptions) {
299
+ padding = 1;
300
+ qrcodeSize += getSpacingNumber(padding) * 2;
301
+ }
302
+ return (
303
+ <Box
304
+ className="did-connect__qrcode"
305
+ sx={{
306
+ p: padding,
307
+ width: (hideChooseList ? 240 : 196) + getSpacingNumber(1) * 2,
308
+ height: (hideChooseList ? 240 : 196) + getSpacingNumber(1) * 2,
309
+ }}>
310
+ {state.url ? (
311
+ <QRCode
312
+ data={urlWithParams}
313
+ size={qrcodeSize}
314
+ sx={{
315
+ width: qrcodeSize + getSpacingNumber(1) * 2,
316
+ height: qrcodeSize + getSpacingNumber(1) * 2,
317
+ flex: 1,
318
+ backgroundColor,
319
+ p: 1,
320
+ fontSize: 0,
321
+ textAlign: 'center',
322
+ boxSizing: 'border-box',
323
+ borderRadius: 1,
324
+ border: '1px solid',
325
+ borderColor: 'divider',
326
+ }}
327
+ config={{
328
+ backgroundOptions: {
329
+ color: backgroundColor,
330
+ },
331
+ }}
332
+ />
333
+ ) : (
334
+ <Skeleton
335
+ animation="wave"
336
+ variant="rectangular"
337
+ sx={{
338
+ position: 'absolute',
339
+ left: getSpacingNumber(2) + 1,
340
+ right: getSpacingNumber(2) + 1,
341
+ top: getSpacingNumber(2) + 1,
342
+ bottom: getSpacingNumber(2) + 1,
343
+ borderRadius: 1,
344
+ zIndex: 1,
345
+ width: 'auto',
346
+ height: 'auto',
347
+ }}
348
+ />
349
+ )}
350
+ </Box>
351
+ );
352
+ }, [urlWithParams, hideChooseList, showWalletOptions]);
353
+
354
+ const didConnectFlexDirection = useCreation(() => {
355
+ if (hideChooseList) {
356
+ return 'column-reverse';
357
+ }
358
+ if (!hideQRCode && isInitSmallView) {
359
+ return 'column';
360
+ }
361
+ return 'row';
362
+ }, [hideChooseList, isInitSmallView, hideQRCode]);
363
+
364
+ const qrcodeContent = useCreation(() => {
365
+ if (!qrcode) return null;
366
+ return (
367
+ <Box
368
+ sx={{
369
+ display: 'flex',
370
+ alignItems: 'center',
371
+ overflowX: 'auto',
372
+ overflowY: 'visible',
373
+ maxWidth: '100%',
374
+ margin: 'auto',
375
+ }}>
376
+ <Box
377
+ sx={{
378
+ fontSize: 0,
379
+ position: 'relative',
380
+ ...(showWalletOptions
381
+ ? {
382
+ mt: hideChooseList ? 4 : 2.5,
383
+ mb: hideChooseList ? 0 : 0,
384
+ }
385
+ : {
386
+ mb: hideChooseList ? 4 : 2.5,
387
+ mt: 0,
388
+ }),
389
+ }}>
390
+ {qrcode}
391
+ <Box
392
+ sx={{
393
+ position: 'absolute',
394
+ color: 'text.secondary',
395
+ fontSize: 12,
396
+ zIndex: 1,
397
+ whiteSpace: 'nowrap',
398
+ ...(showWalletOptions
399
+ ? {
400
+ top: hideChooseList ? -8 : -4,
401
+ transform: 'translateY(-100%)',
402
+ }
403
+ : {
404
+ bottom: hideChooseList ? -8 : -4,
405
+ transform: 'translateY(100%)',
406
+ }),
407
+ left: 0,
408
+ right: 0,
409
+ textAlign: 'center',
410
+ }}>
411
+ {t('scanWithWallet1')} <DownloadTips /> {t('scanWithWallet2')}
412
+ </Box>
413
+ </Box>
414
+ </Box>
415
+ );
416
+ }, [qrcode, hideChooseList, showWalletOptions]);
417
+
418
+ const fallbackContent = (
419
+ <Box
420
+ className="did-connect__body"
421
+ sx={{
422
+ display: 'flex',
423
+ flexDirection: didConnectFlexDirection,
424
+ justifyContent: 'center',
425
+ alignItems: 'stretch',
426
+ flex: 1,
427
+ gap: !hideQRCode && isSmallView ? 0 : 1.5,
428
+ overflow: 'visible',
429
+ px: hideChooseList ? 2 : 0,
430
+ }}>
431
+ {!showStatus && !hideQRCode ? (
432
+ <>
433
+ {qrcodeContent}
434
+ {hideChooseList ? null : (
435
+ <Box>
436
+ <Divider
437
+ orientation="vertical"
438
+ sx={{
439
+ fontSize: 12,
440
+ color: 'text.hint',
441
+ '&::before, &::after': {
442
+ borderColor: 'divider',
443
+ },
444
+ }}>
445
+ or
446
+ </Divider>
447
+ </Box>
448
+ )}
449
+ </>
450
+ ) : null}
451
+ <Box
452
+ sx={{
453
+ display: 'flex',
454
+ flex: 1,
455
+ }}>
456
+ {/* eslint-disable-next-line no-nested-ternary */}
457
+ {content}
458
+ <ConnectProviderList
459
+ slotProps={{
460
+ root: {
461
+ sx: [showStatus ? { display: 'none' } : {}],
462
+ },
463
+ }}
464
+ allowWallet={allowWallet}
465
+ size={hideQRCode && mode !== 'dialog' ? 'normal' : 'small'}
466
+ tokenState={state}
467
+ hideQRCode={hideQRCode}
468
+ messages={messages}
469
+ tokenKey={tokenKey}
470
+ onSuccess={onSuccess}
471
+ passkeyBehavior={passkeyBehavior}
472
+ webWalletUrl={webWalletUrl}
473
+ extraContent={extraContent}
474
+ enabledConnectTypes={enabledConnectTypes}
475
+ onReset={handleReset}
476
+ disableSwitchApp={disableSwitchApp}
477
+ forceUpdate={forceUpdate}
478
+ magicToken={magicToken}
479
+ baseUrl={baseUrl}
480
+ customItems={customItems}
481
+ providerList={providerList}
482
+ qrcode={qrcodeContent}
483
+ />
484
+ </Box>
485
+ </Box>
486
+ );
487
+ let contentResult = fallbackContent;
488
+ if (selectedPlugin) {
489
+ contentResult = selectedPlugin.renderPlaceholder({
490
+ fallback: fallbackContent,
491
+ forceUpdate,
492
+ onSuccess,
493
+ onError,
494
+ });
495
+ } else {
496
+ contentResult = fallbackContent;
497
+ }
498
+
499
+ const containerWidth = hideQRCode || showStatus ? DID_CONNECT_SMALL_WIDTH : DID_CONNECT_MEDIUM_WIDTH;
500
+
501
+ if (loadingProviderList) {
502
+ return <FallbackConnect />;
503
+ }
504
+ return (
505
+ <Box
506
+ ref={rootRef}
507
+ className="did-connect__root"
508
+ sx={{
509
+ backgroundColor: 'background.default',
510
+ display: 'flex',
511
+ flexDirection: 'column',
512
+ height: '100%',
513
+ position: 'relative',
514
+ maxWidth: '100%',
515
+ width:
516
+ // eslint-disable-next-line no-nested-ternary
517
+ mode === 'drawer' ? '100%' : showWalletOptions ? containerWidth - 20 : containerWidth,
518
+ transition: 'width 0.2s ease-in-out',
519
+ margin: 'auto',
520
+ p: 3,
521
+ pb: 0,
522
+ gap: 2.5,
523
+ }}>
524
+ <Box data-did-auth-url={state.url} sx={{ display: 'none' }} />
525
+ <DIDConnectTitle
526
+ title={messages.title}
527
+ description={messages.scan}
528
+ extraContent={extraContent}
529
+ disableSwitchApp={disableSwitchApp}
530
+ showWalletOptions={showWalletOptions}
531
+ onBack={() => setShowWalletOptions(false)}
532
+ />
533
+ <AutoHeight initHeight={24 + 16 + 32}>{contentResult}</AutoHeight>
534
+ <DIDConnectFooter currentAppInfo={currentAppInfo} currentAppColor={currentAppColor} />
535
+ {closeButton}
536
+ </Box>
537
+ );
538
+ }
539
+
540
+ Connect.propTypes = {
541
+ mode: PropTypes.oneOf(['dialog', 'drawer', 'page']),
542
+
543
+ action: PropTypes.string.isRequired,
544
+ baseUrl: PropTypes.string,
545
+ checkFn: PropTypes.func.isRequired,
546
+ checkInterval: PropTypes.number,
547
+ checkTimeout: PropTypes.number,
548
+ // extraParams: PropTypes.object, // 需要使用 useStateContext 中导出的
549
+ prefix: PropTypes.string,
550
+ messages: PropTypes.object,
551
+ tokenKey: PropTypes.string,
552
+ locale: PropTypes.oneOf(['en', 'zh', '']),
553
+ encKey: PropTypes.string,
554
+ autoConnect: PropTypes.bool,
555
+ forceConnected: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
556
+ saveConnect: PropTypes.bool,
557
+ useSocket: PropTypes.bool,
558
+ extraContent: PropTypes.any,
559
+ passkeyBehavior: PropTypes.oneOf(['none', 'both', 'only-existing', 'only-new']),
560
+ enabledConnectTypes: PropTypes.arrayOf(PropTypes.oneOf(['web', 'mobile', ...Object.keys(OAUTH_PROVIDER)])),
561
+ webWalletUrl: PropTypes.string,
562
+
563
+ allowWallet: PropTypes.bool,
564
+ provider: PropTypes.oneOf([LOGIN_PROVIDER.WALLET, ...Object.keys(OAUTH_PROVIDER), '']),
565
+ hideCloseButton: PropTypes.bool,
566
+ disableSwitchApp: PropTypes.bool,
567
+ onClose: PropTypes.func,
568
+ onError: PropTypes.func,
569
+ onSuccess: PropTypes.func,
570
+ onRecreateSession: PropTypes.func,
571
+ setColor: PropTypes.func,
572
+ magicToken: PropTypes.string,
573
+ customItems: PropTypes.arrayOf(PropTypes.node),
574
+ };
575
+
576
+ function WrapConnect({ testOnlyBorderColor = undefined, ...props }) {
577
+ const { checkFn, extraParams = {}, blocklet, masterBlocklet, action, locale = 'en' } = props;
578
+ if (typeof checkFn !== 'function') {
579
+ throw new Error('Cannot initialize did connect component without a fetchFn');
580
+ }
581
+
582
+ return (
583
+ <StateProvider
584
+ blocklet={blocklet}
585
+ masterBlocklet={masterBlocklet}
586
+ action={action}
587
+ locale={locale}
588
+ extraParams={extraParams}
589
+ testOnlyBorderColor={testOnlyBorderColor}
590
+ sx={{
591
+ position: 'relative',
592
+ width: '100%',
593
+ height: '100%',
594
+ lineHeight: 1.2,
595
+ color: 'grey.700',
596
+ '&, & *, & *:before, & *:after': {
597
+ fontFamily: 'Lexend', // 保持跟 DID Wallet 一致
598
+ boxSizing: 'border-box',
599
+ },
600
+ }}>
601
+ <Connect {...props} />
602
+ </StateProvider>
603
+ );
604
+ }
605
+
606
+ WrapConnect.propTypes = {
607
+ checkFn: PropTypes.func.isRequired,
608
+ extraParams: PropTypes.object,
609
+ blocklet: PropTypes.object.isRequired,
610
+ masterBlocklet: PropTypes.object,
611
+ action: PropTypes.string.isRequired,
612
+ locale: PropTypes.string,
613
+ testOnlyBorderColor: PropTypes.string,
614
+ };
615
+
616
+ export default WrapConnect;
@@ -0,0 +1,47 @@
1
+ import { DIDConnectFooter } from '@arcblock/ux/lib/DIDConnect';
2
+ import { DID_CONNECT_SMALL_WIDTH } from '@arcblock/ux/lib/Util/constant';
3
+ import { getCurrentApp } from '@arcblock/ux/lib/Util/federated';
4
+ import { Box, Skeleton } from '@mui/material';
5
+
6
+ export default function FallbackConnect(props) {
7
+ const currentAppInfo = globalThis.blocklet ? getCurrentApp(globalThis.blocklet) : globalThis.env;
8
+ return (
9
+ <Box
10
+ className="did-connect__root"
11
+ sx={{
12
+ backgroundColor: 'background.default',
13
+ display: 'flex',
14
+ flexDirection: 'column',
15
+ height: '100%',
16
+ position: 'relative',
17
+ maxWidth: '100%',
18
+ // eslint-disable-next-line react/prop-types
19
+ width: props.mode === 'drawer' ? '100%' : DID_CONNECT_SMALL_WIDTH - 20,
20
+ transition: 'width 0.2s ease-in-out',
21
+ margin: 'auto',
22
+ p: 3,
23
+ pb: 0,
24
+ gap: 2.5,
25
+ }}>
26
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
27
+ <Skeleton variant="rounded" height={28} sx={{ width: '6rem' }} />
28
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
29
+ <Skeleton variant="rounded" sx={{ fontSize: '13px', height: '15px' }} />
30
+ <Skeleton variant="rounded" sx={{ fontSize: '13px', height: '15px', width: '3rem' }} />
31
+ </Box>
32
+ </Box>
33
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
34
+ <Skeleton variant="rounded" sx={{ height: '35px' }} />
35
+ <Skeleton variant="rounded" sx={{ height: '35px' }} />
36
+ <Skeleton variant="rounded" sx={{ height: '35px' }} />
37
+ <Box sx={{ display: 'flex', gap: 1.5, mt: 1 }}>
38
+ <Skeleton variant="rounded" sx={{ flex: 1, height: '32px' }} />
39
+ <Skeleton variant="rounded" sx={{ flex: 1, height: '32px' }} />
40
+ <Skeleton variant="rounded" sx={{ flex: 1, height: '32px' }} />
41
+ <Skeleton variant="rounded" sx={{ flex: 1, height: '32px' }} />
42
+ </Box>
43
+ </Box>
44
+ <DIDConnectFooter currentAppInfo={currentAppInfo} />
45
+ </Box>
46
+ );
47
+ }
@@ -3,6 +3,7 @@ import { useCreation, useRequest } from 'ahooks';
3
3
  import isNil from 'lodash/isNil';
4
4
  import { LOGIN_PROVIDER } from '@arcblock/ux/lib/Util/constant';
5
5
  import { getBlockletData, getFederatedEnabled, getMaster } from '@arcblock/ux/lib/Util/federated';
6
+ import { useState } from 'react';
6
7
 
7
8
  const getAuthenticationConfig = async ({ sourceAppPid }) => {
8
9
  const blocklet = globalThis?.blocklet;
@@ -56,12 +57,15 @@ export default function useProviderList({
56
57
  blocklet = globalThis.blocklet,
57
58
  isSmallView = false,
58
59
  }) {
60
+ const [loadingProviderList, setLoadingProviderList] = useState(false);
59
61
  const actionList = ['login', 'invite', 'connect-to-did-space', 'connect-to-did-domain', 'destroy-self'];
60
62
  const browser = useBrowser();
61
63
 
62
64
  const { data: loginProviderList = [] } = useRequest(
63
65
  async () => {
66
+ setLoadingProviderList(true);
64
67
  const data = await getLoginProviderList({ sourceAppPid });
68
+ setLoadingProviderList(false);
65
69
  return data;
66
70
  },
67
71
  { refreshDeps: [sourceAppPid] }
@@ -121,6 +125,7 @@ export default function useProviderList({
121
125
  hideChooseList,
122
126
  hideQRCode,
123
127
  providerList: showedLoginProviderList,
128
+ loadingProviderList,
124
129
  };
125
130
 
126
131
  return result;