@blocklet/ui-react 2.10.90 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/lib/@types/index.d.ts +1 -0
  2. package/lib/UserCenter/components/config-profile.js +1 -1
  3. package/lib/UserCenter/components/notification.js +1 -2
  4. package/lib/UserCenter/components/privacy.d.ts +1 -0
  5. package/lib/UserCenter/components/privacy.js +3 -0
  6. package/lib/UserCenter/components/settings.d.ts +1 -1
  7. package/lib/UserCenter/components/settings.js +20 -18
  8. package/lib/UserCenter/components/third-party-login/index.js +4 -3
  9. package/lib/UserCenter/components/user-center.d.ts +1 -1
  10. package/lib/UserCenter/components/user-center.js +192 -195
  11. package/lib/UserCenter/components/user-info/index.d.ts +1 -0
  12. package/lib/UserCenter/components/user-info/index.js +1 -0
  13. package/lib/UserCenter/components/user-info/switch-role.d.ts +7 -0
  14. package/lib/UserCenter/components/user-info/switch-role.js +43 -0
  15. package/lib/UserCenter/components/user-info/user-basic-info.d.ts +1 -2
  16. package/lib/UserCenter/components/user-info/user-basic-info.js +33 -85
  17. package/lib/UserCenter/libs/locales.d.ts +4 -0
  18. package/lib/UserCenter/libs/locales.js +6 -2
  19. package/lib/common/header-addons.js +8 -0
  20. package/lib/common/notification-addon.d.ts +13 -0
  21. package/lib/common/notification-addon.js +41 -0
  22. package/package.json +4 -4
  23. package/src/@types/index.ts +1 -0
  24. package/src/UserCenter/components/config-profile.tsx +3 -1
  25. package/src/UserCenter/components/notification.tsx +1 -2
  26. package/src/UserCenter/components/privacy.tsx +4 -0
  27. package/src/UserCenter/components/settings.tsx +27 -27
  28. package/src/UserCenter/components/third-party-login/index.tsx +10 -3
  29. package/src/UserCenter/components/user-center.tsx +195 -180
  30. package/src/UserCenter/components/user-info/index.tsx +1 -0
  31. package/src/UserCenter/components/user-info/switch-role.tsx +44 -0
  32. package/src/UserCenter/components/user-info/user-basic-info.tsx +33 -81
  33. package/src/UserCenter/libs/locales.ts +4 -0
  34. package/src/common/header-addons.jsx +10 -0
  35. package/src/common/notification-addon.jsx +49 -0
@@ -0,0 +1,43 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { memo } from "react";
3
+ import { Chip } from "@mui/material";
4
+ import { Icon } from "@iconify/react";
5
+ import { useCreation } from "ahooks";
6
+ import SwapHorizRoundedIcon from "@iconify-icons/material-symbols/swap-horiz-rounded";
7
+ import { temp as colors } from "@arcblock/ux/lib/Colors";
8
+ function SwitchRole({ user, switchPassport }) {
9
+ const currentRole = useCreation(
10
+ () => (user?.passports || [])?.find((item) => item.name === user.role),
11
+ [user?.passports, user?.role]
12
+ );
13
+ return /* @__PURE__ */ jsx(
14
+ Chip,
15
+ {
16
+ label: currentRole?.title || user?.role || "Guest",
17
+ size: "small",
18
+ variant: "outlined",
19
+ sx: {
20
+ flexShrink: 0,
21
+ fontWeight: "bold",
22
+ fontSize: "12px",
23
+ color: colors.textBase,
24
+ borderColor: colors.strokeBorderStrong,
25
+ backgroundColor: "transparent",
26
+ textTransform: "capitalize",
27
+ pr: 1,
28
+ pl: 0.5,
29
+ "&:hover": {
30
+ backgroundColor: "rgba(0, 0, 0, 0.04)"
31
+ },
32
+ "&:active": {
33
+ boxShadow: "none"
34
+ }
35
+ },
36
+ clickable: true,
37
+ deleteIcon: /* @__PURE__ */ jsx(Icon, { icon: SwapHorizRoundedIcon, color: colors.textBase }),
38
+ onDelete: switchPassport,
39
+ onClick: switchPassport
40
+ }
41
+ );
42
+ }
43
+ export default memo(SwitchRole);
@@ -1,10 +1,9 @@
1
1
  import type { BoxProps } from '@mui/material';
