@arcblock/ux 2.7.14 → 2.7.16

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 (55) hide show
  1. package/es/Dialog/confirm.js +10 -8
  2. package/es/Img/index.js +7 -7
  3. package/es/Locale/selector.js +9 -9
  4. package/es/SessionManager/account-item.js +132 -0
  5. package/es/SessionManager/add-account-item.js +118 -0
  6. package/es/SessionManager/federated-login-detecter.js +15 -9
  7. package/es/SessionManager/index.js +122 -224
  8. package/es/SessionManager/manage-accounts.js +143 -0
  9. package/es/SessionManager/manage-blocklet.js +62 -0
  10. package/es/SessionManager/menu-accordion.js +95 -0
  11. package/es/SessionManager/translation.js +52 -0
  12. package/es/SessionManager/use-accounts.js +19 -0
  13. package/es/SessionManager/user-info.js +139 -0
  14. package/es/SessionManager/user-popper.js +13 -37
  15. package/es/SessionManager/utils.js +2 -0
  16. package/es/Util/index.js +7 -0
  17. package/lib/Dialog/confirm.js +9 -7
  18. package/lib/Img/index.js +7 -7
  19. package/lib/Locale/selector.js +16 -17
  20. package/lib/SessionManager/account-item.js +140 -0
  21. package/lib/SessionManager/add-account-item.js +126 -0
  22. package/lib/SessionManager/federated-login-detecter.js +15 -9
  23. package/lib/SessionManager/index.js +125 -237
  24. package/lib/SessionManager/manage-accounts.js +155 -0
  25. package/lib/SessionManager/manage-blocklet.js +78 -0
  26. package/lib/SessionManager/menu-accordion.js +103 -0
  27. package/lib/SessionManager/translation.js +59 -0
  28. package/lib/SessionManager/use-accounts.js +25 -0
  29. package/lib/SessionManager/user-info.js +155 -0
  30. package/lib/SessionManager/user-popper.js +8 -3
  31. package/lib/SessionManager/utils.js +16 -0
  32. package/lib/Util/index.js +11 -2
  33. package/package.json +8 -5
  34. package/src/Dialog/confirm.js +9 -6
  35. package/src/Img/index.js +5 -5
  36. package/src/Locale/{selector.js → selector.jsx} +9 -10
  37. package/src/SessionManager/account-item.jsx +111 -0
  38. package/src/SessionManager/add-account-item.jsx +115 -0
  39. package/src/SessionManager/federated-login-detecter.jsx +14 -12
  40. package/src/SessionManager/index.jsx +135 -214
  41. package/src/SessionManager/manage-accounts.jsx +143 -0
  42. package/src/SessionManager/manage-blocklet.jsx +64 -0
  43. package/src/SessionManager/menu-accordion.jsx +87 -0
  44. package/src/SessionManager/translation.js +52 -0
  45. package/src/SessionManager/use-accounts.js +18 -0
  46. package/src/SessionManager/user-info.jsx +116 -0
  47. package/src/SessionManager/user-popper.jsx +9 -37
  48. package/src/SessionManager/utils.js +3 -0
  49. package/src/Util/index.js +8 -0
  50. /package/src/Avatar/{did-motif.js → did-motif.jsx} +0 -0
  51. /package/src/Avatar/{index.js → index.jsx} +0 -0
  52. /package/src/Header/{auto-hidden.js → auto-hidden.jsx} +0 -0
  53. /package/src/Header/{header.js → header.jsx} +0 -0
  54. /package/src/Header/{responsive-header.js → responsive-header.jsx} +0 -0
  55. /package/src/Locale/{context.js → context.jsx} +0 -0
@@ -1,57 +1,25 @@
1
- /* eslint-disable react/no-array-index-key */
1
+ /* eslint-disable react/prop-types */
2
2
  /* eslint-disable react/jsx-no-bind */
3
- import { useMemo, useRef, useState } from 'react';
3
+ import { useEffect, useMemo, useRef, useState } from 'react';
4
4
  import PropTypes from 'prop-types';
