@arcblock/did-connect-react 3.2.19 → 3.3.1
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/docs/assets/diagram/core-components-session-provider-01.ja.jpg +0 -0
- package/docs/assets/diagram/core-components-session-provider-01.jpg +0 -0
- package/docs/assets/diagram/core-components-session-provider-01.zh-TW.jpg +0 -0
- package/docs/assets/diagram/core-components-session-provider-01.zh.jpg +0 -0
- package/docs/assets/diagram/did-connect-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/did-connect-diagram-0.jpg +0 -0
- package/docs/assets/diagram/did-connect-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/did-connect-diagram-0.zh.jpg +0 -0
- package/docs/assets/diagram/overview-01.ja.jpg +0 -0
- package/docs/assets/diagram/overview-01.jpg +0 -0
- package/docs/assets/diagram/overview-01.zh-TW.jpg +0 -0
- package/docs/assets/diagram/overview-01.zh.jpg +0 -0
- package/docs/assets/diagram/use-connect-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/use-connect-diagram-0.jpg +0 -0
- package/docs/assets/diagram/use-connect-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/use-connect-diagram-0.zh.jpg +0 -0
- package/docs/core-components-did-connect.ja.md +3 -50
- package/docs/core-components-did-connect.md +5 -52
- package/docs/core-components-did-connect.zh-TW.md +3 -50
- package/docs/core-components-did-connect.zh.md +3 -50
- package/docs/core-components-session-provider.ja.md +3 -45
- package/docs/core-components-session-provider.md +3 -45
- package/docs/core-components-session-provider.zh-TW.md +3 -45
- package/docs/core-components-session-provider.zh.md +3 -45
- package/docs/hooks-use-connect.ja.md +3 -39
- package/docs/hooks-use-connect.md +4 -40
- package/docs/hooks-use-connect.zh-TW.md +3 -39
- package/docs/hooks-use-connect.zh.md +3 -39
- package/docs/overview.ja.md +3 -45
- package/docs/overview.md +3 -45
- package/docs/overview.zh-TW.md +3 -45
- package/docs/overview.zh.md +3 -45
- package/lib/Connect/assets/locale.js +4 -2
- package/lib/Connect/components/login-item/connect-choose-list.js +2 -2
- package/lib/Connect/components/login-item/connect-provider-list.js +299 -0
- package/lib/Connect/components/login-item/login-method-item.js +42 -41
- package/lib/Connect/hooks/provider-list.js +50 -0
- package/lib/Connect/index.js +180 -196
- package/lib/OAuth/context.js +33 -32
- package/lib/Passkey/context.js +64 -63
- package/lib/Session/hooks/use-verify.js +1 -1
- package/lib/Session/libs/locales.js +3 -3
- package/lib/package.json.js +1 -1
- package/package.json +9 -9
- package/src/Connect/assets/locale.js +2 -0
- package/src/Connect/components/login-item/connect-choose-list.jsx +5 -5
- package/src/Connect/components/login-item/connect-provider-list.jsx +392 -0
- package/src/Connect/components/login-item/login-method-item.jsx +31 -28
- package/src/Connect/hooks/provider-list.js +127 -0
- package/src/Connect/index.jsx +8 -27
- package/src/OAuth/context.jsx +10 -2
- package/src/Passkey/context.jsx +3 -0
- package/src/Session/hooks/use-verify.jsx +1 -1
- package/src/Session/libs/locales.js +3 -3
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import { Box } from '@mui/material';
|
|
3
|
+
import { LOGIN_PROVIDER, LOGIN_PROVIDER_NAME } from '@arcblock/ux/lib/Util/constant';
|
|
4
|
+
import { checkSameProtocol, getWebWalletUrl } from '@arcblock/ux/lib/Util/wallet';
|
|
5
|
+
import noop from 'lodash/noop';
|
|
6
|
+
import { useMemoizedFn } from 'ahooks';
|
|
7
|
+
import Cookie from 'js-cookie';
|
|
8
|
+
import { detectWalletExtension, getCookieOptions } from '@arcblock/ux/lib/Util';
|
|
9
|
+
import { useEffect, useRef } from 'react';
|
|
10
|
+
import { mergeSx } from '@arcblock/ux/lib/Util/style';
|
|
11
|
+
import ProviderIcon from '@arcblock/ux/lib/DIDConnect/provider-icon';
|
|
12
|
+
import { GA_LAST_LOGIN_METHOD } from '@arcblock/ux/lib/withTracker/constant';
|
|
13
|
+
import { translate } from '@arcblock/ux/lib/Locale/util';
|
|
14
|
+
import Empty from '@arcblock/ux/lib/Empty';
|
|
15
|
+
import { useBrowser } from '@arcblock/react-hooks';
|
|
16
|
+
|
|
17
|
+
import MobileLoginItem from './mobile-login-item';
|
|
18
|
+
import WebLoginItem from './web-login-item';
|
|
19
|
+
import LoginMethodItem from './login-method-item';
|
|
20
|
+
import { useOAuth } from '../../../OAuth';
|
|
21
|
+
import { useStateContext } from '../../contexts/state';
|
|
22
|
+
import { getApiErrorMessage, getAppId, logger } from '../../../utils';
|
|
23
|
+
import PasskeyLoginItem from './passkey-login-item';
|
|
24
|
+
import { usePasskey } from '../../../Passkey';
|
|
25
|
+
import { useEmailPlugin } from '../../plugins';
|
|
26
|
+
import translations from '../../assets/locale';
|
|
27
|
+
|
|
28
|
+
export default function ConnectProviderList({
|
|
29
|
+
onSuccess = noop,
|
|
30
|
+
onError = noop,
|
|
31
|
+
size = 'small',
|
|
32
|
+
tokenState,
|
|
33
|
+
webWalletUrl = getWebWalletUrl(),
|
|
34
|
+
tokenKey,
|
|
35
|
+
passkeyBehavior = 'none',
|
|
36
|
+
onReset = noop,
|
|
37
|
+
providerList = [],
|
|
38
|
+
slotProps = {},
|
|
39
|
+
disableSwitchApp = false,
|
|
40
|
+
forceUpdate = noop,
|
|
41
|
+
magicToken = undefined,
|
|
42
|
+
baseUrl = '/',
|
|
43
|
+
customItems = [],
|
|
44
|
+
}) {
|
|
45
|
+
const walletLoginRef = useRef(null);
|
|
46
|
+
const webLoginRef = useRef(null);
|
|
47
|
+
const passkeyLoginRef = useRef(null);
|
|
48
|
+
|
|
49
|
+
const browser = useBrowser();
|
|
50
|
+
const isSameProtocol = checkSameProtocol(webWalletUrl);
|
|
51
|
+
const extension = detectWalletExtension();
|
|
52
|
+
|
|
53
|
+
const { loginOAuth, logoutOAuth, t: oauthT, oauthState } = useOAuth();
|
|
54
|
+
const { passkeyState } = usePasskey();
|
|
55
|
+
const { extraParams, locale, connectState, plugins, setPlugins, setSelectedPlugin, getPlugin } = useStateContext();
|
|
56
|
+
|
|
57
|
+
const t = useMemoizedFn((key, data = {}) => {
|
|
58
|
+
return translate(translations, key, locale, 'en', data);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const handleLoginOAuth = useMemoizedFn(async (item) => {
|
|
62
|
+
localStorage.setItem(GA_LAST_LOGIN_METHOD, item.provider);
|
|
63
|
+
tokenState.reset();
|
|
64
|
+
oauthState.reset({
|
|
65
|
+
status: 'scanned',
|
|
66
|
+
});
|
|
67
|
+
passkeyState.reset();
|
|
68
|
+
connectState.chooseMethod = item.provider;
|
|
69
|
+
|
|
70
|
+
const sourceAppPid = extraParams?.sourceAppPid;
|
|
71
|
+
try {
|
|
72
|
+
oauthState.loading = true;
|
|
73
|
+
oauthState.status = 'scanned';
|
|
74
|
+
const loginResult = await loginOAuth(item, {
|
|
75
|
+
action: tokenState.action,
|
|
76
|
+
...extraParams,
|
|
77
|
+
});
|
|
78
|
+
const cookieOptions = getCookieOptions({ returnDomain: false });
|
|
79
|
+
Cookie.remove('connected_did', cookieOptions);
|
|
80
|
+
Cookie.remove('connected_pk', cookieOptions);
|
|
81
|
+
Cookie.remove('connected_wallet_os', cookieOptions);
|
|
82
|
+
|
|
83
|
+
if (loginResult?.sessionToken) {
|
|
84
|
+
await onSuccess(
|
|
85
|
+
{
|
|
86
|
+
...loginResult,
|
|
87
|
+
encrypted: false,
|
|
88
|
+
},
|
|
89
|
+
(val) => val,
|
|
90
|
+
{
|
|
91
|
+
sourceAppPid,
|
|
92
|
+
connected_app: getAppId(tokenState.appInfo, tokenState.memberAppInfo),
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
oauthState.loading = false;
|
|
96
|
+
oauthState.status = 'succeed';
|
|
97
|
+
}
|
|
98
|
+
} catch (e) {
|
|
99
|
+
logger.error(`Failed login OAuth: ${item.provider}`, e);
|
|
100
|
+
const errorMessage = getApiErrorMessage(e, oauthT('loginOAuthFailed'));
|
|
101
|
+
oauthState.loading = false;
|
|
102
|
+
oauthState.error = errorMessage;
|
|
103
|
+
oauthState.status = 'error';
|
|
104
|
+
await logoutOAuth({ provider: item.provider });
|
|
105
|
+
onError(new Error(errorMessage));
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const defaultRetryConnect = useMemoizedFn(async () => {
|
|
110
|
+
tokenState.reset();
|
|
111
|
+
await onReset();
|
|
112
|
+
tokenState.status = 'created';
|
|
113
|
+
connectState.chooseMethod = 'wallet';
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const showEmailLogin = providerList.some((p) => p.provider === LOGIN_PROVIDER.EMAIL);
|
|
117
|
+
|
|
118
|
+
const emailPlugin = useEmailPlugin({ baseUrl });
|
|
119
|
+
|
|
120
|
+
const setupMagicToken = useMemoizedFn(() => {
|
|
121
|
+
if (
|
|
122
|
+
magicToken &&
|
|
123
|
+
providerList.some((provider) => provider.name === LOGIN_PROVIDER.EMAIL) &&
|
|
124
|
+
plugins.some((plugin) => plugin.name === LOGIN_PROVIDER.EMAIL)
|
|
125
|
+
) {
|
|
126
|
+
const plugin = getPlugin(LOGIN_PROVIDER.EMAIL);
|
|
127
|
+
if (plugin.state.status === 'idle') {
|
|
128
|
+
localStorage.setItem(GA_LAST_LOGIN_METHOD, 'email');
|
|
129
|
+
plugin.state.reset();
|
|
130
|
+
plugin.state.magicToken = magicToken;
|
|
131
|
+
connectState.chooseMethod = LOGIN_PROVIDER.EMAIL;
|
|
132
|
+
setSelectedPlugin(plugin);
|
|
133
|
+
forceUpdate();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// 考虑到切换账号的情况,此时 showEmailLogin 会根据当前上下文的 blocklet 发生变化,所以需要监听 showEmailLogin 的变化
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
const finalList = [];
|
|
141
|
+
if (showEmailLogin) {
|
|
142
|
+
// 尽可能确保在 baseUrl 不变的情况下,不更改 email 插件的数据,以确保页面的展示不会出现闪烁
|
|
143
|
+
const prevEmailPlugin = getPlugin(LOGIN_PROVIDER.EMAIL);
|
|
144
|
+
if (prevEmailPlugin && prevEmailPlugin.baseUrl === emailPlugin.baseUrl) {
|
|
145
|
+
finalList.push(prevEmailPlugin);
|
|
146
|
+
} else {
|
|
147
|
+
finalList.push(emailPlugin);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
setPlugins(finalList);
|
|
151
|
+
connectState.retryConnect = defaultRetryConnect;
|
|
152
|
+
// HACK: 必须要设置延迟,不然拿不到最新的 plugins 的值
|
|
153
|
+
setTimeout(() => {
|
|
154
|
+
setupMagicToken();
|
|
155
|
+
}, 100);
|
|
156
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
157
|
+
}, [showEmailLogin]);
|
|
158
|
+
|
|
159
|
+
const renderProviderList = providerList
|
|
160
|
+
.map((item) => {
|
|
161
|
+
if (item.provider === LOGIN_PROVIDER.WALLET) {
|
|
162
|
+
if (browser.mobile.any) {
|
|
163
|
+
return (
|
|
164
|
+
<MobileLoginItem
|
|
165
|
+
key={LOGIN_PROVIDER.WALLET}
|
|
166
|
+
ref={walletLoginRef}
|
|
167
|
+
tokenState={tokenState}
|
|
168
|
+
sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
|
|
169
|
+
locale={locale}
|
|
170
|
+
tokenKey={tokenKey}
|
|
171
|
+
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
|
+
}}
|
|
209
|
+
/>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (item.provider === LOGIN_PROVIDER.PASSKEY) {
|
|
215
|
+
if (passkeyBehavior === 'none') {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
return (
|
|
219
|
+
<PasskeyLoginItem
|
|
220
|
+
key={LOGIN_PROVIDER.PASSKEY}
|
|
221
|
+
ref={passkeyLoginRef}
|
|
222
|
+
onSuccess={onSuccess}
|
|
223
|
+
onError={onError}
|
|
224
|
+
tokenState={tokenState}
|
|
225
|
+
behavior={passkeyBehavior}
|
|
226
|
+
sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
|
|
227
|
+
onClick={() => {
|
|
228
|
+
localStorage.setItem(GA_LAST_LOGIN_METHOD, 'passkey');
|
|
229
|
+
const connectFn = passkeyLoginRef.current.connect;
|
|
230
|
+
connectState.chooseMethod = 'passkey';
|
|
231
|
+
connectState.retryConnect = () => {
|
|
232
|
+
passkeyState.verifying = true;
|
|
233
|
+
connectState.chooseMethod = 'passkey';
|
|
234
|
+
connectFn();
|
|
235
|
+
};
|
|
236
|
+
}}
|
|
237
|
+
slotProps={{
|
|
238
|
+
icon: {
|
|
239
|
+
sx: {
|
|
240
|
+
fontSize: 24,
|
|
241
|
+
'& svg': {
|
|
242
|
+
fontSize: 24,
|
|
243
|
+
width: '1em',
|
|
244
|
+
height: '1em',
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
}}
|
|
249
|
+
/>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (item.provider === LOGIN_PROVIDER.EMAIL) {
|
|
254
|
+
const findEmailPlugin = plugins.find((p) => p.name === LOGIN_PROVIDER.EMAIL);
|
|
255
|
+
if (!findEmailPlugin) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
return findEmailPlugin?.renderListItem({
|
|
259
|
+
key: findEmailPlugin.name,
|
|
260
|
+
sx: [size === 'small' ? { p: 1 } : { p: 2 }],
|
|
261
|
+
// forceUpdate,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (item.type === 'oauth') {
|
|
266
|
+
return (
|
|
267
|
+
<LoginMethodItem
|
|
268
|
+
key={item.provider}
|
|
269
|
+
title={LOGIN_PROVIDER_NAME[item.provider]}
|
|
270
|
+
icon={
|
|
271
|
+
<ProviderIcon
|
|
272
|
+
provider={item.provider}
|
|
273
|
+
sx={{
|
|
274
|
+
transform: 'scale(0.95)',
|
|
275
|
+
width: 24,
|
|
276
|
+
height: 24,
|
|
277
|
+
color: 'text.primary',
|
|
278
|
+
}}
|
|
279
|
+
/>
|
|
280
|
+
}
|
|
281
|
+
onClick={() => {
|
|
282
|
+
handleLoginOAuth(item);
|
|
283
|
+
connectState.retryConnect = () => {
|
|
284
|
+
handleLoginOAuth(item);
|
|
285
|
+
};
|
|
286
|
+
}}
|
|
287
|
+
sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
|
|
288
|
+
/>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return null;
|
|
293
|
+
})
|
|
294
|
+
.filter(Boolean);
|
|
295
|
+
|
|
296
|
+
return (
|
|
297
|
+
<Box className="did-connect__choose" sx={mergeSx({ flex: 1 }, slotProps?.root?.sx)}>
|
|
298
|
+
<Box
|
|
299
|
+
sx={{
|
|
300
|
+
display: 'flex',
|
|
301
|
+
flexDirection: 'column',
|
|
302
|
+
gap: 2,
|
|
303
|
+
}}>
|
|
304
|
+
<Box
|
|
305
|
+
sx={[
|
|
306
|
+
{
|
|
307
|
+
display: 'flex',
|
|
308
|
+
flexDirection: 'column',
|
|
309
|
+
gap: 1.5,
|
|
310
|
+
},
|
|
311
|
+
]}>
|
|
312
|
+
<Box
|
|
313
|
+
sx={{
|
|
314
|
+
display: 'grid',
|
|
315
|
+
gridTemplateColumns: 'repeat(12, 1fr)' /* 灵活的12列基础网格 */,
|
|
316
|
+
gap: 1.25,
|
|
317
|
+
'.arc-login-item:nth-child(-n+3)': {
|
|
318
|
+
gridColumn: 'span 12',
|
|
319
|
+
},
|
|
320
|
+
'.arc-login-item:nth-child(4):last-child': {
|
|
321
|
+
gridColumn: 'span 12',
|
|
322
|
+
},
|
|
323
|
+
'.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':
|
|
324
|
+
{
|
|
325
|
+
gridColumn: 'span 6',
|
|
326
|
+
justifyContent: 'center',
|
|
327
|
+
'.arc-login-item__body, .other-item-icon': {
|
|
328
|
+
display: 'none',
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
'.arc-login-item:nth-child(4):nth-last-child(3), .arc-login-item:nth-child(4):nth-last-child(3) ~ .arc-login-item':
|
|
332
|
+
{
|
|
333
|
+
justifyContent: 'center',
|
|
334
|
+
gridColumn: 'span 4',
|
|
335
|
+
'.arc-login-item__body, .other-item-icon': {
|
|
336
|
+
display: 'none',
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
'.arc-login-item:nth-child(4):nth-last-child(4), .arc-login-item:nth-child(4):nth-last-child(4) ~ .arc-login-item':
|
|
340
|
+
{
|
|
341
|
+
gridColumn: 'span 3',
|
|
342
|
+
justifyContent: 'center',
|
|
343
|
+
'.arc-login-item__body, .other-item-icon': {
|
|
344
|
+
display: 'none',
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
'&:has(.arc-login-item:nth-child(8)) .arc-login-item:nth-child(n+4)': {
|
|
348
|
+
gridColumn: 'span 3',
|
|
349
|
+
justifyContent: 'center',
|
|
350
|
+
'.arc-login-item__body, .other-item-icon': {
|
|
351
|
+
display: 'none',
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
}}>
|
|
355
|
+
{renderProviderList.length > 0 ? (
|
|
356
|
+
renderProviderList
|
|
357
|
+
) : (
|
|
358
|
+
<Empty
|
|
359
|
+
className="arc-login-item"
|
|
360
|
+
sx={{
|
|
361
|
+
'.empty-content': {
|
|
362
|
+
textAlign: 'center',
|
|
363
|
+
},
|
|
364
|
+
}}>
|
|
365
|
+
{t('noAuthenticationProvider')}
|
|
366
|
+
</Empty>
|
|
367
|
+
)}
|
|
368
|
+
</Box>
|
|
369
|
+
{customItems.map((item) => (!item ? null : item))}
|
|
370
|
+
</Box>
|
|
371
|
+
</Box>
|
|
372
|
+
</Box>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
ConnectProviderList.propTypes = {
|
|
377
|
+
onSuccess: PropTypes.func,
|
|
378
|
+
onError: PropTypes.func,
|
|
379
|
+
size: PropTypes.oneOf(['small', 'normal', 'large']),
|
|
380
|
+
tokenState: PropTypes.object.isRequired,
|
|
381
|
+
webWalletUrl: PropTypes.string,
|
|
382
|
+
tokenKey: PropTypes.string.isRequired,
|
|
383
|
+
passkeyBehavior: PropTypes.oneOf(['none', 'both', 'only-existing', 'only-new']),
|
|
384
|
+
onReset: PropTypes.func,
|
|
385
|
+
providerList: PropTypes.array,
|
|
386
|
+
slotProps: PropTypes.object,
|
|
387
|
+
disableSwitchApp: PropTypes.bool,
|
|
388
|
+
forceUpdate: PropTypes.func,
|
|
389
|
+
magicToken: PropTypes.string,
|
|
390
|
+
baseUrl: PropTypes.string,
|
|
391
|
+
customItems: PropTypes.arrayOf(PropTypes.node),
|
|
392
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box, Typography, useTheme } from '@mui/material';
|
|
1
|
+
import { Box, Tooltip, Typography, useTheme } from '@mui/material';
|
|
2
2
|
import { Icon } from '@iconify/react';
|
|
3
3
|
import { isValidElement } from 'react';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
@@ -19,6 +19,7 @@ export default function LoginMethodItem({
|
|
|
19
19
|
return (
|
|
20
20
|
<Box
|
|
21
21
|
{...rest}
|
|
22
|
+
className="arc-login-item"
|
|
22
23
|
sx={mergeSx(
|
|
23
24
|
{
|
|
24
25
|
display: 'flex',
|
|
@@ -48,34 +49,36 @@ export default function LoginMethodItem({
|
|
|
48
49
|
},
|
|
49
50
|
rest?.sx
|
|
50
51
|
)}>
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
icon
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
52
|
+
<Tooltip title={title}>
|
|
53
|
+
<Box
|
|
54
|
+
className="arc-login-item__icon"
|
|
55
|
+
sx={mergeSx(
|
|
56
|
+
{
|
|
57
|
+
display: 'flex',
|
|
58
|
+
justifyContent: 'center',
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
color: 'text.primary',
|
|
61
|
+
},
|
|
62
|
+
slotProps?.icon?.sx
|
|
63
|
+
)}>
|
|
64
|
+
{isValidElement(icon) ? (
|
|
65
|
+
icon
|
|
66
|
+
) : (
|
|
67
|
+
<Box
|
|
68
|
+
component={Icon}
|
|
69
|
+
icon={icon}
|
|
70
|
+
sx={{
|
|
71
|
+
transform: `scale(${iconScale})`,
|
|
72
|
+
width: 24,
|
|
73
|
+
height: 24,
|
|
74
|
+
}}
|
|
75
|
+
/>
|
|
76
|
+
)}
|
|
77
|
+
</Box>
|
|
78
|
+
</Tooltip>
|
|
76
79
|
{mode === 'normal' ? (
|
|
77
80
|
<>
|
|
78
|
-
<Box sx={{ display: 'flex', flexDirection: 'column', flex: 1 }}>
|
|
81
|
+
<Box className="arc-login-item__body" sx={{ display: 'flex', flexDirection: 'column', flex: 1 }}>
|
|
79
82
|
<Typography
|
|
80
83
|
sx={{
|
|
81
84
|
fontSize: 14,
|
|
@@ -114,5 +117,5 @@ LoginMethodItem.propTypes = {
|
|
|
114
117
|
icon: PropTypes.any.isRequired,
|
|
115
118
|
iconScale: PropTypes.number,
|
|
116
119
|
slotProps: PropTypes.object,
|
|
117
|
-
mode: PropTypes.oneOf(['simple', 'normal']),
|
|
120
|
+
mode: PropTypes.oneOf(['mini', 'simple', 'normal']),
|
|
118
121
|
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useBrowser } from '@arcblock/react-hooks';
|
|
2
|
+
import { useCreation, useRequest } from 'ahooks';
|
|
3
|
+
import isNil from 'lodash/isNil';
|
|
4
|
+
import { LOGIN_PROVIDER } from '@arcblock/ux/lib/Util/constant';
|
|
5
|
+
import { getBlockletData, getFederatedEnabled, getMaster } from '@arcblock/ux/lib/Util/federated';
|
|
6
|
+
|
|
7
|
+
const getAuthenticationConfig = async ({ sourceAppPid }) => {
|
|
8
|
+
const blocklet = globalThis?.blocklet;
|
|
9
|
+
if (!blocklet) {
|
|
10
|
+
return {
|
|
11
|
+
[LOGIN_PROVIDER.WALLET]: {
|
|
12
|
+
order: 0,
|
|
13
|
+
enabled: true,
|
|
14
|
+
showQrcode: true,
|
|
15
|
+
},
|
|
16
|
+
[LOGIN_PROVIDER.PASSKEY]: {
|
|
17
|
+
order: 1,
|
|
18
|
+
enabled: true,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const federatedEnabled = getFederatedEnabled(blocklet);
|
|
23
|
+
const master = getMaster(blocklet);
|
|
24
|
+
if (federatedEnabled && master?.appPid && sourceAppPid === master?.appPid) {
|
|
25
|
+
const masterBlocklet = await getBlockletData(master.appUrl);
|
|
26
|
+
return masterBlocklet?.settings?.authentication || {};
|
|
27
|
+
}
|
|
28
|
+
return blocklet?.settings?.authentication || {};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const getLoginProviderList = async ({ sourceAppPid } = {}) => {
|
|
32
|
+
const authenticationConfig = await getAuthenticationConfig({ sourceAppPid });
|
|
33
|
+
const loginProviderList = Object.entries(authenticationConfig)
|
|
34
|
+
.map(([key, value]) => {
|
|
35
|
+
return { ...value, provider: key };
|
|
36
|
+
})
|
|
37
|
+
.filter((item) => item.enabled)
|
|
38
|
+
.sort((a, b) => {
|
|
39
|
+
if (!isNil(a?.order) && !isNil(b?.order)) {
|
|
40
|
+
return a.order - b.order;
|
|
41
|
+
}
|
|
42
|
+
if (!isNil(a?.order)) {
|
|
43
|
+
return -1;
|
|
44
|
+
}
|
|
45
|
+
return 1;
|
|
46
|
+
});
|
|
47
|
+
return loginProviderList;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default function useProviderList({
|
|
51
|
+
allowWallet = true,
|
|
52
|
+
passkeyBehavior = 'none',
|
|
53
|
+
action,
|
|
54
|
+
sourceAppPid,
|
|
55
|
+
mode = 'dialog',
|
|
56
|
+
blocklet = globalThis.blocklet,
|
|
57
|
+
isSmallView = false,
|
|
58
|
+
}) {
|
|
59
|
+
const actionList = ['login', 'invite', 'connect-to-did-space', 'connect-to-did-domain', 'destroy-self'];
|
|
60
|
+
const browser = useBrowser();
|
|
61
|
+
|
|
62
|
+
const { data: loginProviderList = [] } = useRequest(
|
|
63
|
+
async () => {
|
|
64
|
+
const data = await getLoginProviderList({ sourceAppPid });
|
|
65
|
+
return data;
|
|
66
|
+
},
|
|
67
|
+
{ refreshDeps: [sourceAppPid] }
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const showedLoginProviderList = useCreation(() => {
|
|
71
|
+
const result = loginProviderList.filter((item) => {
|
|
72
|
+
if (item.name === LOGIN_PROVIDER.WALLET) {
|
|
73
|
+
return allowWallet;
|
|
74
|
+
}
|
|
75
|
+
if (item.name === LOGIN_PROVIDER.PASSKEY) {
|
|
76
|
+
return passkeyBehavior !== 'none' && !browser.wallet && !browser.arcSphere;
|
|
77
|
+
}
|
|
78
|
+
if (item.name === LOGIN_PROVIDER.EMAIL) {
|
|
79
|
+
return blocklet?.settings?.notification?.email?.enabled ?? false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (item.type === 'oauth') {
|
|
83
|
+
if (!actionList.includes(action)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
});
|
|
89
|
+
return result;
|
|
90
|
+
}, [loginProviderList]);
|
|
91
|
+
|
|
92
|
+
const hideQRCode = useCreation(() => {
|
|
93
|
+
const isMobileView = mode === 'drawer' || (isSmallView && browser.mobile.any);
|
|
94
|
+
if (!globalThis?.blocklet) {
|
|
95
|
+
if (!isMobileView) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const walletProvider = showedLoginProviderList.find((item) => item.provider === LOGIN_PROVIDER.WALLET);
|
|
102
|
+
if (walletProvider?.showQrcode !== true) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (isMobileView) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}, [mode, isSmallView, showedLoginProviderList]);
|
|
110
|
+
|
|
111
|
+
const hideChooseList = useCreation(() => {
|
|
112
|
+
return false;
|
|
113
|
+
// NOTICE: 先保留这部分逻辑,后续根据需求调整
|
|
114
|
+
// if (showOAuthLogin || showPasskeyLogin) {
|
|
115
|
+
// return false;
|
|
116
|
+
// }
|
|
117
|
+
// return true;
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const result = {
|
|
121
|
+
hideChooseList,
|
|
122
|
+
hideQRCode,
|
|
123
|
+
providerList: showedLoginProviderList,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
return result;
|
|
127
|
+
}
|
package/src/Connect/index.jsx
CHANGED
|
@@ -36,7 +36,7 @@ import useSecurity from './hooks/security';
|
|
|
36
36
|
|
|
37
37
|
import { SessionContext } from '../Session/context';
|
|
38
38
|
import { StateProvider, useStateContext } from './contexts/state';
|
|
39
|
-
import
|
|
39
|
+
import ConnectProviderList from './components/login-item/connect-provider-list';
|
|
40
40
|
import AutoHeight from './components/auto-height';
|
|
41
41
|
import useToken from './hooks/token';
|
|
42
42
|
import { useOAuth } from '../OAuth';
|
|
@@ -46,7 +46,7 @@ import { API_DID_PREFIX, DEFAULT_TIMEOUT, BUSY_STATUS, CHECK_STATUS_INTERVAL } f
|
|
|
46
46
|
import { getWebWalletUrl } from '../utils';
|
|
47
47
|
import DIDConnectTitle from './components/did-connect-title';
|
|
48
48
|
import DownloadTips from './components/download-tips';
|
|
49
|
-
import
|
|
49
|
+
import useProviderList from './hooks/provider-list';
|
|
50
50
|
import useAuthUrl from './hooks/auth-url';
|
|
51
51
|
import { getWalletDid } from '../User/use-did';
|
|
52
52
|
|
|
@@ -213,19 +213,9 @@ function Connect({
|
|
|
213
213
|
return LOGIN_PROVIDER_NAME[connectState.chooseMethod] || 'DID Wallet';
|
|
214
214
|
}, [connectState.chooseMethod]);
|
|
215
215
|
|
|
216
|
-
const {
|
|
217
|
-
showMobileLogin,
|
|
218
|
-
hideChooseList,
|
|
219
|
-
oauthProviderList,
|
|
220
|
-
showOAuthLogin,
|
|
221
|
-
showPasskeyLogin,
|
|
222
|
-
showWebLogin,
|
|
223
|
-
showEmailLogin,
|
|
224
|
-
hideQRCode,
|
|
225
|
-
} = useMethodList({
|
|
216
|
+
const { providerList, hideQRCode, hideChooseList } = useProviderList({
|
|
226
217
|
action: state.action,
|
|
227
218
|
sourceAppPid: connectState?.sourceAppPid,
|
|
228
|
-
enabledConnectTypes,
|
|
229
219
|
allowWallet,
|
|
230
220
|
passkeyBehavior,
|
|
231
221
|
webWalletUrl,
|
|
@@ -301,7 +291,7 @@ function Connect({
|
|
|
301
291
|
|
|
302
292
|
// 防止二维码抖动,使用 useCreation 进行缓存
|
|
303
293
|
const qrcode = useCreation(() => {
|
|
304
|
-
const backgroundColor = theme.mode === 'dark' ? theme.palette.grey[
|
|
294
|
+
const backgroundColor = theme.mode === 'dark' ? theme.palette.grey[600] : 'white';
|
|
305
295
|
return (
|
|
306
296
|
<Box
|
|
307
297
|
sx={{
|
|
@@ -437,7 +427,7 @@ function Connect({
|
|
|
437
427
|
}}>
|
|
438
428
|
{/* eslint-disable-next-line no-nested-ternary */}
|
|
439
429
|
{content}
|
|
440
|
-
<
|
|
430
|
+
<ConnectProviderList
|
|
441
431
|
slotProps={{
|
|
442
432
|
root: {
|
|
443
433
|
sx: [showStatus ? { display: 'none' } : {}],
|
|
@@ -453,18 +443,13 @@ function Connect({
|
|
|
453
443
|
webWalletUrl={webWalletUrl}
|
|
454
444
|
extraContent={extraContent}
|
|
455
445
|
enabledConnectTypes={enabledConnectTypes}
|
|
456
|
-
|
|
457
|
-
showMobileLogin={showMobileLogin && hideQRCode}
|
|
458
|
-
showOAuthLogin={showOAuthLogin}
|
|
459
|
-
showPasskeyLogin={showPasskeyLogin}
|
|
460
|
-
showWebLogin={showWebLogin}
|
|
461
|
-
showEmailLogin={showEmailLogin}
|
|
462
|
-
oauthProviderList={oauthProviderList}
|
|
446
|
+
onReset={handleReset}
|
|
463
447
|
disableSwitchApp={disableSwitchApp}
|
|
464
448
|
forceUpdate={forceUpdate}
|
|
465
449
|
magicToken={magicToken}
|
|
466
450
|
baseUrl={baseUrl}
|
|
467
451
|
customItems={customItems}
|
|
452
|
+
providerList={providerList}
|
|
468
453
|
/>
|
|
469
454
|
</Box>
|
|
470
455
|
</Box>
|
|
@@ -494,11 +479,7 @@ function Connect({
|
|
|
494
479
|
maxWidth: '100%',
|
|
495
480
|
width:
|
|
496
481
|
// eslint-disable-next-line no-nested-ternary
|
|
497
|
-
mode === 'drawer'
|
|
498
|
-
? '100%'
|
|
499
|
-
: hideQRCode || showStatus || hideChooseList
|
|
500
|
-
? DID_CONNECT_SMALL_WIDTH
|
|
501
|
-
: DID_CONNECT_MEDIUM_WIDTH,
|
|
482
|
+
mode === 'drawer' ? '100%' : hideQRCode || showStatus ? DID_CONNECT_SMALL_WIDTH : DID_CONNECT_MEDIUM_WIDTH,
|
|
502
483
|
transition: 'width 0.2s ease-in-out',
|
|
503
484
|
margin: 'auto',
|
|
504
485
|
p: isSmallView ? 2 : 3,
|