@arcblock/ux 2.7.15 → 2.7.17

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 (57) hide show
  1. package/es/Dialog/confirm.js +10 -8
  2. package/es/Img/index.js +7 -7
  3. package/es/SessionManager/account-item.js +133 -0
  4. package/es/SessionManager/add-account-item.js +100 -0
  5. package/es/SessionManager/federated-login-detecter.js +37 -33
  6. package/es/SessionManager/index.js +119 -259
  7. package/es/SessionManager/manage-accounts.js +156 -0
  8. package/es/SessionManager/manage-blocklet.js +70 -0
  9. package/es/SessionManager/menu-accordion.js +104 -0
  10. package/es/SessionManager/translation.js +52 -0
  11. package/es/SessionManager/use-config.js +34 -0
  12. package/es/SessionManager/user-info.js +147 -0
  13. package/es/SessionManager/user-popper.js +10 -53
  14. package/es/SessionManager/utils.js +2 -0
  15. package/es/Typography/index.js +89 -0
  16. package/es/Util/federated.js +65 -0
  17. package/es/Util/index.js +7 -0
  18. package/lib/Dialog/confirm.js +9 -7
  19. package/lib/Img/index.js +7 -7
  20. package/lib/SessionManager/account-item.js +141 -0
  21. package/lib/SessionManager/add-account-item.js +108 -0
  22. package/lib/SessionManager/federated-login-detecter.js +38 -33
  23. package/lib/SessionManager/index.js +122 -272
  24. package/lib/SessionManager/manage-accounts.js +168 -0
  25. package/lib/SessionManager/manage-blocklet.js +86 -0
  26. package/lib/SessionManager/menu-accordion.js +112 -0
  27. package/lib/SessionManager/translation.js +59 -0
  28. package/lib/SessionManager/use-config.js +41 -0
  29. package/lib/SessionManager/user-info.js +163 -0
  30. package/lib/SessionManager/user-popper.js +8 -8
  31. package/lib/SessionManager/utils.js +16 -0
  32. package/lib/Typography/index.js +100 -0
  33. package/lib/Util/federated.js +85 -0
  34. package/lib/Util/index.js +11 -2
  35. package/package.json +12 -5
  36. package/src/Dialog/confirm.js +9 -6
  37. package/src/Img/index.js +5 -5
  38. package/src/SessionManager/account-item.jsx +109 -0
  39. package/src/SessionManager/add-account-item.jsx +97 -0
  40. package/src/SessionManager/federated-login-detecter.jsx +42 -29
  41. package/src/SessionManager/index.jsx +131 -259
  42. package/src/SessionManager/manage-accounts.jsx +157 -0
  43. package/src/SessionManager/manage-blocklet.jsx +70 -0
  44. package/src/SessionManager/menu-accordion.jsx +94 -0
  45. package/src/SessionManager/translation.js +52 -0
  46. package/src/SessionManager/use-config.js +33 -0
  47. package/src/SessionManager/user-info.jsx +116 -0
  48. package/src/SessionManager/user-popper.jsx +6 -51
  49. package/src/SessionManager/utils.js +3 -0
  50. package/src/Typography/index.jsx +79 -0
  51. package/src/Util/federated.js +73 -0
  52. package/src/Util/index.js +8 -0
  53. /package/src/Avatar/{did-motif.js → did-motif.jsx} +0 -0
  54. /package/src/Avatar/{index.js → index.jsx} +0 -0
  55. /package/src/Header/{auto-hidden.js → auto-hidden.jsx} +0 -0
  56. /package/src/Header/{header.js → header.jsx} +0 -0
  57. /package/src/Header/{responsive-header.js → responsive-header.jsx} +0 -0
