@arcblock/ux 2.5.58 → 2.5.60

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 (135) hide show
  1. package/lib/ActionButton/index.js +20 -39
  2. package/lib/ActivityIndicator/index.js +20 -41
  3. package/lib/Address/compact-text.js +16 -38
  4. package/lib/Address/did-address.js +22 -51
  5. package/lib/Address/index.js +6 -19
  6. package/lib/Address/responsive-did-address.js +20 -36
  7. package/lib/Alert/index.js +12 -34
  8. package/lib/AnimationWaiter/index.js +35 -59
  9. package/lib/Async/index.js +5 -19
  10. package/lib/Avatar/did-motif.js +12 -26
  11. package/lib/Avatar/etherscan-blockies.js +2 -20
  12. package/lib/Avatar/index.js +28 -54
  13. package/lib/Badge/index.js +12 -33
  14. package/lib/Blocklet/blocklet.js +20 -47
  15. package/lib/Blocklet/index.js +0 -6
  16. package/lib/Blocklet/utils.js +0 -6
  17. package/lib/BlockletNFT/index.js +37 -67
  18. package/lib/Button/index.js +0 -4
  19. package/lib/Button/wrap.js +19 -38
  20. package/lib/ButtonGroup/index.js +0 -5
  21. package/lib/CardSelector/index.js +9 -22
  22. package/lib/Center/index.js +2 -12
  23. package/lib/ClickToCopy/copy-button.js +9 -26
  24. package/lib/ClickToCopy/hook.js +0 -10
  25. package/lib/ClickToCopy/index.js +13 -41
  26. package/lib/CodeBlock/LightBox.js +1 -6
  27. package/lib/CodeBlock/index.js +12 -80
  28. package/lib/Colors/index.js +0 -2
  29. package/lib/Colors/themes/default.js +2 -3
  30. package/lib/ContactForm/index.js +5 -35
  31. package/lib/CookieConsent/index.js +10 -32
  32. package/lib/CountDown/index.js +10 -35
  33. package/lib/DID/index.js +8 -32
  34. package/lib/Datatable/CustomToolbar.js +9 -56
  35. package/lib/Datatable/DatatableContext.js +2 -5
  36. package/lib/Datatable/TableSearch.js +0 -21
  37. package/lib/Datatable/index.js +76 -152
  38. package/lib/Datatable/utils.js +14 -27
  39. package/lib/Dialog/confirm.js +18 -31
  40. package/lib/Dialog/dialog.js +24 -50
  41. package/lib/Dialog/index.js +0 -3
  42. package/lib/DidLogo/index.js +0 -7
  43. package/lib/DriftBot/index.js +1 -12
  44. package/lib/Earth/index.js +17 -58
  45. package/lib/Earth/util.js +0 -7
  46. package/lib/Empty/index.js +9 -23
  47. package/lib/ErrorBoundary/fallback.js +0 -14
  48. package/lib/ErrorBoundary/index.js +0 -2
  49. package/lib/Footer/index.js +1 -14
  50. package/lib/Header/auto-hidden.js +9 -23
  51. package/lib/Header/header.js +19 -38
  52. package/lib/Header/index.js +0 -3
  53. package/lib/Header/responsive-header.js +16 -40
  54. package/lib/Icon/image.js +12 -26
  55. package/lib/Icon/index.js +15 -34
  56. package/lib/Img/index.js +32 -54
  57. package/lib/InfoRow/index.js +11 -27
  58. package/lib/Layout/dashboard/external-link.js +13 -30
  59. package/lib/Layout/dashboard/full-page.js +8 -24
  60. package/lib/Layout/dashboard/index.js +28 -72
  61. package/lib/Layout/dashboard/sidebar.js +11 -31
  62. package/lib/Layout/dashboard-legacy/header.js +14 -36
  63. package/lib/Layout/dashboard-legacy/index.js +19 -44
  64. package/lib/Layout/dashboard-legacy/sidebar.js +10 -32
  65. package/lib/Layout/index.js +18 -52
  66. package/lib/Locale/browser-lang.js +6 -19
  67. package/lib/Locale/context.js +15 -51
  68. package/lib/Locale/languages.js +3 -7
  69. package/lib/Locale/selector.js +11 -44
  70. package/lib/Locale/util.js +0 -10
  71. package/lib/Logo/index.js +10 -33
  72. package/lib/Metric/index.js +1 -9
  73. package/lib/NFTDisplay/aspect-ratio-container.js +7 -20
  74. package/lib/NFTDisplay/broken.js +0 -8
  75. package/lib/NFTDisplay/index.js +63 -110
  76. package/lib/NFTDisplay/loading.js +0 -6
  77. package/lib/NFTDisplay/svg-embedder/img.js +9 -22
  78. package/lib/NFTDisplay/svg-embedder/inline-svg.js +9 -23
  79. package/lib/NavMenu/index.js +0 -2
  80. package/lib/NavMenu/nav-menu.js +40 -69
  81. package/lib/NavMenu/style.js +1 -5
  82. package/lib/PageScroller/index.js +9 -39
  83. package/lib/PageScroller/story/FifthComponent.js +0 -2
  84. package/lib/PageScroller/story/FirstComponent.js +0 -2
  85. package/lib/PageScroller/story/FourthComponent.js +0 -2
  86. package/lib/PageScroller/story/FullPage.js +1 -14
  87. package/lib/PageScroller/story/PageContain.js +1 -14
  88. package/lib/PageScroller/story/SecondComponent.js +0 -2
  89. package/lib/PageScroller/story/ThirdComponent.js +0 -2
  90. package/lib/PageScroller/usePrevValue.js +1 -2
  91. package/lib/PricingTable/PricingPlan.js +0 -12
  92. package/lib/PricingTable/index.js +0 -14
  93. package/lib/QRCode/index.js +14 -24
  94. package/lib/RelativeTime/index.js +12 -45
  95. package/lib/Result/common.js +43 -63
  96. package/lib/Result/index.js +6 -23
  97. package/lib/Result/result.js +9 -23
  98. package/lib/Screenshot/BaseScreenshot/index.js +9 -26
  99. package/lib/Screenshot/BaseScreenshot/shells/Macbook.js +9 -10
  100. package/lib/Screenshot/BaseScreenshot/shells/Phone.js +3 -8
  101. package/lib/Screenshot/index.js +32 -65
  102. package/lib/SessionManager/federated-login-detecter.js +171 -0
  103. package/lib/SessionManager/index.js +189 -271
  104. package/lib/SessionManager/user-popper.js +83 -0
  105. package/lib/Sparkline/index.js +40 -42
  106. package/lib/Spinner/index.js +12 -23
  107. package/lib/SplitButton/index.js +20 -48
  108. package/lib/Switch/index.js +7 -22
  109. package/lib/Tabs/index.js +8 -22
  110. package/lib/Tag/index.js +13 -33
  111. package/lib/TextCollapse/index.js +14 -31
  112. package/lib/Theme/index.js +3 -13
  113. package/lib/Theme/theme-provider.js +1 -10
  114. package/lib/Theme/theme.js +15 -27
  115. package/lib/Toast/index.js +13 -40
  116. package/lib/Util/deprecate.js +3 -12
  117. package/lib/Util/index.js +13 -74
  118. package/lib/Util/wallet.js +3 -10
  119. package/lib/Video/index.js +3 -19
  120. package/lib/Wallet/Action.js +9 -28
  121. package/lib/Wallet/Download.js +11 -36
  122. package/lib/Wallet/Open.js +0 -12
  123. package/lib/WebWalletSWKeeper/index.js +30 -55
  124. package/lib/WechatPrompt/index.js +0 -16
  125. package/lib/index.js +0 -38
  126. package/lib/withTheme/index.js +3 -15
  127. package/lib/withTracker/error_boundary.js +3 -14
  128. package/lib/withTracker/index.js +3 -24
  129. package/package.json +5 -5
  130. package/src/Blocklet/blocklet.js +1 -1
  131. package/src/BlockletNFT/index.js +1 -1
  132. package/src/NFTDisplay/index.js +4 -4
  133. package/src/SessionManager/federated-login-detecter.jsx +138 -0
  134. package/src/SessionManager/index.jsx +140 -233
  135. package/src/SessionManager/user-popper.jsx +121 -0