5
- import { Box, IconButton, MenuList, MenuItem, SvgIcon, Button, Chip, Link, CircularProgress } from '@mui/material';
6
- import SwitchProfileIcon from '@mui/icons-material/PersonOutline';
7
- import BindWalletIcon from '@mui/icons-material/Link';
8
- import SwitchPassportIcon from '@mui/icons-material/VpnKeyOutlined';
9
- import ConnectWithoutContactIcon from '@mui/icons-material/ConnectWithoutContact';
10
- import ShieldCheck from 'mdi-material-ui/ShieldCheck';
5
+ import { Box, IconButton, MenuList, MenuItem, SvgIcon, Button, CircularProgress, Divider } from '@mui/material';
11
6
  import AccountIcon from '@arcblock/icons/lib/Account';
12
- import OpenInIcon from '@arcblock/icons/lib/OpenIn';
13
7
  import DisconnectIcon from '@arcblock/icons/lib/Disconnect';
14
- import SwitchDidIcon from '@arcblock/icons/lib/Switch';
15
- import useBrowser from '@arcblock/react-hooks/lib/useBrowser';
16
- import isEmpty from 'lodash/isEmpty';
17
8
  import noop from 'lodash/noop';
9
+ import isEmpty from 'lodash/isEmpty';
10
+ import { useLatest, useMemoizedFn } from 'ahooks';
18
11
 
19
12
  import DidAvatar from '../Avatar';
20
- import DidAddress from '../Address';
21
- import { getUserAvatar } from '../Util';
13
+ import { getUserAvatar, sleep } from '../Util';
22
14
  import FederatedLoginDetecter from './federated-login-detecter';
23
15
  import UserPopper from './user-popper';
24
-
25
- const translations = {
26
- en: {
27
- switchDid: 'Switch DID',
28
- switchProfile: 'Switch Profile',
29
- switchPassport: 'Switch Passport',
30
- disconnect: 'Disconnect',
31
- connect: 'Connect',
32
- openInWallet: 'Open DID Wallet',
33
- alreadyBindOAuth: 'Already bind Auth0',
34
- bind: 'Bind ',
35
- thirdParty: 'Third Party Login',
36
- connectedWith: 'Connected with',
37
- },
38
- zh: {
39
- switchDid: '切换账户',
40
- switchProfile: '切换用户信息',
41
- switchPassport: '切换通行证',
42
- disconnect: '退出',
43
- connect: '登录',
44
- openInWallet: '打开 DID 钱包',
45
- // NOTE: 目前只有 Auth0,展示出具体的第三方名字会更好
46
- alreadyBindOAuth: '已绑定 Auth0 账号',
47
- bind: '绑定',
48
- thirdParty: '第三方登录',
49
- connectedWith: '连接至',
50
- },
51
- };
52
-
53
- const getConnectedAccounts = (user) => user?.connectedAccounts || user?.extraConfigs?.connectedAccounts || [];
54
- const getSourceProvider = (user) => user?.sourceProvider || user?.extraConfigs?.sourceProvider || 'wallet';
16
+ import UserInfo from './user-info';
17
+ import { translate } from '../Locale/util';
18
+ import ManageAccounts from './manage-accounts';
19
+ import ManageBlocklet from './manage-blocklet';
20
+ import { translations } from './translation';
21
+ import { getConnectedAccounts, getSourceProvider } from './utils';
22
+ import useAccounts from './use-accounts';
55
23
 