@@ -0,0 +1,157 @@
1
+ /* eslint-disable react/prop-types */
2
+ /* eslint-disable react/jsx-no-bind */
3
+ import { useMemoizedFn, useUpdate } from 'ahooks';
4
+ import { MenuItem, SvgIcon } 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
+ import DisconnectIcon from '@arcblock/icons/lib/Disconnect';
9
+
10
+ import AccountItem from './account-item';
11
+ import MenuAccordion from './menu-accordion';
12
+ import { translations } from './translation';
13
+ import { translate } from '../Locale/util';
14
+ import { useConfirm } from '../Dialog/confirm';
15
+ import { getSourceProvider } from './utils';
16
+ import AddAccountItem from './add-account-item';
17
+ import useConfig from './use-config';
18
+
19
+ export default function ManageAccounts({
20
+ session,
21
+ locale,
22
+ onBindWallet,
23
+ onSwitchDid,
24
+ connectAccount,
25
+ close,
26
+ hasBindAccount,
27
+ disableLogout,
28
+ onLogout,
29
+ expanded,
30
+ onExpand,
31
+ }) {
32
+ const { bindOAuth, configs: oauthConfigs } = session.useOAuth();
33
+ const t = useMemoizedFn((key, data = {}) => {
34
+ return translate(translations, key, locale, 'en', data);
35
+ });
36
+
37
+ const { confirmApi, confirmHolder } = useConfirm();
38
+ const { deleteAccount, config } = useConfig();
39
+ const update = useUpdate();
40
+
41
+ const onChoose = useMemoizedFn((account, { active }) => {
42
+ if (active) {
43
+ return;
44
+ }
45
+ close();
46
+ session.switchDid(
47
+ (...args) => {
48
+ connectAccount();
49
+ onSwitchDid(...args);
50
+ },
51
+ {
52
+ provider: account.provider,
53
+ sourceAppPid: account.sourceAppPid,
54
+ }
55
+ );
56
+ });
57
+
58
+ const oauthConfigList = Object.entries(oauthConfigs)
59
+ .map(([key, value]) => {
60
+ return { ...value, provider: key };
61
+ })
62
+ .filter((item) => item.enabled);
63
+
64
+ const isRawWalletAccount = getSourceProvider(session.user) === 'wallet';
65
+
66
+ const onDelete = useMemoizedFn((account, { active }) => {
67
+ if (active) {
68
+ return;
69
+ }
70
+ confirmApi.open({
71
+ title: t('deleteAccountTitle'),
72
+ content: t('deleteAccountContent'),
73
+ confirmButtonText: t('confirm'),
74
+ cancelButtonText: t('cancel'),
75
+ onConfirm(done) {
76
+ deleteAccount(account);
77
+
78
+ update();
79
+ done();
80
+ },
81
+ });
82
+ });
83
+
84
+ const onAdd = useMemoizedFn((app) => {
85
+ close();
86
+ session.switchDid(
87
+ (...args) => {
88
+ connectAccount();
89
+ update();
90
+ onSwitchDid(...args);
91
+ },
92
+ {
93
+ sourceAppPid: app.sourceAppPid,
94
+ provider: app.provider,
95
+ }
96
+ );
97
+ });
98
+
99
+ function _onBindWallet() {
100
+ close();
101
+ // FIXME: @zhanghan 暂时切换回 isRawWalletAccount 的方式来判断,在 did-connect 改版时,简化这里的关系判断
102
+ if (!isRawWalletAccount) {
103
+ session.bindWallet(onBindWallet);
104
+ } else {
105
+ bindOAuth();
106
+ }
107
+ }
108
+
109
+ return (
110
+ <>
111
+ <MenuAccordion
112
+ expanded={expanded}
113
+ onChange={onExpand}
114
+ locale={locale}
115
+ title={
116
+ <>
117
+ <AccountIcon className="session-manager-menu-icon" style={{ width: 24, height: 24 }} />
118
+ {t('accounts')}
119
+ </>
120
+ }>
121
+ {config.accounts.map((item) => (
122
+ <AccountItem
123
+ key={item.did}
124
+ account={item}
125
+ locale={locale}
126
+ active={session.user.did === item.did}
127
+ onDelete={onDelete}
128
+ onChoose={onChoose}
129
+ />
130
+ ))}
131
+ <AddAccountItem locale={locale} onAdd={onAdd} />
132
+
133
+ {oauthConfigList.length > 0 && !hasBindAccount && (
134
+ <MenuItem
135
+ className="session-manager-menu-item"
136
+ onClick={_onBindWallet}
137
+ aria-label={!isRawWalletAccount ? `${t('bind')}DID Wallet` : `${t('bind')}${t('thirdParty')}`}
138
+ data-cy="sessionManager-bind-trigger">
139
+ <Icon icon={LinkIcon} width={24} height={24} className="session-manager-menu-icon" />
140
+ {!isRawWalletAccount ? `${t('bind')}DID Wallet` : `${t('bind')}${t('thirdParty')}`}
141
+ </MenuItem>
142
+ )}
143
+
144
+ <MenuItem
145
+ className="session-manager-menu-item"
146
+ onClick={onLogout}
147
+ disabled={disableLogout}
148
+ aria-label="Logout account"
149
+ data-cy="sessionManager-logout-trigger">
150
+ <SvgIcon component={DisconnectIcon} className="session-manager-menu-icon" />
151
+ {t('disconnect')}
152
+ </MenuItem>
153
+ </MenuAccordion>
154
+ {confirmHolder}
155
+ </>
156
+ );
157
+ }
@@ -0,0 +1,70 @@
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, expanded, onExpand }) {
13
+ const t = useMemoizedFn((key, data = {}) => {
14
+ return translate(translations, key, locale, 'en', data);
15
+ });
16
+
17
+ return (
18
+ <MenuAccordion
19
+ expanded={expanded}
20
+ locale={locale}
21
+ onChange={onExpand}
22
+ title={
23
+ <>
24
+ <Icon icon={AppsIcon} width={24} height={24} className="session-manager-menu-icon" />
25
+ {t('blocklet')}
26
+ </>
27
+ }>
28
+ {Array.isArray(menu) &&
29
+ menu.map((menuItem, index) => {
30
+ const { svgIcon, ...menuProps } = menuItem;
31
+ return (
32
+ <MenuItem
33
+ key={index}
34
+ className="session-manager-menu-item"
35
+ {...{
36
+ ...menuProps,
37
+ icon: undefined,
38
+ label: undefined,
39
+ }}>
40
+ {svgIcon
41
+ ? svgIcon && <SvgIcon component={svgIcon} className="session-manager-menu-icon" />
42
+ : menuItem.icon}
43
+ {menuItem.label}
44
+ </MenuItem>
45
+ );
46
+ })}
47
+ {menuRender({
48
+ classes: {
49
+ menuItem: 'session-manager-menu-item',
50
+ menuIcon: 'session-manager-menu-icon',
51
+ },
52
+ })}
53
+ </MenuAccordion>
54
+ );
55
+ }
56
+
57
+ ManageBlocklet.propTypes = {
58
+ menu: PropTypes.array,
59
+ menuRender: PropTypes.func,
60
+ locale: PropTypes.string.isRequired,
61
+ expanded: PropTypes.bool,
62
+ onExpand: PropTypes.func,
63
+ };
64
+
65
+ ManageBlocklet.defaultProps = {
66
+ menu: [],
67
+ menuRender: () => {},
68
+ expanded: true,
69
+ onExpand: () => {},
70
+ };
@@ -0,0 +1,94 @@
1
+ import PropTypes from 'prop-types';
2
+ import { Accordion, AccordionDetails, AccordionSummary, MenuItem } from '@mui/material';
3
+ import ExpandMoreIcon from '@iconify-icons/mdi/expand-more';
4
+ import { Icon } from '@iconify/react';
5
+ import isEmpty from 'lodash/isEmpty';
6
+ import isNil from 'lodash/isNil';
7
+ import { useCreation, useMemoizedFn } from 'ahooks';
8
+ import { translate } from '../Locale/util';
9
+ import { translations } from './translation';
10
+
11
+ function isEmptyNode(node) {
12
+ if (isNil(node)) {
13
+ return true;
14
+ }
15
+ if (isEmpty(node)) {
16
+ return true;
17
+ }
18
+ if (Array.isArray(node)) {
19
+ return node.every((item) => isEmptyNode(item));
20
+ }
21
+ return false;
22
+ }
23
+
24
+ export default function MenuAccordion({ title, children, locale, expanded, onChange }) {
25
+ const isEmptyChildren = useCreation(() => isEmptyNode(children), [children]);
26
+ const t = useMemoizedFn((key, data = {}) => {
27
+ return translate(translations, key, locale, 'en', data);
28
+ });
29
+
30
+ return (
31
+ <Accordion
32
+ expanded={expanded}
33
+ disableGutters
34
+ elevation={0}
35
+ onChange={(e, value) => {
36
+ onChange(value);
37
+ }}
38
+ sx={{
39
+ '&.MuiAccordion-root:before': {
40
+ content: 'unset',
41
+ },
42
+ '.MuiAccordionSummary-root': {
43
+ minHeight: 'auto',
44
+ width: '100%',
45
+ },
46
+ '.MuiAccordionSummary-content': {
47
+ margin: 0,
48
+ },
49
+ '.MuiAccordionDetails-root': {
50
+ padding: 0,
51
+ paddingLeft: '30px',
52
+ '.session-manager-menu-item': {
53
+ fontSize: '15px',
54
+ padding: '12px 15px',
55
+ whiteSpace: 'normal',
56
+ },
57
+ '.session-manager-menu-icon': {
58
+ height: '21px',
59
+ widht: '21px',
60
+ },
61
+ },
62
+ }}>
63
+ <MenuItem sx={{ padding: 0, width: '100%' }}>
64
+ <AccordionSummary
65
+ className="session-manager-menu-item"
66
+ expandIcon={<Icon icon={ExpandMoreIcon} width={24} height={24} />}>
67
+ {title}
68
+ </AccordionSummary>
69
+ </MenuItem>
70
+ <AccordionDetails>
71
+ {isEmptyChildren ? (
72
+ <MenuItem disabled key="empty" className="session-manager-menu-item" sx={{ justifyContent: 'center' }}>
73
+ {t('noneMenu')}
74
+ </MenuItem>
75
+ ) : (
76
+ children
77
+ )}
78
+ </AccordionDetails>
79
+ </Accordion>
80
+ );
81
+ }
82
+
83
+ MenuAccordion.propTypes = {
84
+ title: PropTypes.any.isRequired,
85
+ children: PropTypes.any.isRequired,
86
+ locale: PropTypes.string,
87
+ expanded: PropTypes.bool,
88
+ onChange: PropTypes.func,
89
+ };
90
+ MenuAccordion.defaultProps = {
91
+ locale: 'en',
92
+ expanded: true,
93
+ onChange: () => {},
94
+ };
@@ -0,0 +1,52 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+ export const translations = {
3
+ en: {
4
+ switch: 'Switch',
5
+ account: 'account',
6
+ switchDid: 'Switch DID',
7
+ switchTo: 'Switch to',
8
+ switchProfile: 'Switch Profile',
9
+ switchPassport: 'Switch Passport',
10
+ disconnect: 'Disconnect',
11
+ connect: 'Connect',
12
+ openInWallet: 'Open DID Wallet',
13
+ alreadyBindOAuth: 'Already bind Auth0',
14
+ bind: 'Bind ',
15
+ thirdParty: 'Third Party Login',
16
+ addAppAccount: 'Add {appName} account',
17
+ accounts: 'Accounts',
18
+ blocklet: 'Blocklet',
19
+ from: 'From',
20
+ addAnotherAccount: 'Add another account',
21
+ deleteAccountTitle: 'Remove this account ?',
22
+ deleteAccountContent: 'After delete account, you can add it agian',
23
+ confirm: 'Confirm',
24
+ cancel: 'Cancel',
25
+ noneMenu: 'Empty menu, maybe you should switch to another role',
26
+ },
27
+ zh: {
28
+ switch: '切换',
29
+ account: '账号',
30
+ switchDid: '切换账户',
31
+ switchTo: '切换至',
32
+ switchProfile: '切换用户信息',
33
+ switchPassport: '切换通行证',
34
+ disconnect: '退出登录',
35
+ connect: '登录',
36
+ openInWallet: '打开 DID 钱包',
37
+ // NOTE: 目前只有 Auth0,展示出具体的第三方名字会更好
38
+ alreadyBindOAuth: '已绑定 Auth0 账号',
39
+ bind: '绑定',
40
+ thirdParty: '第三方登录',
41
+ addAppAccount: '添加 {appName} 账户',
42
+ accounts: '账户',
43
+ blocklet: '应用',
44
+ from: '连接至',
45
+ addAnotherAccount: '添加账号',
46
+ deleteAccountTitle: '是否删除账户?',
47
+ deleteAccountContent: '账户删除后,可再次添加',
48
+ confirm: '确认',
49
+ cancel: '取消',
50
+ noneMenu: '无操作项,请尝试切换角色',
51
+ },
52
+ };
@@ -0,0 +1,33 @@
1
+ import { useLocalStorageState, useMemoizedFn } from 'ahooks';
2
+ import cloneDeep from 'lodash/cloneDeep';
3
+
4
+ export default function useConfig() {
5
+ const [config, setConfig] = useLocalStorageState('blocklet:sessionManager:config', {
6
+ defaultValue: {
7
+ accounts: [],
8
+ expandAccount: true,
9
+ expandBlocklet: true,
10
+ },
11
+ });
12
+
13
+ const connectAccount = useMemoizedFn((account) => {
14
+ const cloneConfig = cloneDeep(config);
15
+ const accountIndex = cloneConfig.accounts.findIndex((x) => x.did === account.did);
16
+ if (accountIndex >= 0) {
17
+ cloneConfig.accounts.splice(accountIndex, 1);
18
+ }
19
+ cloneConfig.accounts.unshift(account);
20
+ setConfig(cloneConfig);
21
+ });
22
+
23
+ const deleteAccount = useMemoizedFn((account) => {
24
+ const cloneConfig = cloneDeep(config);
25
+ const findIndex = cloneConfig.accounts.findIndex((item) => item.did === account.did);
26
+ if (findIndex >= 0) {
27
+ cloneConfig.accounts.splice(findIndex, 1);
28
+ }
29
+ setConfig(cloneConfig);
30
+ });
31
+
32
+ return { config, setConfig, connectAccount, deleteAccount };
33
+ }
@@ -0,0 +1,116 @@
1
+ /* eslint-disable react/jsx-no-bind */
2
+ /* eslint-disable react/prop-types */
3
+ import { Box, Chip } from '@mui/material';
4
+ import ShieldCheckIcon from '@iconify-icons/mdi/shield-check';
5
+ import ExpandMoreIcon from '@iconify-icons/mdi/expand-more';
6
+ import { Icon } from '@iconify/react';
7
+ import { useCreation, useMemoizedFn } from 'ahooks';
8
+
9
+ import DidAvatar from '../Avatar';
10
+ import DidAddress from '../Address';
11
+ import DID from '../DID';
12
+ import { getUserAvatar } from '../Util';
13
+ import { t as translate } from '../Locale/util';
14
+ import { translations } from './translation';
15
+ import Typography from '../Typography';
16
+
17
+ export default function UserInfo({ session, locale, onSwitchProfile, onSwitchPassport, switchProfile, hasBindWallet }) {
18
+ const t = (key, data = {}) => {
19
+ return translate(translations, key, locale, 'en', data);
20
+ };
21
+ const avatar = getUserAvatar(session.user?.avatar?.replace(/\s/g, encodeURIComponent(' ')));
22
+ const { walletDid } = session.useDid({ session });
23
+ const currentRole = useCreation(
24
+ () => session.user?.passports?.find((item) => item.name === session.user.role),
25
+ [session?.user?.passports, session?.user?.role]
26
+ );
27
+ const userEmail = useCreation(() => session.user?.email || 'lancelot_lewis@163.com', [session?.user]);
28
+
29
+ const canEdit = useCreation(() => {
30
+ if (onSwitchProfile instanceof Function) {
31
+ if (switchProfile && hasBindWallet) {
32
+ return true;
33
+ }
34
+ }
35
+ return false;
36
+ }, [onSwitchProfile, switchProfile, hasBindWallet, session.provider]);
37
+
38
+ const _onSwitchProfile = useMemoizedFn(() => {
39
+ if (canEdit) {
40
+ onSwitchProfile();
41
+ }
42
+ });
43
+
44
+ return (
45
+ <Box
46
+ sx={{
47
+ display: 'flex',
48
+ alignItems: 'center',
49
+ gap: '12px',
50
+ padding: '15px',
51
+ }}>
52
+ <Box
53
+ onClick={_onSwitchProfile}
54
+ data-cy="sessionManager-switch-profile-trigger"
55
+ sx={{
56
+ cursor: canEdit ? 'pointer' : 'default',
57
+ position: 'relative',
58
+ borderRadius: '100%',
59
+ overflow: 'hidden',
60
+ fontSize: 0,
61
+ '&:hover': canEdit
62
+ ? {
63
+ '&::after': {
64
+ content: `"${t('switch')}"`,
65
+ position: 'absolute',
66
+ bottom: 0,
67
+ background: 'rgba(0, 0, 0, 0.2)',
68
+ left: 0,
69
+ right: 0,
70
+ height: '2.2em',
71
+ color: 'white',
72
+ textAlign: 'center',
73
+ fontSize: '12px',
74
+ lineHeight: '2em',
75
+ },
76
+ }
77
+ : {},
78
+ }}>
79
+ <DidAvatar variant="circle" did={session.user.did} src={avatar} size={64} shape="circle" />
80
+ </Box>
81
+ <Box sx={{ flex: 1, position: 'static', overflow: 'hidden', fontSize: '14px' }}>
82
+ <Box display="flex" alignItems="center" gap="4px" mb={0.5}>
83
+ <Typography variant="h5" fontWeight="bold" fontSize="auto" sx={{ flex: 1, wordBreak: 'break-all' }}>
84
+ {session.user.fullName}
85
+ </Typography>
86
+ <Chip
87
+ label={currentRole?.title || session.user?.role.toUpperCase()}
88
+ size="small"
89
+ variant="outlined"
90
+ sx={{
91
+ height: 'auto',
92
+ marginRight: 0,
93
+ fontWeight: 'bold',
94
+ fontSize: '12px',
95
+ }}
96
+ icon={<Icon icon={ShieldCheckIcon} height="0.8em" />}
97
+ deleteIcon={<Icon icon={ExpandMoreIcon} height="1em" />}
98
+ // HACK: 必须设置 onDelete 函数,deleteIcon 才能显示出来
99
+ onDelete={onSwitchPassport}
100
+ onClick={onSwitchPassport}
101
+ data-cy="sessionManager-switch-passport-trigger"
102
+ />
103
+ </Box>
104
+ {/* eslint-disable-next-line no-nested-ternary */}
105
+ {session.provider === 'auth0' ? (
106
+ walletDid ? (
107
+ <DID responsive={false} compact did={walletDid} />
108
+ ) : null
109
+ ) : (
110
+ <DID responsive={false} compact did={session.user.did} />
111
+ )}
112
+ {userEmail ? <DidAddress responsive={false}>{userEmail}</DidAddress> : null}
113
+ </Box>
114
+ </Box>
115
+ );
116
+ }
@@ -1,6 +1,7 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import { IconButton, Paper, Popper, ClickAwayListener } from '@mui/material';
3
- import { Close as CloseIcon } from '@mui/icons-material';
3
+ import CloseIcon from '@iconify-icons/mdi/close';
4
+ import { Icon } from '@iconify/react';
4
5
  import { styled } from '../Theme';