@@ -0,0 +1,138 @@
1
+ import PropTypes from 'prop-types';
2
+ import { useCallback, useState } from 'react';
3
+ import { Box, Chip, Divider, SvgIcon } from '@mui/material';
4
+ import ShieldCheck from 'mdi-material-ui/ShieldCheck';
5
+
6
+ import UserPopper from './user-popper';
7
+ import DidAvatar from '../Avatar';
8
+ import DidAddress from '../Address';
9
+ import Button from '../Button';
10
+ import Toast from '../Toast';
11
+
12
+ const translations = {
13
+ en: {
14
+ useToConnect({ master, member }) {
15
+ return (
16
+ <>
17
+ Use {master} account to connect {member}
18
+ </>
19
+ );
20
+ },
21
+ connect: 'Connect Account',
22
+ loginFederatedFailed: 'Login federated account failed',
23
+ },
24
+ zh: {
25
+ useToConnect({ master, member }) {
26
+ return (
27
+ <>
28
+ 使用 {master} 账号连接 {member}
29
+ </>
30
+ );
31
+ },
32
+ connect: '连接账号',
33
+ loginFederatedFailed: '登录统一登录账号失败',
34
+ },
35
+ };
36
+
37
+ export default function FederatedLoginDetecter({ session, anchorEl, dark, locale: _locale }) {
38
+ const [federatedLoginOpen, setFederatedLoginOpen] = useState(true);
39
+
40
+ const siteInfo = session.federatedMaster?.site;
41
+ const userInfo = session.federatedMaster?.user;
42
+
43
+ const localeList = Object.keys(translations);
44
+ const locale = localeList.includes(_locale) ? _locale : localeList[0];
45
+
46
+ const onLoginFederated = useCallback(() => {
47
+ session?.loginFederated(
48
+ (err) => {
49
+ if (err) {
50
+ Toast.error(err || translations[_locale].loginFederatedFailed);
51
+ } else {
52
+ setFederatedLoginOpen(false);
53
+ }
54
+ },
55
+ { mode: userInfo ? 'auto' : 'manual' }
56
+ );
57
+ }, [session, userInfo, _locale]);
58
+
59
+ return (
60
+ siteInfo && (
61
+ <UserPopper
62
+ open={federatedLoginOpen}
63
+ anchorEl={anchorEl}
64
+ dark={dark}
65
+ onClose={() => setFederatedLoginOpen(false)}>
66
+ <Box p={2}>
67
+ {siteInfo && (
68
+ <Box display="flex" alignItems="center">
69
+ <Box
70
+ component="img"
71
+ mr={2}
72
+ src={`${siteInfo.appUrl}${siteInfo.appLogo}`}
73
+ alt={siteInfo.appName}
74
+ width="30px"
75
+ height="30px"
76
+ />
77
+ <Box>
78
+ {translations[locale].useToConnect({
79
+ master: (
80
+ <Box
81
+ component="a"
82
+ href={siteInfo.appUrl}
83
+ target="_blank"
84
+ sx={{ textDecoration: 'none', fontWeight: 'bold', color: 'primary.main', fontSize: '1.2em' }}>
85
+ {siteInfo.appName}
86
+ </Box>
87
+ ),
88
+ member: window.blocklet.appName,
89
+ })}
90
+ </Box>
91
+ </Box>
92
+ )}
93
+ {siteInfo && userInfo && <Divider style={{ margin: '15px 0 10px 0' }} />}
94
+ {userInfo && (
95
+ <Box display="flex" alignItems="center">
96
+ <DidAvatar variant="circle" did={userInfo.did} src={userInfo.avatar} size={28} shape="circle" />
97
+ <Box ml={2}>
98
+ <Box display="flex" justifyContent="space-between" alignItems="center">
99
+ <Box fontSize={18}>{userInfo.fullName}</Box>
100
+ {userInfo.role?.toUpperCase() && (
101
+ <Chip
102
+ label={userInfo.role?.toUpperCase()}
103
+ size="small"
104
+ variant="outlined"
105
+ sx={{ height: 'auto', marginRight: 0, fontSize: 12 }}
106
+ icon={<SvgIcon component={ShieldCheck} style={{ fontSize: '14px' }} />}
107
+ />
108
+ )}
109
+ </Box>
110
+ <DidAddress responsive={false}>{userInfo.did}</DidAddress>
111
+ </Box>
112
+ </Box>
113
+ )}
114
+ {siteInfo && (
115
+ <Box display="flex" justifyContent="center" mt={2}>
116
+ <Button variant="contained" size="small" onClick={onLoginFederated}>
117
+ {translations[locale].connect}
118
+ </Button>
119
+ </Box>
120
+ )}
121
+ </Box>
122
+ </UserPopper>
123
+ )
124
+ );
125
+ }
126
+
127
+ FederatedLoginDetecter.propTypes = {
128
+ session: PropTypes.object.isRequired,
129
+ anchorEl: PropTypes.instanceOf(Element),
130
+ dark: PropTypes.bool,
131
+ locale: PropTypes.string,
132
+ };
133
+
134
+ FederatedLoginDetecter.defaultProps = {
135
+ dark: false,
136
+ anchorEl: null,
137
+ locale: 'en',
138
+ };
@@ -2,7 +2,7 @@
2
2
  /* eslint-disable react/jsx-no-bind */