56
24
  function SessionManager({
57
25
  session,
@@ -74,25 +42,26 @@ function SessionManager({
74
42
  size,
75
43
  ...rest
76
44
  }) {
77
- const translation = translations[locale] || translations.en;
45
+ const latestSession = useLatest(session);
46
+ const { connectAccount } = useAccounts();
47
+ const t = useMemoizedFn((key, data = {}) => {
48
+ return translate(translations, key, locale, 'en', data);
49
+ });
78
50
  const userAnchorRef = useRef(null);
79
- // eslint-disable-next-line react/prop-types
80
- const { logoutOAuth, bindOAuth, configs: oauthConfigs, switchOAuthPassport } = session.useOAuth();
51
+ const { logoutOAuth, switchOAuthPassport } = session.useOAuth();
81
52
  const [userOpen, setUserOpen] = useState(false);
82
53
 
83
54
  // base64 img maybe have some blank char, need encodeURIComponent to transform it
84
55
  const avatar = getUserAvatar(session.user?.avatar?.replace(/\s/g, encodeURIComponent(' ')));
85
- const currentRole = useMemo(
86
- () => session.user?.passports?.find((item) => item.name === session.user.role),
87
- [session.user]
88
- );
89
- const browser = useBrowser();
90
- // eslint-disable-next-line react/prop-types
91
- const { walletDid } = session.useDid({ session });
92
56
 
93
57
  const isRawWalletAccount = getSourceProvider(session.user) === 'wallet';
94
58
  const connectedAccounts = getConnectedAccounts(session.user);
95
- const federatedAccount = connectedAccounts.find((item) => item.provider === 'federated');
59
+
60
+ const isFirstLoading = useMemo(() => {
61
+ return session?.initialized === false && session?.loading === true;
62
+ }, [session?.initialized, session?.loading]);
63
+
64
+ // const federatedAccount = connectedAccounts.find((item) => item.provider === 'federated');
96
65
  let hasBindWallet = false;
97
66
  let hasBindAccount = false;
98
67
  if (isRawWalletAccount) {
@@ -105,19 +74,59 @@ function SessionManager({
105
74
  hasBindWallet = true;
106
75
  }
107
76
 
108
- const oauthConfigList = Object.entries(oauthConfigs)
109
- .map(([key, value]) => {
110
- return { ...value, provider: key };
111
- })
112
- .filter((item) => item.enabled);
113
-
114
- const isFirstLoading = useMemo(() => {
115
- // eslint-disable-next-line react/prop-types
116
- return session?.initialized === false && session?.loading === true;
117
- // eslint-disable-next-line react/prop-types
118
- }, [session?.initialized, session?.loading]);
77
+ const _connectAccount = useMemoizedFn(async () => {
78
+ // HACK: 强制等待组件渲染一轮,以拿到最新的 session 值
79
+ await sleep();
80
+ if (!latestSession.current?.user) {
81
+ return;
82
+ }
83
+ if (typeof window === 'undefined') {
84
+ return;
85
+ }
86
+ const blocklet = window?.blocklet;
87
+ if (!blocklet) {
88
+ return;
89
+ }
90
+ const currentApp = {
91
+ appId: blocklet.appId,
92
+ appPid: blocklet.appPid,
93
+ appName: blocklet.appName,
94
+ appDescription: blocklet.appDescription,
95
+ appLogo: blocklet.appLogo,
96
+ appUrl: blocklet.appUrl,
97
+ version: blocklet.version,
98
+ provider: 'wallet',
99
+ };
100
+ const federatedMaster = blocklet.settings?.federated?.master;
101
+ const masterApp = isEmpty(federatedMaster)
102
+ ? null
103
+ : {
104
+ appId: federatedMaster.appId,
105
+ appName: federatedMaster.appName,
106
+ appDescription: federatedMaster.appDescription,
107
+ appLogo: federatedMaster.appLogo,
108
+ appPid: federatedMaster.appPid,
109
+ appUrl: federatedMaster.appUrl,
110
+ version: federatedMaster.version,
111
+ provider: 'federated',
112
+ };
113
+ const loginAccount = {
114
+ did: latestSession.current.user.did,
115
+ avatar: latestSession.current.user.avatar,
116
+ provider: latestSession.current.provider,
117
+ ...(latestSession.current.provider === 'federated' ? masterApp : currentApp),
118
+ };
119
+ connectAccount(loginAccount);
120
+ });
119
121
 
120
- const masterSiteInfo = window.blocklet?.settings?.federated?.master;
122
+ // HACK: 用于处理 统一登录 的自动登录情况,添加一个已连接的账号
123
+ // 同时可用于以前的站点,会自动生成一个已连接的账号
124
+ useEffect(() => {
125
+ if (session.user) {
126
+ _connectAccount();
127
+ }
128
+ // eslint-disable-next-line react-hooks/exhaustive-deps
129
+ }, [session.user]);
121
130
 
122
131
  if (!session.user) {
123
132
  return (
@@ -133,7 +142,7 @@ function SessionManager({
133
142
  {...rest}
134
143
  data-cy="sessionManager-login">
135
144
  {isFirstLoading ? <CircularProgress /> : <AccountIcon />}
136
- <span style={{ lineHeight: '25px' }}>{translation.connect}</span>
145
+ <span style={{ lineHeight: '25px' }}>{t('connect')}</span>
137
146
  </Button>
138
147
  ) : (
139
148
  <IconButton
@@ -163,16 +172,23 @@ function SessionManager({
163
172
  setUserOpen((prevOpen) => !prevOpen);
164
173
  }
165
174
 
175
+ function close() {
176
+ setUserOpen(false);
177
+ }
178
+
166
179
  function onCloseUser(e) {
167
180
  if (userAnchorRef.current && userAnchorRef.current.contains(e.target)) {
168
181
  return;
169
182
  }
170
- setUserOpen(false);
183
+ close();
171
184
  }
172
185
 
173
186
  function _onLogin() {
174
187
  if (!isFirstLoading) {
175
- session.login(onLogin);
188
+ session.login((...args) => {
189
+ _connectAccount();
190
+ onLogin(...args);
191
+ });
176
192
  }
177
193
  }
178
194
  function _onLogout() {
@@ -185,48 +201,35 @@ function SessionManager({
185
201
  console.error(err);
186
202
  })
187
203
  .finally(() => {
188
- setUserOpen(false);
204
+ close();
189
205
  });
190
206
  });
191
207
  }
192
- function _onSwitchDid() {
193
- session.switchDid((...args) => {
194
- setUserOpen(false);
195
- onSwitchDid(...args);
196
- });
197
- }
208
+ /**
209
+ * @name 切换账户
210
+ * @description 该功能仅在登录后才能使用,目前仅用于切换普通登录和统一登录的账户,所以会增加一些与统一登录相关的逻辑
211
+ */
212
+
198
213
  function _onSwitchProfile() {
199
214
  session.switchProfile((...args) => {
200
- setUserOpen(false);
215
+ close();
201
216
  onSwitchProfile(...args);
202
217
  });
203
218
  }
219
+
204
220
  function _onSwitchPassport() {
205
221
  const { user, provider } = session;
206
222
  if (!isRawWalletAccount && provider !== 'federated') {
207
223
  switchOAuthPassport(user);
208
224
  } else {
209
- setUserOpen(false);
225
+ close();
210
226
  session.switchPassport((...args) => {
211
- setUserOpen(false);
227
+ close();
212
228
  onSwitchPassport(...args);
213
229
  });
214
230
  }
215
231
  }
216
232
 
217
- function _onBindWallet() {
218
- setUserOpen(false);
219
- // FIXME: @zhanghan 暂时切换回 isRawWalletAccount 的方式来判断,在 did-connect 改版时,简化这里的关系判断
220
- if (!isRawWalletAccount) {
221
- session.bindWallet((...args) => {
222
- setUserOpen(false);
223
- onBindWallet(...args);
224
- });
225
- } else {
226
- bindOAuth();
227
- }
228
- }
229
-
230
233
  return (
231
234
  <>
232
235
  <IconButton
@@ -243,103 +246,43 @@ function SessionManager({
243
246
 
244
247
  <UserPopper open={userOpen} onClose={onCloseUser} anchorEl={userAnchorRef.current} dark={dark}>
245
248
  <MenuList sx={{ p: 0 }}>
246
- <div className="session-manager-user">
247
- <div className="session-manager-user-name" role="button" aria-label="User info panel">
248
- <span>{session.user.fullName}</span>
249
- {!!showRole && (currentRole?.title || session.user?.role.toUpperCase()) && (
250
- <Chip
251
- label={currentRole?.title || session.user?.role.toUpperCase()}
252
- size="small"
253
- variant="outlined"
254
- sx={{ height: 'auto', marginRight: 0 }}
255
- icon={<SvgIcon component={ShieldCheck} size="small" />}
256
- />
257
- )}
258
- </div>
259
- <div className="session-manager-id-list" aria-label="User DID list">
260
- {walletDid && (
261
- <div className="session-manager-id-item">
262
- <DidAddress responsive={false}>{walletDid}</DidAddress>
263
- </div>
264
- )}
265
- {federatedAccount && (
266
- <div className="session-manager-id-item">
267
- <DidAddress responsive={false}>{federatedAccount.did}</DidAddress>
268
- </div>
269
- )}
270
- {session?.user?.email && (
271
- <div className="session-manager-id-item">
272
- <DidAddress responsive={false}>{session.user.email}</DidAddress>
273
- </div>
274
- )}
275
- </div>
276
- </div>
277
- {federatedAccount && !isEmpty(masterSiteInfo) && (
278
- <MenuItem className="session-manager-menu-item" data-cy="sessionManager-connectWithFederated">
279
- <Box overflow="hidden" textOverflow="ellipsis">
280
- <SvgIcon component={ConnectWithoutContactIcon} className="session-manager-menu-icon" />
281
- {translation.connectedWith}
282
- <Link
283
- ml={1}
284
- href={masterSiteInfo.appUrl}
285
- underline="hover"
286
- target="_blank"
287
- title={masterSiteInfo.appName}
288
- aria-label="Open federated master site url">
289
- {masterSiteInfo.appName}
290
- </Link>
291
- </Box>
292
- </MenuItem>
293
- )}
294
- {Array.isArray(menu) &&
295
- menu.map((menuItem, index) => {
296
- const { svgIcon, ...menuProps } = menuItem;
297
- return (
298
- <MenuItem
299
- key={index}
300
- className="session-manager-menu-item"
301
- {...{
302
- ...menuProps,
303
- icon: undefined,
304
- label: undefined,
305
- }}>
306
- {svgIcon
307
- ? svgIcon && <SvgIcon component={svgIcon} className="session-manager-menu-icon" />
308
- : menuItem.icon}
309
- {menuItem.label}
310
- </MenuItem>
311
- );
312
- })}
313
- {menuRender({
314
- classes: {
315
- menuItem: 'session-manager-menu-item',
316
- menuIcon: 'session-manager-menu-icon',
317
- },
318
- })}
319
- {!browser.wallet && (
320
- <MenuItem
321
- component="a"
322
- className="session-manager-menu-item"
323
- data-cy="sessionManager-openInWallet"
324
- href="https://www.abtwallet.io/"
325
- aria-label={translation.openInWallet}
326
- target="_blank">
327
- <SvgIcon component={OpenInIcon} className="session-manager-menu-icon" />
328
- {translation.openInWallet}
329
- </MenuItem>
330
- )}
331
- {!!switchDid && (
332
- <MenuItem
333
- className="session-manager-menu-item"
334
- onClick={_onSwitchDid}
335
- aria-label={translation.switchDid}
336
- data-cy="sessionManager-switch-trigger">
337
- <SvgIcon component={SwitchDidIcon} className="session-manager-menu-icon" />
338
- {translation.switchDid}
339
- </MenuItem>
340
- )}
249
+ <UserInfo
250
+ session={session}
251
+ size={size}
252
+ locale={locale}
253
+ onEditUser={() => {
254
+ close();
255
+ _onSwitchProfile();
256
+ }}
257
+ onSwitchPassport={() => {
258
+ close();
259
+ _onSwitchPassport();
260
+ }}
261
+ close={close}
262
+ switchProfile={switchProfile}
263
+ hasBindWallet={hasBindWallet}
264
+ />
265
+
266
+ <Divider />
267
+
268
+ <ManageAccounts
269
+ session={session}
270
+ locale={locale}
271
+ onBindWallet={onBindWallet}
272
+ onSwitchDid={onSwitchDid}
273
+ connectAccount={_connectAccount}
274
+ close={close}
275
+ hasBindAccount={hasBindAccount}
276
+ />
277
+
278
+ <Divider />
279
+
280
+ <ManageBlocklet menu={menu} menuRender={menuRender} locale={locale} />
281
+
282
+ <Divider />
283
+
341
284
  {/* NOTE: federated 登录方式不允许切换 profile */}
342
- {!!switchProfile && hasBindWallet && session.provider !== 'federated' && (
285
+ {/* {!!switchProfile && hasBindWallet && session.provider !== 'federated' && (
343
286
  <MenuItem
344
287
  className="session-manager-menu-item"
345
288
  onClick={_onSwitchProfile}
@@ -348,29 +291,7 @@ function SessionManager({
348
291
  <SvgIcon component={SwitchProfileIcon} className="session-manager-menu-icon" />
349
292
  {translation.switchProfile}
350
293
  </MenuItem>
351
- )}
352
- {!!switchPassport && (
353
- <MenuItem
354
- className="session-manager-menu-item"
355
- onClick={_onSwitchPassport}
356
- aria-label={translation.switchPassport}
357
- data-cy="sessionManager-switch-passport-trigger">
358
- <SvgIcon component={SwitchPassportIcon} className="session-manager-menu-icon" />
359
- {translation.switchPassport}
360
- </MenuItem>
361
- )}
362
- {oauthConfigList.length > 0 && !hasBindAccount && session.provider !== 'federated' && (
363
- <MenuItem
364
- className="session-manager-menu-item"
365
- onClick={_onBindWallet}
366
- aria-label={
367
- !isRawWalletAccount ? `${translation.bind}DID Wallet` : `${translation.bind}${translation.thirdParty}`
368
- }
369
- data-cy="sessionManager-bind-trigger">
370
- <SvgIcon component={BindWalletIcon} className="session-manager-menu-icon" />
371
- {!isRawWalletAccount ? `${translation.bind}DID Wallet` : `${translation.bind}${translation.thirdParty}`}
372
- </MenuItem>
373
- )}
294
+ )} */}
374
295
 
375
296
  <MenuItem
376
297
  className="session-manager-menu-item"
@@ -379,7 +300,7 @@ function SessionManager({
379
300
  aria-label="Logout account"
380
301
  data-cy="sessionManager-logout-trigger">
381
302
  <SvgIcon component={DisconnectIcon} className="session-manager-menu-icon" />
382
- {translation.disconnect}
303
+ {t('disconnect')}
383
304
  </MenuItem>
384
305
  </MenuList>
385
306
  </UserPopper>
@@ -0,0 +1,143 @@
1
+ /* eslint-disable react/prop-types */
2
+ /* eslint-disable react/jsx-no-bind */
3
+ import { useMemoizedFn, useUpdate } from 'ahooks';
4
+ import { MenuItem } from '@mui/material';
5
+ import { Icon } from '@iconify/react';
6
+ import AccountIcon from '@arcblock/icons/lib/Account';
7
+ import LinkIcon from '@iconify-icons/mdi/link';
8
+
9
+ import AccountItem from './account-item';
10
+ import MenuAccordion from './menu-accordion';
11
+ import { translations } from './translation';
12
+ import { translate } from '../Locale/util';
13
+ import { useConfirm } from '../Dialog/confirm';
14
+ import { getSourceProvider } from './utils';
15
+ import AddAccountItem from './add-account-item';
16
+ import useAccounts from './use-accounts';
17
+
18
+ export default function ManageAccounts({
19
+ session,
20
+ locale,
21
+ onBindWallet,
22
+ onSwitchDid,
23
+ connectAccount,
24
+ close,
25
+ hasBindAccount,
26
+ }) {
27
+ const { bindOAuth, configs: oauthConfigs } = session.useOAuth();
28
+ const t = useMemoizedFn((key, data = {}) => {
29
+ return translate(translations, key, locale, 'en', data);
30
+ });
31
+
32
+ const { confirmApi, confirmHolder } = useConfirm();
33
+ const { accounts, setAccounts } = useAccounts();
34
+ const update = useUpdate();
35
+
36
+ const onChoose = useMemoizedFn((account, { active }) => {
37
+ if (active) {
38
+ return;
39
+ }
40
+ close();
41
+ session.switchDid(
42
+ (...args) => {
43
+ connectAccount();
44
+ onSwitchDid(...args);
45
+ },
46
+ {
47
+ provider: account.provider,
48
+ providerMode: 'paramsFirst',
49
+ }
50
+ );
51
+ });
52
+
53
+ const oauthConfigList = Object.entries(oauthConfigs)
54
+ .map(([key, value]) => {
55
+ return { ...value, provider: key };
56
+ })
57
+ .filter((item) => item.enabled);
58
+
59
+ const isRawWalletAccount = getSourceProvider(session.user) === 'wallet';
60
+
61
+ const onDelete = useMemoizedFn((account, { active }) => {
62
+ if (active) {
63
+ return;
64
+ }
65
+ confirmApi.open({
66
+ title: t('deleteAccountTitle'),
67
+ content: t('deleteAccountContent'),
68
+ confirmButtonText: t('confirm'),
69
+ cancelButtonText: t('cancel'),
70
+ onConfirm(done) {
71
+ const findIndex = accounts.findIndex((item) => item.did === account.did);
72
+ if (findIndex >= 0) {
73
+ accounts.splice(findIndex, 1);
74
+ }
75
+ setAccounts(accounts);
76
+ update();
77
+ done();
78
+ },
79
+ });
80
+ });
81
+
82
+ const onAdd = useMemoizedFn((app) => {
83
+ close();
84
+ session.switchDid(
85
+ (...args) => {
86
+ connectAccount();
87
+ update();
88
+ onSwitchDid(...args);
89
+ },
90
+ {
91
+ provider: app.provider,
92
+ providerMode: 'paramsFirst',
93
+ }
94
+ );
95
+ });
96
+
97
+ function _onBindWallet() {
98
+ close();
99
+ // FIXME: @zhanghan 暂时切换回 isRawWalletAccount 的方式来判断,在 did-connect 改版时,简化这里的关系判断
100
+ if (!isRawWalletAccount) {
101
+ session.bindWallet(onBindWallet);
102
+ } else {
103
+ bindOAuth();
104
+ }
105
+ }
106
+
107
+ return (
108
+ <>
109
+ <MenuAccordion
110
+ locale={locale}
111
+ title={
112
+ <>
113
+ <AccountIcon className="session-manager-menu-icon" style={{ width: 24, height: 24 }} />
114
+ {t('manageAccounts')}
115
+ </>
116
+ }>
117
+ {accounts.map((item) => (
118
+ <AccountItem
119
+ key={item.did}
120
+ account={item}
121
+ locale={locale}
122
+ active={session.user.did === item.did}
123
+ onDelete={onDelete}
124
+ onChoose={onChoose}
125
+ />
126
+ ))}
127
+ <AddAccountItem locale={locale} onAdd={onAdd} />
128
+
129
+ {oauthConfigList.length > 0 && !hasBindAccount && session.provider !== 'federated' && (
130
+ <MenuItem
131
+ className="session-manager-menu-item"
132
+ onClick={_onBindWallet}
133
+ aria-label={!isRawWalletAccount ? `${t('bind')}DID Wallet` : `${t('bind')}${t('thirdParty')}`}
134
+ data-cy="sessionManager-bind-trigger">
135
+ <Icon icon={LinkIcon} width={24} height={24} className="session-manager-menu-icon" />
136
+ {!isRawWalletAccount ? `${t('bind')}DID Wallet` : `${t('bind')}${t('thirdParty')}`}
137
+ </MenuItem>
138
+ )}
139
+ </MenuAccordion>
140
+ {confirmHolder}
141
+ </>
142
+ );
143
+ }
@@ -0,0 +1,64 @@
1
+ /* eslint-disable react/no-array-index-key */
2
+ import PropTypes from 'prop-types';
3
+ import { useMemoizedFn } from 'ahooks';
4
+ import { Icon } from '@iconify/react';
5
+ import AppsIcon from '@iconify-icons/mdi/apps';
6
+ import { MenuItem, SvgIcon } from '@mui/material';
7
+
8
+ import MenuAccordion from './menu-accordion';
9
+ import { translate } from '../Locale/util';
10
+ import { translations } from './translation';
11
+
12
+ export default function ManageBlocklet({ menu, menuRender, locale }) {
13
+ const t = useMemoizedFn((key, data = {}) => {
14
+ return translate(translations, key, locale, 'en', data);
15
+ });
16
+
17
+ return (
18
+ <MenuAccordion
19
+ locale={locale}
20
+ title={
21
+ <>
22
+ <Icon icon={AppsIcon} width={24} height={24} className="session-manager-menu-icon" />
23
+ {t('manageBlocklet')}
24
+ </>
25
+ }>
26
+ {Array.isArray(menu) &&
27
+ menu.map((menuItem, index) => {
28
+ const { svgIcon, ...menuProps } = menuItem;
29
+ return (
30
+ <MenuItem
31
+ key={index}
32
+ className="session-manager-menu-item"
33
+ {...{
34
+ ...menuProps,
35
+ icon: undefined,
36
+ label: undefined,
37
+ }}>
38
+ {svgIcon
39
+ ? svgIcon && <SvgIcon component={svgIcon} className="session-manager-menu-icon" />
40
+ : menuItem.icon}
41
+ {menuItem.label}
42
+ </MenuItem>
43
+ );
44
+ })}
45
+ {menuRender({
46
+ classes: {
47
+ menuItem: 'session-manager-menu-item',
48
+ menuIcon: 'session-manager-menu-icon',
49
+ },
50
+ })}
51
+ </MenuAccordion>
52
+ );
53
+ }
54
+
55
+ ManageBlocklet.propTypes = {
56
+ menu: PropTypes.array,
57
+ menuRender: PropTypes.func,
58
+ locale: PropTypes.string.isRequired,
59
+ };
60
+
61
+ ManageBlocklet.defaultProps = {
62
+ menu: [],
63
+ menuRender: () => {},
64
+ };