5
6
 
6
7
  export default function UserPopper({ anchorEl, dark, children, open, onClose, autoClose }) {
@@ -43,7 +44,7 @@ export default function UserPopper({ anchorEl, dark, children, open, onClose, au
43
44
  size="small"
44
45
  sx={{ cursor: 'pointer', position: 'absolute', right: 0, top: 0, zIndex: 1 }}
45
46
  onClick={onClose}>
46
- <CloseIcon />
47
+ <Icon icon={CloseIcon} width={20} height={20} />
47
48
  </IconButton>
48
49
  {children}
49
50
  </>
@@ -75,56 +76,10 @@ const StyledPopper = styled(Popper)`
75
76
  return theme.zIndex.tooltip;
76
77
  }};
77
78
  .MuiList-root {
78
- /* HACK: 需要288px 才能将 did 展示完整 */
79
- width: 290px;
80
- }
81
- .session-manager-user {
82
- font-size: 12px;
83
- flex-direction: column;
84
- align-items: flex-start;
85
- padding: 24px 24px 10px;
86
- }
87
- .session-manager-user-name {
88
- font-size: 20px;
89
- color: ${({ $dark }) => ($dark ? '#aaa' : '#222')};
90
- font-weight: bold;
91
- margin-bottom: 10px;
92
- display: flex;
93
- align-items: center;
94
- justify-content: space-between;
95
- }
96
- .session-manager-id-item {
97
- position: relative;
98
- padding-left: 8px;
99
- /* HACK: 当前元素既是第一个,也是最后一个,即只有一个同级元素 */
100
- &:first-of-type:last-of-type {
101
- padding-left: 0;
102
- &:before,
103
- &:after {
104
- content: unset;
105
- }
106
- }
107
- &:before {
108
- position: absolute;
109
- content: '';
110
- left: 0px;
111
- top: 50%;
112
- width: 6px;
113
- height: 1px;
114
- background-color: #aeaeae;
115
- }
116
- &:not(:last-of-type):after {
117
- position: absolute;
118
- content: '';
119
- left: 0px;
120
- top: 50%;
121
- height: 100%;
122
- width: 1px;
123
- background-color: #aeaeae;
124
- }
79
+ width: 320px;
125
80
  }
126
81
  .session-manager-menu-item {
127
- padding: 18.5px 24px;
82
+ padding: 15px;
128
83
  color: #777;
129
84
  font-size: 16px;
130
85
  &:hover {
@@ -133,6 +88,6 @@ const StyledPopper = styled(Popper)`
133
88
  }
134
89
  .session-manager-menu-icon {
135
90
  color: #999;
136
- margin-right: 16px;
91
+ margin-right: 8px;
137
92
  }
138
93
  `;
@@ -0,0 +1,3 @@
1
+ export const getSourceProvider = (user) => user?.sourceProvider || user?.extraConfigs?.sourceProvider || 'wallet';
2
+
3
+ export const getConnectedAccounts = (user) => user?.connectedAccounts || user?.extraConfigs?.connectedAccounts || [];
@@ -0,0 +1,79 @@
1
+ import PropTyps from 'prop-types';
2
+ import { Box, Typography as MuiTypography, Skeleton } from '@mui/material';
3
+ import { useReactive, useSize } from 'ahooks';
4
+ import { useEffect, useRef } from 'react';
5
+
6
+ export default function Typography({ minFontSize, fontSize, children, sx, ...rest }) {
7
+ const refMock = useRef(null);
8
+ const refContainer = useRef(null);
9
+ const state = useReactive({
10
+ fontSize,
11
+ loading: true,
12
+ initialSize: undefined,
13
+ });
14
+ const mockSize = useSize(refMock.current);
15
+ const containerSize = useSize(refContainer.current);
16
+ useEffect(() => {
17
+ if (state.loading) {
18
+ if (fontSize === 'auto') {
19
+ if (mockSize && !state.initialSize) {
20
+ const styleSize = getComputedStyle(refMock.current).fontSize;
21
+ state.initialSize = Number(styleSize.replace('px', ''));
22
+ state.fontSize = state.initialSize;
23
+ }
24
+ if (containerSize && mockSize) {
25
+ if (containerSize.width < mockSize.width && state.fontSize > minFontSize) {
26
+ state.fontSize--;
27
+ } else {
28
+ state.loading = false;
29
+ }
30
+ }
31
+ } else {
32
+ state.loading = false;
33
+ }
34
+ }
35
+ // eslint-disable-next-line react-hooks/exhaustive-deps
36
+ }, [fontSize, mockSize?.width, containerSize?.width]);
37
+
38
+ return state.loading ? (
39
+ <Box ref={refContainer} flex={1}>
40
+ <MuiTypography {...rest} sx={sx} noWrap>
41
+ <Skeleton variant="text" sx={{ fontSize: '1rem' }} />
42
+ </MuiTypography>
43
+ <MuiTypography
44
+ ref={refMock}
45
+ {...rest}
46
+ sx={{
47
+ ...sx,
48
+ fontSize: `${state.fontSize}px !important`,
49
+ position: 'fixed',
50
+ top: -1000,
51
+ left: -1000,
52
+ }}
53
+ noWrap>
54
+ {children}
55
+ </MuiTypography>
56
+ </Box>
57
+ ) : (
58
+ <MuiTypography
59
+ {...rest}
60
+ sx={{
61
+ ...sx,
62
+ fontSize: `${state.fontSize}px !important`,
63
+ }}>
64
+ {children}
65
+ </MuiTypography>
66
+ );
67
+ }
68
+
69
+ Typography.propTypes = {
70
+ fontSize: PropTyps.oneOfType([PropTyps.string, PropTyps.number]),
71
+ children: PropTyps.any.isRequired,
72
+ minFontSize: PropTyps.number,
73
+ sx: PropTyps.object,
74
+ };
75
+ Typography.defaultProps = {
76
+ fontSize: undefined,
77
+ minFontSize: 12,
78
+ sx: {},
79
+ };