3
3
  import { useMemo, useRef, useState } from 'react';
4
4
  import PropTypes from 'prop-types';
5
- import { IconButton, ClickAwayListener, MenuList, MenuItem, Paper, Popper, SvgIcon, Button, Chip } from '@mui/material';
5
+ import { IconButton, MenuList, MenuItem, SvgIcon, Button, Chip } from '@mui/material';
6
6
  import AccountIcon from '@arcblock/icons/lib/Account';
7
7
  import ShieldCheck from 'mdi-material-ui/ShieldCheck';
8
8
  import OpenInIcon from '@arcblock/icons/lib/OpenIn';
@@ -12,12 +12,11 @@ import SwitchProfileIcon from '@mui/icons-material/PersonOutline';
12
12
  import BindWalletIcon from '@mui/icons-material/Link';
13
13
  import SwitchPassportIcon from '@mui/icons-material/VpnKeyOutlined';
14
14
  import useBrowser from '@arcblock/react-hooks/lib/useBrowser';
15
- import { useOAuth } from '@arcblock/did-connect/lib/OAuth';
16
- import { useDid } from '@arcblock/did-connect/lib/User';
17
15
 
18
- import { styled } from '../Theme';
19
16
  import DidAvatar from '../Avatar';
20
17
  import DidAddress from '../Address';