2
2
  import type { User } from '../../../@types';
3
- export default function UserBasicInfo({ user, isMyself, showFullDid, switchPassport, switchProfile, openSettings, ...rest }: {
3
+ export default function UserBasicInfo({ user, isMyself, showFullDid, switchPassport, switchProfile, ...rest }: {
4
4
  user: User;
5
5
  isMyself?: boolean;
6
6
  showFullDid?: boolean;
7
7
  switchPassport: () => void;
8
8
  switchProfile: () => void;
9
- openSettings: () => void;
10
9
  } & BoxProps): import("react").JSX.Element | null;
@@ -1,13 +1,9 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { Box, Chip, CircularProgress, IconButton, Tooltip, Typography } from "@mui/material";
3
- import { Icon } from "@iconify/react";
4
- import SwapHorizRoundedIcon from "@iconify-icons/material-symbols/swap-horiz-rounded";
5
- import SettingsOutlineRoundedIcon from "@iconify-icons/material-symbols/settings-outline-rounded";
2
+ import { Box, CircularProgress, Tooltip, Typography } from "@mui/material";
6
3
  import Avatar from "@arcblock/ux/lib/Avatar";
7
- import { temp as colors } from "@arcblock/ux/lib/Colors";
8
4
  import { useTheme } from "@arcblock/ux/lib/Theme";
9
5
  import DID from "@arcblock/ux/lib/DID";
10
- import { useCreation, useMemoizedFn } from "ahooks";
6
+ import { useMemoizedFn } from "ahooks";
11
7
  import { translate } from "@arcblock/ux/lib/Locale/util";
12
8
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
13
9
  import noop from "lodash/noop";
@@ -18,13 +14,13 @@ import Toast from "@arcblock/ux/lib/Toast";
18
14
  import isEmpty from "lodash/isEmpty";
19
15
  import { translations } from "../../libs/locales.js";
20
16
  import { formatAxiosError } from "../../libs/utils.js";
17
+ import SwitchRole from "./switch-role.js";
21
18
  export default function UserBasicInfo({
22
19
  user,
23
20
  isMyself = true,
24
21
  showFullDid = true,
25
22
  switchPassport,
26
23
  switchProfile,
27
- openSettings,
28
24
  ...rest
29
25
  }) {
30
26
  const { locale } = useLocaleContext();
@@ -33,10 +29,6 @@ export default function UserBasicInfo({
33
29
  const t = useMemoizedFn((key, data = {}) => {
34
30
  return translate(translations, key, locale, "en", data);
35
31
  });
36
- const currentRole = useCreation(
37
- () => (user?.passports || [])?.find((item) => item.name === user.role),
38
- [user?.passports, user?.role]
39
- );
40
32
  const theme = useTheme();
41
33
  const isSmallView = theme.breakpoints.down("md");
42
34
  if (!user) {
@@ -77,7 +69,6 @@ export default function UserBasicInfo({
77
69
  backgroundColor: "#fff",
78
70
  position: "relative",
79
71
  overflow: "hidden",
80
- mb: 1.5,
81
72
  ...isMyself ? {
82
73
  cursor: "pointer",
83
74
  "&::after": {
@@ -99,79 +90,36 @@ export default function UserBasicInfo({
99
90
  onClick: isMyself ? switchProfile : noop
100
91
  }
101
92
  ),
102
- /* @__PURE__ */ jsxs(
103
- Typography,
104
- {
105
- variant: "h6",
106
- sx: {
107
- fontWeight: "bold",
108
- display: "flex",
109
- alignItems: "center",
110
- gap: 1,
111
- fontSize: "24px !important"
112
- },
113
- children: [
114
- user?.fullName,
115
- isMyself ? /* @__PURE__ */ jsxs(Fragment, { children: [
116
- /* @__PURE__ */ jsx(
117
- Chip,
118
- {
119
- label: currentRole?.title || user?.role || "Guest",
120
- size: "small",
121
- variant: "outlined",
122
- sx: {
123
- flexShrink: 0,
124
- fontWeight: "bold",
125
- fontSize: "12px",
126
- color: colors.textBase,
127
- borderColor: colors.strokeBorderStrong,
128
- backgroundColor: "transparent",
129
- textTransform: "capitalize",
130
- pr: 1,
131
- pl: 0.5,
132
- "&:hover": {
133
- backgroundColor: "rgba(0, 0, 0, 0.04)"
134
- },
135
- "&:active": {
136
- boxShadow: "none"
137
- }
138
- },
139
- clickable: true,
140
- deleteIcon: /* @__PURE__ */ jsx(Icon, { icon: SwapHorizRoundedIcon, color: colors.textBase }),
141
- onDelete: switchPassport,
142
- onClick: switchPassport
143
- }
144
- ),
145
- !isEmpty(user.url) && /* @__PURE__ */ jsx(
146
- Tooltip,
147
- {
148
- title: !loading && translate(translations, "refreshProfile.title", locale),
149
- sx: { display: "flex", alignItems: "center" },
150
- children: loading ? /* @__PURE__ */ jsx(CircularProgress, { size: "16px", sx: { color: theme?.colors?.primary, ml: 0.5 } }) : /* @__PURE__ */ jsx(RefreshOutlinedIcon, { onClick: refreshProfile })
151
- }
152
- )
153
- ] }) : null
154
- ]
155
- }
156
- ),
157
- /* @__PURE__ */ jsx(DID, { did: user.did, showQrcode: true, copyable: true, compact: !showFullDid, responsive: !showFullDid, locale }),
158
- isMyself ? /* @__PURE__ */ jsx(
159
- IconButton,
160
- {
161
- sx: {
162
- borderRadius: 2,
163
- color: colors.textBase,
164
- backgroundColor: "white",
165
- border: `1px solid ${colors.strokeBorderBase}`,
166
- position: "absolute",
167
- top: 0,
168
- right: 0
169
- },
170
- disableFocusRipple: true,
171
- onClick: openSettings,
172
- children: /* @__PURE__ */ jsx(Icon, { icon: SettingsOutlineRoundedIcon })
173
- }
174
- ) : null
93
+ /* @__PURE__ */ jsxs(Box, { children: [
94
+ /* @__PURE__ */ jsxs(
95
+ Typography,
96
+ {
97
+ variant: "h6",
98
+ sx: {
99
+ fontWeight: "bold",
100
+ display: "flex",
101
+ alignItems: "center",
102
+ gap: 1,
103
+ fontSize: "24px !important"
104
+ },
105
+ children: [
106
+ user?.fullName,
107
+ isMyself ? /* @__PURE__ */ jsxs(Fragment, { children: [
108
+ /* @__PURE__ */ jsx(SwitchRole, { user, switchPassport }),
109
+ !isEmpty(user.url) && /* @__PURE__ */ jsx(
110
+ Tooltip,
111
+ {
112
+ title: !loading && translate(translations, "refreshProfile.title", locale),
113
+ sx: { display: "flex", alignItems: "center" },
114
+ children: loading ? /* @__PURE__ */ jsx(CircularProgress, { size: "16px", sx: { color: theme?.colors?.primary, ml: 0.5 } }) : /* @__PURE__ */ jsx(RefreshOutlinedIcon, { onClick: refreshProfile })
115
+ }
116
+ )
117
+ ] }) : null
118
+ ]
119
+ }
120
+ ),
121
+ /* @__PURE__ */ jsx(DID, { did: user.did, showQrcode: true, copyable: true, compact: !showFullDid, responsive: !showFullDid, locale })
122
+ ] })
175
123
  ]
176
124
  }
177
125
  );
@@ -38,6 +38,8 @@ export declare const translations: {
38
38
  cancel: string;
39
39
  required: string;
40
40
  invalid: string;
41
+ setting: string;
42
+ profile: string;
41
43
  };
42
44
  toPublic: string;
43
45
  currentPassport: string;
@@ -130,6 +132,8 @@ export declare const translations: {
130
132
  cancel: string;
131
133
  required: string;
132
134
  invalid: string;
135
+ setting: string;
136
+ profile: string;
133
137
  };
134
138
  toPublic: string;
135
139
  currentPassport: string;
@@ -37,7 +37,9 @@ export const translations = {
37
37
  confirm: "\u786E\u8BA4",
38
38
  cancel: "\u53D6\u6D88",
39
39
  required: "\u5FC5\u586B\u7684",
40
- invalid: "\u65E0\u6548"
40
+ invalid: "\u65E0\u6548",
41
+ setting: "\u8BBE\u7F6E",
42
+ profile: "\u4FE1\u606F"
41
43
  },
42
44
  toPublic: "\u516C\u5F00 \u201C{name}\u201D \u9875\u9762",
43
45
  currentPassport: "\u5F53\u524D\u4F7F\u7528\u7684\u901A\u884C\u8BC1",
@@ -129,7 +131,9 @@ export const translations = {
129
131
  confirm: "Confirm",
130
132
  cancel: "Cancel",
131
133
  required: "Required",
132
- invalid: "Invalid"
134
+ invalid: "Invalid",
135
+ setting: "Settings",
136
+ profile: "Profile"
133
137
  },
134
138
  toPublic: 'Public "{name}" page',
135
139
  currentPassport: "Passport currently in use",
@@ -10,6 +10,11 @@ import LocaleSelector from "@arcblock/ux/lib/Locale/selector";
10
10
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
11
11
  import { SessionManagerProps } from "../types.js";
12
12
  import { getLocalizedNavigation, filterNavByRole } from "../blocklets.js";
13
+ import NotificationAddon from "./notification-addon.js";
14
+ const hasNotification = () => {
15
+ const navigations = window?.blocklet?.navigation ?? [];
16
+ return !!navigations.find((n) => n.id === "/userCenter/notification");
17
+ };
13
18
  export default function HeaderAddons({ formattedBlocklet, addons, sessionManagerProps }) {
14
19
  const sessionCtx = useContext(SessionContext);
15
20
  const { locale } = useLocaleContext() || {};
@@ -22,6 +27,9 @@ export default function HeaderAddons({ formattedBlocklet, addons, sessionManager
22
27
  return Array.isArray(addons) ? addons : [addons];
23
28
  }
24
29
  let addonsArray = [];
30
+ if (hasNotification()) {
31
+ addonsArray.push(/* @__PURE__ */ jsx(NotificationAddon, { session: sessionCtx.session }, "notification-addon"));
32
+ }
25
33
  if (enableLocale && locale) {
26
34
  addonsArray.push(/* @__PURE__ */ jsx(LocaleSelector, { showText: false }, "locale-selector"));
27
35
  }
@@ -0,0 +1,13 @@
1
+ declare function NotificationAddon({ session }: {
2
+ session?: {} | undefined;
3
+ }): import("react").JSX.Element | null;
4
+ declare namespace NotificationAddon {
5
+ namespace propTypes {
6
+ let session: any;
7
+ }
8
+ namespace defaultProps {
9
+ let session_1: {};
10
+ export { session_1 as session };
11
+ }
12
+ }
13
+ export default NotificationAddon;
@@ -0,0 +1,41 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import Badge from "@mui/material/Badge";
3
+ import PropTypes from "prop-types";
4
+ import { temp as colors } from "@arcblock/ux/lib/Colors";
5
+ import { IconButton } from "@mui/material";
6
+ import NotificationsOutlinedIcon from "@arcblock/icons/lib/Notification";
7
+ import { useCreation } from "ahooks";
8
+ export default function NotificationAddon({ session = {} }) {
9
+ const { unReadCount } = session;
10
+ const viewAllUrl = useCreation(() => {
11
+ const { navigation } = window.blocklet ?? {};
12
+ const notification = navigation?.find((x) => x.id === "/userCenter/notification");
13
+ return notification?.link;
14
+ });
15
+ if (!session.user || !viewAllUrl) {
16
+ return null;
17
+ }
18
+ return /* @__PURE__ */ jsx(
19
+ IconButton,
20
+ {
21
+ size: "medium",
22
+ variant: "outlined",
23
+ href: viewAllUrl,
24
+ sx: {
25
+ borderRadius: 2,
26
+ color: "rgba(0, 0, 0, 0.6)",
27
+ borderColor: colors.lineBorderStrong,
28
+ "&:hover": {
29
+ borderRadius: "50%"
30
+ }
31
+ },
32
+ children: /* @__PURE__ */ jsx(Badge, { variant: "dot", color: "error", invisible: unReadCount === 0, children: /* @__PURE__ */ jsx(NotificationsOutlinedIcon, { style: { width: "auto", height: 24 } }) })
33
+ }
34
+ );
35
+ }
36
+ NotificationAddon.propTypes = {
37
+ session: PropTypes.object
38
+ };
39
+ NotificationAddon.defaultProps = {
40
+ session: {}
41
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/ui-react",
3
- "version": "2.10.90",
3
+ "version": "2.11.0",
4
4
  "description": "Some useful front-end web components that can be used in Blocklets.",
5
5
  "keywords": [
6
6
  "react",
@@ -32,8 +32,8 @@
32
32
  "url": "https://github.com/ArcBlock/ux/issues"
33
33
  },
34
34
  "dependencies": {
35
- "@arcblock/bridge": "^2.10.90",
36
- "@arcblock/react-hooks": "^2.10.90",
35
+ "@arcblock/bridge": "^2.11.0",
36
+ "@arcblock/react-hooks": "^2.11.0",
37
37
  "@blocklet/did-space-react": "^0.5.81",
38
38
  "@iconify-icons/logos": "^1.2.36",
39
39
  "@iconify-icons/material-symbols": "^1.2.58",
@@ -80,5 +80,5 @@
80
80
  "jest": "^29.7.0",
81
81
  "unbuild": "^2.0.0"
82
82
  },
83
- "gitHead": "435dd00e8c7987da0293e31cf1496ff3e9c82bc2"
83
+ "gitHead": "c07858fa8f3721cc7ba060e70e1577ae6a509bbb"
84
84
  }
@@ -69,6 +69,7 @@ export type UserCenterTab = {
69
69
  url: string;
70
70
  protected: boolean;
71
71
  icon?: string;
72
+ isPrivate?: boolean; // 如果为 true 则不应该出现在隐私设置中
72
73
  };
73
74
 
74
75
  export type Session = {
@@ -62,7 +62,9 @@ export default function ConfigProfile({ user, onSave }: { user: User; onSave: (t
62
62
  />
63
63
  <Select fullWidth value={currentState.locale} onChange={handleChange} size="small">
64
64
  {(window.blocklet.languages || languages).map((x: { name: string; code: string }) => (
65
- <MenuItem value={x.code}>{x.name}</MenuItem>
65
+ <MenuItem value={x.code} key={x.code}>
66
+ {x.name}
67
+ </MenuItem>
66
68
  ))}
67
69
  </Select>
68
70
  </Box>
@@ -1,5 +1,5 @@
1
1
  import { ChangeEvent } from 'react';
2
- import { Alert, Box, CircularProgress, Divider, Stack, Typography } from '@mui/material';
2
+ import { Alert, Box, CircularProgress, Stack, Typography } from '@mui/material';
3
3
  import { useCreation, useMemoizedFn, useReactive, useRequest } from 'ahooks';
4
4
  import { Icon } from '@iconify/react';
5
5
  import AddRoundedIcon from '@iconify-icons/material-symbols/add-rounded';
@@ -240,7 +240,6 @@ export default function Notification({ user }: { user: User }) {
240
240
  onChange={(event: ChangeEvent<HTMLInputElement>) => handleChangeSwitch('phone', event.target.checked)}
241
241
  /> */}
242
242
  </Box>
243
- <Divider />
244
243
  <Stack spacing={1.5} useFlexGap direction="column" alignItems="start">
245
244
  {webhooks.map((item: WebhookItemData, index: number) => (
246
245
  <WebhookItem
@@ -15,6 +15,7 @@ type PrivacyConfig = {
15
15
  key: string;
16
16
  name: string;
17
17
  value: boolean;
18
+ isPrivate?: boolean;
18
19
  };
19
20
 
20
21
  export default function Privacy({
@@ -86,6 +87,9 @@ export default function Privacy({
86
87
  },
87
88
  }}>
88
89
  {dataList.map((item) => {
90
+ if (item.isPrivate) {
91
+ return null;
92
+ }
89
93
  return (
90
94
  <Switch
91
95
  key={item.key}
@@ -1,10 +1,10 @@
1
- import Tabs from '@arcblock/ux/lib/Tabs';
2
- import { Box } from '@mui/material';
1
+ import { Box, Divider, Typography } from '@mui/material';
3
2
  import type { BoxProps } from '@mui/material';
4
- import { useCreation, useMemoizedFn, useReactive } from 'ahooks';
3
+ import { useCreation, useMemoizedFn } from 'ahooks';
5
4
  import { translate } from '@arcblock/ux/lib/Locale/util';
6
5
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
7
6
 
7
+ import colors from '@arcblock/ux/lib/Colors/themes/temp';
8
8
  import { ConfigUserSpaceProvider } from '../../contexts/config-user-space';
9
9
  import { translations } from '../libs/locales';
10
10
  import Notification from './notification';
@@ -38,6 +38,7 @@ export default function Settings({
38
38
  key: item.value,
39
39
  name: item.label,
40
40
  value: item.protected,
41
+ isPrivate: item.isPrivate,
41
42
  };
42
43
  });
43
44
  }, [settings?.userCenterTabs]);
@@ -82,31 +83,30 @@ export default function Settings({
82
83
  },
83
84
  ].filter(Boolean);
84
85
  }, [user, privacyConfigList]);
85
- const currentState = useReactive({
86
- tab: tabs[0].value,
87
- });
88
-
89
- const currentTab = useCreation(() => {
90
- return tabs.find((x) => x.value === currentState.tab);
91
- }, [currentState.tab, tabs]);
92
- const handleChangeTab = useMemoizedFn((value) => {
93
- currentState.tab = value;
94
- });
95
86
 
96
87
  return (
97
- currentTab && (
98
- <Box
99
- {...rest}
100
- sx={{
101
- ...rest?.sx,
102
- minWidth: {
103
- md: 500,
104
- },
105
- maxWidth: '100%',
106
- }}>
107
- <Tabs variant="card" tabs={tabs} current={currentTab.value} onChange={handleChangeTab} />
108
- <Box mt={2.5}>{currentTab.content}</Box>
109
- </Box>
110
- )
88
+ <Box
89
+ {...rest}
90
+ sx={{
91
+ ...rest?.sx,
92
+ minWidth: {
93
+ md: 500,
94
+ },
95
+ maxWidth: '100%',
96
+ }}>
97
+ {tabs.map((tab, index) => (
98
+ <Box key={tab.value}>
99
+ <Typography
100
+ sx={{
101
+ color: colors.foregroundsFgBase,
102
+ fontWeight: 600,
103
+ }}>
104
+ {tab.label}
105
+ </Typography>
106
+ <Box mt={2.5}>{tab.content}</Box>
107
+ {index < tabs.length - 1 ? <Divider sx={{ mt: 2.5, mb: 2.5 }} /> : null}
108
+ </Box>
109
+ ))}
110
+ </Box>
111
111
  );
112
112
  }
@@ -5,6 +5,7 @@ import { getConnectedAccounts, getSourceProvider } from '@arcblock/ux/lib/Sessio
5
5
  import { LOGIN_PROVIDER } from '@arcblock/ux/lib/Util/constant';
6
6
  import { SessionContext } from '@arcblock/did-connect/lib/Session';
7
7
 
8
+ import Empty from '@arcblock/ux/lib/Empty';
8
9
  import type { ConnectedAccount, OAuthAccount, User, SessionContext as TSessionContext } from '../../../@types';
9
10
  import ThirdPartyItem from './third-party-item';
10
11
 
@@ -124,9 +125,15 @@ export default function ThirdPartyLogin({ user }: { user: User }) {
124
125
  display: 'grid',
125
126
  gridTemplateColumns: '1fr min-content',
126
127
  }}>
127
- {normalizedAccounts.map((account) => {
128
- return <ThirdPartyItem key={account?.provider} item={account as OAuthAccount} />;
129
- })}
128
+ {!normalizedAccounts.length ? (
129
+ <Empty />
130
+ ) : (
131
+ <>
132
+ {normalizedAccounts.map((account) => {
133
+ return <ThirdPartyItem key={account?.provider} item={account as OAuthAccount} />;
134
+ })}
135
+ </>
136
+ )}
130
137
  </Box>
131
138
  );
132
139
  }