18
+ import FederatedLoginDetecter from './federated-login-detecter';
19
+ import UserPopper from './user-popper';
21
20
 
22
21
  const translations = {
23
22
  en: {
@@ -71,7 +70,8 @@ function SessionManager({
71
70
  }) {
72
71
  const translation = translations[locale] || translations.en;
73
72
  const userAnchorRef = useRef(null);
74
- const { logoutOAuth, bindOAuth, configs: oauthConfigs, switchOAuthPassport } = useOAuth();
73
+ // eslint-disable-next-line react/prop-types
74
+ const { logoutOAuth, bindOAuth, configs: oauthConfigs, switchOAuthPassport } = session.useOAuth();
75
75
  const [userOpen, setUserOpen] = useState(false);
76
76
 
77
77
  // base64 img maybe have some blank char, need encodeURIComponent to transform it
@@ -81,7 +81,8 @@ function SessionManager({
81
81
  [session.user]
82
82
  );
83
83
  const browser = useBrowser();
84
- const { walletDid } = useDid({ session });
84
+ // eslint-disable-next-line react/prop-types
85
+ const { walletDid } = session.useDid({ session });
85
86
 
86
87
  const isRawWalletAccount = getSourceProvider(session.user) === 'wallet';
87
88
  const connectedAccounts = getConnectedAccounts(session.user);
@@ -104,21 +105,28 @@ function SessionManager({
104
105
  .filter((item) => item.enabled);
105
106
 
106
107
  if (!session.user) {
107
- return showText ? (
108
- <Button
109
- sx={[{ borderRadius: '100vw' }, dark && { color: '#fff', borderColor: '#fff' }]}
110
- variant="outlined"
111
- onClick={_onLogin}
112
- aria-label="login button"
113
- {...rest}
114
- data-cy="sessionManager-login">
115
- <AccountIcon />
116
- <span style={{ lineHeight: '25px' }}>{translation.connect}</span>
117
- </Button>
118
- ) : (
119
- <IconButton {...rest} onClick={_onLogin} data-cy="sessionManager-login" size="medium">
120
- <AccountIcon style={{ width: size, height: size, color: dark ? '#fff' : '' }} />
121
- </IconButton>
108
+ return (
109
+ <>
110
+ {showText ? (
111
+ <Button
112
+ ref={userAnchorRef}
113
+ sx={[{ borderRadius: '100vw' }, dark && { color: '#fff', borderColor: '#fff' }]}
114
+ variant="outlined"
115
+ onClick={_onLogin}
116
+ aria-label="login button"
117
+ {...rest}
118
+ data-cy="sessionManager-login">
119
+ <AccountIcon />
120
+ <span style={{ lineHeight: '25px' }}>{translation.connect}</span>
121
+ </Button>
122
+ ) : (
123
+ <IconButton ref={userAnchorRef} {...rest} onClick={_onLogin} data-cy="sessionManager-login" size="medium">
124
+ <AccountIcon style={{ width: size, height: size, color: dark ? '#fff' : '' }} />
125
+ </IconButton>
126
+ )}
127
+
128
+ <FederatedLoginDetecter locale={locale} dark={dark} session={session} anchorEl={userAnchorRef.current} />
129
+ </>
122
130
  );
123
131
  }
124
132
 
@@ -198,163 +206,127 @@ function SessionManager({
198
206
  style={{ lineHeight: 1 }}>
199
207
  <DidAvatar variant="circle" did={session.user.did} src={avatar} size={size} shape="circle" />
200
208
  </IconButton>
201
- {userAnchorRef.current && (
202
- <StyledPopper
203
- open={userOpen}
204
- disablePortal
205
- anchorEl={userAnchorRef.current}
206
- placement="bottom-end"
207
- $dark={dark}>
208
- <Paper
209
- sx={[
210
- (theme) => ({
211
- borderColor: '#F0F0F0',
212
- boxShadow: '0px 8px 12px rgba(92, 92, 92, 0.04)',
213
- borderRadius: theme.spacing(2),
214
- overflow: 'hidden',
215
- maxWidth: 'calc(100vw - 10px)',
216
- '& .MuiChip-root .MuiChip-icon': {
217
- color: theme.palette.success.main,
218
- },
219
- }),
220
- dark && {
221
- backgroundColor: '#27282c',
222
- color: '#fff',
223
- border: 0,
224
- '& .MuiChip-root': {
225
- borderColor: '#aaa',
226
- },
227
- '& .MuiListItem-root, & .MuiChip-label': {
228
- color: '#aaa',
229
- },
230
- '& .MuiListItem-root:hover': {
231
- backgroundColor: '#363434',
232
- },
233
- },
234
- ]}
235
- variant="outlined">
236
- <ClickAwayListener onClickAway={onCloseUser}>
237
- <MenuList sx={{ p: 0 }}>
238
- <div className="session-manager-user">
239
- <div className="session-manager-user-name">
240
- <span>{session.user.fullName}</span>
241
- {!!showRole && (currentRole?.title || session.user?.role.toUpperCase()) && (
242
- <Chip
243
- label={currentRole?.title || session.user?.role.toUpperCase()}
244
- size="small"
245
- variant="outlined"
246
- sx={{ height: 'auto', marginRight: 0 }}
247
- icon={<SvgIcon component={ShieldCheck} size="small" />}
248
- />
249
- )}
250
- </div>
251
- <div className="session-manager-id-list">
252
- {walletDid && (
253
- <div className="session-manager-id-item">
254
- <DidAddress responsive={false}>{walletDid}</DidAddress>
255
- </div>
256
- )}
257
- {session?.user?.email && (
258
- <div className="session-manager-id-item">
259
- <DidAddress responsive={false}>{session.user.email}</DidAddress>
260
- </div>
261
- )}
262
- </div>
263
- </div>
264
- {Array.isArray(menu) &&
265
- menu.map((menuItem, index) => {
266
- const { svgIcon, ...menuProps } = menuItem;
267
- return (
268
- <MenuItem
269
- key={index}
270
- className="session-manager-menu-item"
271
- {...{
272
- ...menuProps,
273
- icon: undefined,
274
- label: undefined,
275
- }}>
276
- {svgIcon
277
- ? svgIcon && <SvgIcon component={svgIcon} className="session-manager-menu-icon" />
278
- : menuItem.icon}
279
- {menuItem.label}
280
- </MenuItem>
281
- );
282
- })}
283
- {menuRender({
284
- classes: {
285
- menuItem: 'session-manager-menu-item',
286
- menuIcon: 'session-manager-menu-icon',
287
- },
288
- })}
289
- {!browser.wallet && (
290
- <MenuItem
291
- component="a"
292
- className="session-manager-menu-item"
293
- data-cy="sessionManager-openInWallet"
294
- href="https://www.abtwallet.io/"
295
- target="_blank">
296
- <SvgIcon component={OpenInIcon} className="session-manager-menu-icon" />
297
- {translation.openInWallet}
298
- </MenuItem>
299
- )}
300
- {!!switchDid && (
301
- <MenuItem
302
- className="session-manager-menu-item"
303
- onClick={_onSwitchDid}
304
- data-cy="sessionManager-switch-trigger">
305
- <SvgIcon component={SwitchDidIcon} className="session-manager-menu-icon" />
306
- {translation.switchDid}
307
- </MenuItem>
308
- )}
309
- {!!switchProfile && hasBindWallet && (
310
- <MenuItem
311
- className="session-manager-menu-item"
312
- onClick={_onSwitchProfile}
313
- data-cy="sessionManager-switch-profile-trigger">
314
- <SvgIcon component={SwitchProfileIcon} className="session-manager-menu-icon" />
315
- {translation.switchProfile}
316
- </MenuItem>
317
- )}
318
- {!!switchPassport && (
319
- <MenuItem
320
- className="session-manager-menu-item"
321
- onClick={_onSwitchPassport}
322
- data-cy="sessionManager-switch-passport-trigger">
323
- <SvgIcon component={SwitchPassportIcon} className="session-manager-menu-icon" />
324
- {translation.switchPassport}
325
- </MenuItem>
326
- )}
327
- {oauthConfigList.length > 0 && !hasBindAccount && (
328
- <MenuItem
329
- className="session-manager-menu-item"
330
- onClick={_onBindWallet}
331
- data-cy="sessionManager-bind-trigger">
332
- <SvgIcon component={BindWalletIcon} className="session-manager-menu-icon" />
333
- {isRawWalletAccount
334
- ? `${translation.bind}${translation.thirdParty}`
335
- : `${translation.bind}DID Wallet`}
336
- </MenuItem>
337
- )}
338
209
 
210
+ <UserPopper open={userOpen} onClose={onCloseUser} anchorEl={userAnchorRef.current} dark={dark}>
211
+ <MenuList sx={{ p: 0 }}>
212
+ <div className="session-manager-user">
213
+ <div className="session-manager-user-name">
214
+ <span>{session.user.fullName}</span>
215
+ {!!showRole && (currentRole?.title || session.user?.role.toUpperCase()) && (
216
+ <Chip
217
+ label={currentRole?.title || session.user?.role.toUpperCase()}
218
+ size="small"
219
+ variant="outlined"
220
+ sx={{ height: 'auto', marginRight: 0 }}
221
+ icon={<SvgIcon component={ShieldCheck} size="small" />}
222
+ />
223
+ )}
224
+ </div>
225
+ <div className="session-manager-id-list">
226
+ {walletDid && (
227
+ <div className="session-manager-id-item">
228
+ <DidAddress responsive={false}>{walletDid}</DidAddress>
229
+ </div>
230
+ )}
231
+ {session?.user?.email && (
232
+ <div className="session-manager-id-item">
233
+ <DidAddress responsive={false}>{session.user.email}</DidAddress>
234
+ </div>
235
+ )}
236
+ </div>
237
+ </div>
238
+ {Array.isArray(menu) &&
239
+ menu.map((menuItem, index) => {
240
+ const { svgIcon, ...menuProps } = menuItem;
241
+ return (
339
242
  <MenuItem
243
+ key={index}
340
244
  className="session-manager-menu-item"
341
- onClick={_onLogout}
342
- disabled={disableLogout}
343
- data-cy="sessionManager-logout-trigger">
344
- <SvgIcon component={DisconnectIcon} className="session-manager-menu-icon" />
345
- {translation.disconnect}
245
+ {...{
246
+ ...menuProps,
247
+ icon: undefined,
248
+ label: undefined,
249
+ }}>
250
+ {svgIcon
251
+ ? svgIcon && <SvgIcon component={svgIcon} className="session-manager-menu-icon" />
252
+ : menuItem.icon}
253
+ {menuItem.label}
346
254
  </MenuItem>
347
- </MenuList>
348
- </ClickAwayListener>
349
- </Paper>
350
- </StyledPopper>
351
- )}
255
+ );
256
+ })}
257
+ {menuRender({
258
+ classes: {
259
+ menuItem: 'session-manager-menu-item',
260
+ menuIcon: 'session-manager-menu-icon',
261
+ },
262
+ })}
263
+ {!browser.wallet && (
264
+ <MenuItem
265
+ component="a"
266
+ className="session-manager-menu-item"
267
+ data-cy="sessionManager-openInWallet"
268
+ href="https://www.abtwallet.io/"
269
+ target="_blank">
270
+ <SvgIcon component={OpenInIcon} className="session-manager-menu-icon" />
271
+ {translation.openInWallet}
272
+ </MenuItem>
273
+ )}
274
+ {!!switchDid && (
275
+ <MenuItem
276
+ className="session-manager-menu-item"
277
+ onClick={_onSwitchDid}
278
+ data-cy="sessionManager-switch-trigger">
279
+ <SvgIcon component={SwitchDidIcon} className="session-manager-menu-icon" />
280
+ {translation.switchDid}
281
+ </MenuItem>
282
+ )}
283
+ {/* NOTE: federated 登录方式不允许切换 profile */}
284
+ {!!switchProfile && hasBindWallet && session.provider !== 'federated' && (
285
+ <MenuItem
286
+ className="session-manager-menu-item"
287
+ onClick={_onSwitchProfile}
288
+ data-cy="sessionManager-switch-profile-trigger">
289
+ <SvgIcon component={SwitchProfileIcon} className="session-manager-menu-icon" />
290
+ {translation.switchProfile}
291
+ </MenuItem>
292
+ )}
293
+ {!!switchPassport && (
294
+ <MenuItem
295
+ className="session-manager-menu-item"
296
+ onClick={_onSwitchPassport}
297
+ data-cy="sessionManager-switch-passport-trigger">
298
+ <SvgIcon component={SwitchPassportIcon} className="session-manager-menu-icon" />
299
+ {translation.switchPassport}
300
+ </MenuItem>
301
+ )}
302
+ {oauthConfigList.length > 0 && !hasBindAccount && session.provider !== 'federated' && (
303
+ <MenuItem
304
+ className="session-manager-menu-item"
305
+ onClick={_onBindWallet}
306
+ data-cy="sessionManager-bind-trigger">
307
+ <SvgIcon component={BindWalletIcon} className="session-manager-menu-icon" />
308
+ {isRawWalletAccount ? `${translation.bind}${translation.thirdParty}` : `${translation.bind}DID Wallet`}
309
+ </MenuItem>
310
+ )}
311
+
312
+ <MenuItem
313
+ className="session-manager-menu-item"
314
+ onClick={_onLogout}
315
+ disabled={disableLogout}
316
+ data-cy="sessionManager-logout-trigger">
317
+ <SvgIcon component={DisconnectIcon} className="session-manager-menu-icon" />
318
+ {translation.disconnect}
319
+ </MenuItem>
320
+ </MenuList>
321
+ </UserPopper>
352
322
  </>
353
323
  );
354
324
  }
355
325
 
356
326
  SessionManager.propTypes = {
357
327
  session: PropTypes.shape({
328
+ federatedMaster: PropTypes.object,
329
+ provider: PropTypes.oneOf(['wallet', 'federated', 'auth0']),
358
330
  user: PropTypes.shape({
359
331
  did: PropTypes.string.isRequired,
360
332
  role: PropTypes.string.isRequired,
@@ -427,69 +399,4 @@ SessionManager.defaultProps = {
427
399
  size: 24,
428
400
  };
429
401
 
430
- const StyledPopper = styled(Popper)`
431
- z-index: ${({ theme }) => theme.zIndex.tooltip};
432
- .MuiList-root {
433
- /* HACK: 需要288px 才能将 did 展示完整 */
434
- width: 290px;
435
- }
436
- .session-manager-user {
437
- font-size: 12px;
438
- flex-direction: column;
439
- align-items: flex-start;
440
- padding: 24px 24px 10px;
441
- }
442
- .session-manager-user-name {
443
- font-size: 20px;
444
- color: ${({ $dark }) => ($dark ? '#aaa' : '#222')};
445
- font-weight: bold;
446
- margin-bottom: 10px;
447
- display: flex;
448
- align-items: center;
449
- justify-content: space-between;
450
- }
451
- .session-manager-id-item {
452
- position: relative;
453
- padding-left: 8px;
454
- /* HACK: 当前元素既是第一个,也是最后一个,即只有一个同级元素 */
455
- &:first-of-type:last-of-type {
456
- padding-left: 0;
457
- &:before,
458
- &:after {
459
- content: unset;
460
- }
461
- }
462
- &:before {
463
- position: absolute;
464
- content: '';
465
- left: 0px;
466
- top: 50%;
467
- width: 6px;
468
- height: 1px;
469
- background-color: #aeaeae;
470
- }
471
- &:not(:last-of-type):after {
472
- position: absolute;
473
- content: '';
474
- left: 0px;
475
- top: 50%;
476
- height: 100%;
477
- width: 1px;
478
- background-color: #aeaeae;
479
- }
480
- }
481
- .session-manager-menu-item {
482
- padding: 18.5px 24px;
483
- color: #777;
484
- font-size: 16px;
485
- &:hover {
486
- background-color: #fbfbfb;
487
- }
488
- }
489
- .session-manager-menu-icon {
490
- color: #999;
491
- margin-right: 16px;
492
- }
493
- `;
494
-
495
402
  export default SessionManager;
@@ -0,0 +1,121 @@
1
+ import PropTypes from 'prop-types';
2
+ import { ClickAwayListener, Paper, Popper } from '@mui/material';
3
+ import { styled } from '../Theme';
4
+
5
+ export default function UserPopper({ anchorEl, dark, children, open, onClose }) {
6
+ return (
7
+ anchorEl && (
8
+ <StyledPopper open={open} disablePortal anchorEl={anchorEl} placement="bottom-end" $dark={dark}>
9
+ <Paper
10
+ sx={[
11
+ (theme) => ({
12
+ borderColor: '#F0F0F0',
13
+ boxShadow: '0px 8px 12px rgba(92, 92, 92, 0.04)',
14
+ borderRadius: theme.spacing(2),
15
+ overflow: 'hidden',
16
+ maxWidth: 'calc(100vw - 10px)',
17
+ '& .MuiChip-root .MuiChip-icon': {
18
+ color: theme.palette.success.main,
19
+ },
20
+ }),
21
+ dark && {
22
+ backgroundColor: '#27282c',
23
+ color: '#fff',
24
+ border: 0,
25
+ '& .MuiChip-root': {
26
+ borderColor: '#aaa',
27
+ },
28
+ '& .MuiListItem-root, & .MuiChip-label': {
29
+ color: '#aaa',
30
+ },
31
+ '& .MuiListItem-root:hover': {
32
+ backgroundColor: '#363434',
33
+ },
34
+ },
35
+ ]}
36
+ variant="outlined">
37
+ <ClickAwayListener onClickAway={onClose}>{children}</ClickAwayListener>
38
+ </Paper>
39
+ </StyledPopper>
40
+ )
41
+ );
42
+ }
43
+
44
+ UserPopper.propTypes = {
45
+ anchorEl: PropTypes.instanceOf(Element),
46
+ dark: PropTypes.bool,
47
+ open: PropTypes.bool,
48
+ children: PropTypes.any.isRequired,
49
+ onClose: PropTypes.func,
50
+ };
51
+ UserPopper.defaultProps = {
52
+ anchorEl: null,
53
+ dark: false,
54
+ open: false,
55
+ onClose: () => {},
56
+ };
57
+
58
+ const StyledPopper = styled(Popper)`
59
+ z-index: ${({ theme }) => theme.zIndex.tooltip};
60
+ .MuiList-root {
61
+ /* HACK: 需要288px 才能将 did 展示完整 */
62
+ width: 290px;
63
+ }
64
+ .session-manager-user {
65
+ font-size: 12px;
66
+ flex-direction: column;
67
+ align-items: flex-start;
68
+ padding: 24px 24px 10px;
69
+ }
70
+ .session-manager-user-name {
71
+ font-size: 20px;
72
+ color: ${({ $dark }) => ($dark ? '#aaa' : '#222')};
73
+ font-weight: bold;
74
+ margin-bottom: 10px;
75
+ display: flex;
76
+ align-items: center;
77
+ justify-content: space-between;
78
+ }
79
+ .session-manager-id-item {
80
+ position: relative;
81
+ padding-left: 8px;
82
+ /* HACK: 当前元素既是第一个,也是最后一个,即只有一个同级元素 */
83
+ &:first-of-type:last-of-type {
84
+ padding-left: 0;
85
+ &:before,
86
+ &:after {
87
+ content: unset;
88
+ }
89
+ }
90
+ &:before {
91
+ position: absolute;
92
+ content: '';
93
+ left: 0px;
94
+ top: 50%;
95
+ width: 6px;
96
+ height: 1px;
97
+ background-color: #aeaeae;
98
+ }
99
+ &:not(:last-of-type):after {
100
+ position: absolute;
101
+ content: '';
102
+ left: 0px;
103
+ top: 50%;
104
+ height: 100%;
105
+ width: 1px;
106
+ background-color: #aeaeae;
107
+ }
108
+ }
109
+ .session-manager-menu-item {
110
+ padding: 18.5px 24px;
111
+ color: #777;
112
+ font-size: 16px;
113
+ &:hover {
114
+ background-color: #fbfbfb;
115
+ }
116
+ }
117
+ .session-manager-menu-icon {
118
+ color: #999;
119
+ margin-right: 16px;
120
+ }
121
+ `;