@blocklet/ui-react 3.4.14 → 3.5.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 (256) hide show
  1. package/lib/common/org-switch/use-org.d.ts +4 -4
  2. package/lib/common/ws.d.ts +22 -1
  3. package/package.json +10 -7
  4. package/.aigne/doc-smith/.local/afs-storage.sqlite3 +0 -0
  5. package/.aigne/doc-smith/config.yaml +0 -78
  6. package/.aigne/doc-smith/history.yaml +0 -14
  7. package/.aigne/doc-smith/media-description.yaml +0 -11
  8. package/.aigne/doc-smith/output/structure-plan.json +0 -255
  9. package/.aigne/doc-smith/translation-cache.yaml +0 -11
  10. package/.aigne/doc-smith/upload-cache.yaml +0 -528
  11. package/build.config.ts +0 -24
  12. package/docs/_sidebar.md +0 -19
  13. package/docs/assets/diagram/component-installer-diagram-0.ja.jpg +0 -0
  14. package/docs/assets/diagram/component-installer-diagram-0.jpg +0 -0
  15. package/docs/assets/diagram/component-installer-diagram-0.zh-TW.jpg +0 -0
  16. package/docs/assets/diagram/component-installer-diagram-0.zh.jpg +0 -0
  17. package/docs/assets/diagram/component-management-diagram-0.ja.jpg +0 -0
  18. package/docs/assets/diagram/component-management-diagram-0.jpg +0 -0
  19. package/docs/assets/diagram/component-management-diagram-0.zh-TW.jpg +0 -0
  20. package/docs/assets/diagram/component-management-diagram-0.zh.jpg +0 -0
  21. package/docs/assets/diagram/core-concepts-diagram-0.ja.jpg +0 -0
  22. package/docs/assets/diagram/core-concepts-diagram-0.jpg +0 -0
  23. package/docs/assets/diagram/core-concepts-diagram-0.zh-TW.jpg +0 -0
  24. package/docs/assets/diagram/core-concepts-diagram-0.zh.jpg +0 -0
  25. package/docs/assets/diagram/dashboard-diagram-0.ja.jpg +0 -0
  26. package/docs/assets/diagram/dashboard-diagram-0.jpg +0 -0
  27. package/docs/assets/diagram/dashboard-diagram-0.zh-TW.jpg +0 -0
  28. package/docs/assets/diagram/dashboard-diagram-0.zh.jpg +0 -0
  29. package/docs/assets/diagram/header-diagram-0.ja.jpg +0 -0
  30. package/docs/assets/diagram/header-diagram-0.jpg +0 -0
  31. package/docs/assets/diagram/header-diagram-0.zh-TW.jpg +0 -0
  32. package/docs/assets/diagram/header-diagram-0.zh.jpg +0 -0
  33. package/docs/assets/diagram/layout-diagram-0.ja.jpg +0 -0
  34. package/docs/assets/diagram/layout-diagram-0.jpg +0 -0
  35. package/docs/assets/diagram/layout-diagram-0.zh-TW.jpg +0 -0
  36. package/docs/assets/diagram/layout-diagram-0.zh.jpg +0 -0
  37. package/docs/assets/diagram/notifications-diagram-0.ja.jpg +0 -0
  38. package/docs/assets/diagram/notifications-diagram-0.jpg +0 -0
  39. package/docs/assets/diagram/notifications-diagram-0.zh-TW.jpg +0 -0
  40. package/docs/assets/diagram/notifications-diagram-0.zh.jpg +0 -0
  41. package/docs/assets/diagram/overview-diagram-0.ja.jpg +0 -0
  42. package/docs/assets/diagram/overview-diagram-0.jpg +0 -0
  43. package/docs/assets/diagram/overview-diagram-0.zh-TW.jpg +0 -0
  44. package/docs/assets/diagram/overview-diagram-0.zh.jpg +0 -0
  45. package/docs/assets/diagram/user-center-diagram-0.ja.jpg +0 -0
  46. package/docs/assets/diagram/user-center-diagram-0.jpg +0 -0
  47. package/docs/assets/diagram/user-center-diagram-0.zh-TW.jpg +0 -0
  48. package/docs/assets/diagram/user-center-diagram-0.zh.jpg +0 -0
  49. package/docs/assets/diagram/user-management-diagram-0.ja.jpg +0 -0
  50. package/docs/assets/diagram/user-management-diagram-0.jpg +0 -0
  51. package/docs/assets/diagram/user-management-diagram-0.zh-TW.jpg +0 -0
  52. package/docs/assets/diagram/user-management-diagram-0.zh.jpg +0 -0
  53. package/docs/assets/diagram/user-sessions-diagram-0.ja.jpg +0 -0
  54. package/docs/assets/diagram/user-sessions-diagram-0.jpg +0 -0
  55. package/docs/assets/diagram/user-sessions-diagram-0.zh-TW.jpg +0 -0
  56. package/docs/assets/diagram/user-sessions-diagram-0.zh.jpg +0 -0
  57. package/docs/components-component-management-blocklet-studio.ja.md +0 -194
  58. package/docs/components-component-management-blocklet-studio.md +0 -194
  59. package/docs/components-component-management-blocklet-studio.zh-TW.md +0 -194
  60. package/docs/components-component-management-blocklet-studio.zh.md +0 -194
  61. package/docs/components-component-management-component-installer.ja.md +0 -182
  62. package/docs/components-component-management-component-installer.md +0 -182
  63. package/docs/components-component-management-component-installer.zh-TW.md +0 -182
  64. package/docs/components-component-management-component-installer.zh.md +0 -182
  65. package/docs/components-component-management.ja.md +0 -30
  66. package/docs/components-component-management.md +0 -30
  67. package/docs/components-component-management.zh-TW.md +0 -30
  68. package/docs/components-component-management.zh.md +0 -30
  69. package/docs/components-layout-dashboard.ja.md +0 -185
  70. package/docs/components-layout-dashboard.md +0 -187
  71. package/docs/components-layout-dashboard.zh-TW.md +0 -185
  72. package/docs/components-layout-dashboard.zh.md +0 -185
  73. package/docs/components-layout-footer.ja.md +0 -165
  74. package/docs/components-layout-footer.md +0 -165
  75. package/docs/components-layout-footer.zh-TW.md +0 -165
  76. package/docs/components-layout-footer.zh.md +0 -165
  77. package/docs/components-layout-header.ja.md +0 -183
  78. package/docs/components-layout-header.md +0 -183
  79. package/docs/components-layout-header.zh-TW.md +0 -183
  80. package/docs/components-layout-header.zh.md +0 -183
  81. package/docs/components-layout.ja.md +0 -31
  82. package/docs/components-layout.md +0 -31
  83. package/docs/components-layout.zh-TW.md +0 -31
  84. package/docs/components-layout.zh.md +0 -31
  85. package/docs/components-notifications.ja.md +0 -125
  86. package/docs/components-notifications.md +0 -125
  87. package/docs/components-notifications.zh-TW.md +0 -125
  88. package/docs/components-notifications.zh.md +0 -125
  89. package/docs/components-user-management-user-center.ja.md +0 -148
  90. package/docs/components-user-management-user-center.md +0 -147
  91. package/docs/components-user-management-user-center.zh-TW.md +0 -148
  92. package/docs/components-user-management-user-center.zh.md +0 -148
  93. package/docs/components-user-management-user-sessions.ja.md +0 -121
  94. package/docs/components-user-management-user-sessions.md +0 -123
  95. package/docs/components-user-management-user-sessions.zh-TW.md +0 -121
  96. package/docs/components-user-management-user-sessions.zh.md +0 -121
  97. package/docs/components-user-management.ja.md +0 -49
  98. package/docs/components-user-management.md +0 -51
  99. package/docs/components-user-management.zh-TW.md +0 -49
  100. package/docs/components-user-management.zh.md +0 -49
  101. package/docs/components-utilities-icon.ja.md +0 -106
  102. package/docs/components-utilities-icon.md +0 -106
  103. package/docs/components-utilities-icon.zh-TW.md +0 -106
  104. package/docs/components-utilities-icon.zh.md +0 -106
  105. package/docs/components-utilities.ja.md +0 -136
  106. package/docs/components-utilities.md +0 -136
  107. package/docs/components-utilities.zh-TW.md +0 -136
  108. package/docs/components-utilities.zh.md +0 -136
  109. package/docs/components.ja.md +0 -27
  110. package/docs/components.md +0 -27
  111. package/docs/components.zh-TW.md +0 -27
  112. package/docs/components.zh.md +0 -27
  113. package/docs/core-concepts.ja.md +0 -134
  114. package/docs/core-concepts.md +0 -135
  115. package/docs/core-concepts.zh-TW.md +0 -134
  116. package/docs/core-concepts.zh.md +0 -134
  117. package/docs/getting-started.ja.md +0 -132
  118. package/docs/getting-started.md +0 -132
  119. package/docs/getting-started.zh-TW.md +0 -132
  120. package/docs/getting-started.zh.md +0 -132
  121. package/docs/hooks-api.ja.md +0 -214
  122. package/docs/hooks-api.md +0 -214
  123. package/docs/hooks-api.zh-TW.md +0 -214
  124. package/docs/hooks-api.zh.md +0 -214
  125. package/docs/how-to-guides.ja.md +0 -413
  126. package/docs/how-to-guides.md +0 -413
  127. package/docs/how-to-guides.zh-TW.md +0 -413
  128. package/docs/how-to-guides.zh.md +0 -413
  129. package/docs/overview.ja.md +0 -51
  130. package/docs/overview.md +0 -51
  131. package/docs/overview.zh-TW.md +0 -51
  132. package/docs/overview.zh.md +0 -51
  133. package/glossary.md +0 -12
  134. package/src/@types/index.ts +0 -230
  135. package/src/@types/shims.d.ts +0 -18
  136. package/src/BlockletStudio/README.md +0 -116
  137. package/src/BlockletStudio/index.tsx +0 -145
  138. package/src/ComponentInstaller/ComponentInstaller.stories.jsx +0 -16
  139. package/src/ComponentInstaller/index.jsx +0 -207
  140. package/src/ComponentInstaller/installer-item.jsx +0 -129
  141. package/src/ComponentInstaller/locales.js +0 -22
  142. package/src/ComponentInstaller/use-component-installed.js +0 -88
  143. package/src/ComponentManager/components/add-component.tsx +0 -136
  144. package/src/ComponentManager/components/check-component.tsx +0 -3
  145. package/src/ComponentManager/components/publish-component.tsx +0 -90
  146. package/src/ComponentManager/components/resource-dialog.tsx +0 -91
  147. package/src/ComponentManager/index.tsx +0 -3
  148. package/src/ComponentManager/libs/locales.ts +0 -15
  149. package/src/Dashboard/Dashboard.stories.jsx +0 -20
  150. package/src/Dashboard/app-shell/app-badge.stories.tsx +0 -64
  151. package/src/Dashboard/app-shell/app-badge.tsx +0 -94
  152. package/src/Dashboard/app-shell/app-header.tsx +0 -104
  153. package/src/Dashboard/app-shell/app-info-context.tsx +0 -182
  154. package/src/Dashboard/app-shell/badges/app-badge-default.tsx +0 -130
  155. package/src/Dashboard/app-shell/badges/app-badge-did.tsx +0 -28
  156. package/src/Dashboard/app-shell/badges/app-badge-state.tsx +0 -40
  157. package/src/Dashboard/app-shell/badges/app-badge-switch.tsx +0 -72
  158. package/src/Dashboard/app-shell/badges/app-badge-version.tsx +0 -60
  159. package/src/Dashboard/app-shell/index.ts +0 -5
  160. package/src/Dashboard/index.jsx +0 -184
  161. package/src/Footer/Footer.stories.jsx +0 -33
  162. package/src/Footer/brand.jsx +0 -81
  163. package/src/Footer/copyright.jsx +0 -22
  164. package/src/Footer/index.jsx +0 -111
  165. package/src/Footer/internal-footer.jsx +0 -139
  166. package/src/Footer/layout/plain.jsx +0 -55
  167. package/src/Footer/layout/row.jsx +0 -43
  168. package/src/Footer/layout/standard.jsx +0 -114
  169. package/src/Footer/links.jsx +0 -321
  170. package/src/Footer/social-media.jsx +0 -55
  171. package/src/Header/Header.stories.jsx +0 -30
  172. package/src/Header/index.tsx +0 -259
  173. package/src/Icon/Icon.stories.jsx +0 -12
  174. package/src/Icon/index.tsx +0 -87
  175. package/src/Notifications/Snackbar.tsx +0 -261
  176. package/src/Notifications/hooks/use-title.tsx +0 -254
  177. package/src/Notifications/hooks/use-width.tsx +0 -16
  178. package/src/Notifications/utils.ts +0 -246
  179. package/src/UserCenter/assets/banner.png +0 -0
  180. package/src/UserCenter/components/config-inviter.tsx +0 -48
  181. package/src/UserCenter/components/config-profile.tsx +0 -99
  182. package/src/UserCenter/components/danger-zone.tsx +0 -82
  183. package/src/UserCenter/components/editable-field.tsx +0 -273
  184. package/src/UserCenter/components/fallback.tsx +0 -57
  185. package/src/UserCenter/components/nft-preview.tsx +0 -84
  186. package/src/UserCenter/components/nft.tsx +0 -279
  187. package/src/UserCenter/components/notification.tsx +0 -319
  188. package/src/UserCenter/components/passport.tsx +0 -107
  189. package/src/UserCenter/components/privacy.tsx +0 -120
  190. package/src/UserCenter/components/settings.tsx +0 -170
  191. package/src/UserCenter/components/status-dialog/date-picker.tsx +0 -77
  192. package/src/UserCenter/components/status-dialog/index.tsx +0 -293
  193. package/src/UserCenter/components/status-selector/duration-menu.tsx +0 -90
  194. package/src/UserCenter/components/status-selector/index.tsx +0 -58
  195. package/src/UserCenter/components/status-selector/menu-item.tsx +0 -56
  196. package/src/UserCenter/components/storage/action.tsx +0 -49
  197. package/src/UserCenter/components/storage/connected.tsx +0 -61
  198. package/src/UserCenter/components/storage/delete.tsx +0 -72
  199. package/src/UserCenter/components/storage/disconnect.tsx +0 -40
  200. package/src/UserCenter/components/storage/icons/empty-spaces-nft.svg +0 -1
  201. package/src/UserCenter/components/storage/icons/long-arrow.svg +0 -5
  202. package/src/UserCenter/components/storage/icons/space-connected.svg +0 -3
  203. package/src/UserCenter/components/storage/icons/space-disconnect.svg +0 -3
  204. package/src/UserCenter/components/storage/index.tsx +0 -41
  205. package/src/UserCenter/components/storage/preview-nft.tsx +0 -72
  206. package/src/UserCenter/components/third-party-login/index.tsx +0 -199
  207. package/src/UserCenter/components/third-party-login/third-party-item.tsx +0 -296
  208. package/src/UserCenter/components/user-center.tsx +0 -787
  209. package/src/UserCenter/components/user-info/address.tsx +0 -143
  210. package/src/UserCenter/components/user-info/index.tsx +0 -4
  211. package/src/UserCenter/components/user-info/link-preview-input.tsx +0 -274
  212. package/src/UserCenter/components/user-info/metadata.tsx +0 -658
  213. package/src/UserCenter/components/user-info/social-actions/chat.tsx +0 -43
  214. package/src/UserCenter/components/user-info/social-actions/follow.tsx +0 -23
  215. package/src/UserCenter/components/user-info/social-actions/index.tsx +0 -17
  216. package/src/UserCenter/components/user-info/switch-role.tsx +0 -42
  217. package/src/UserCenter/components/user-info/timezone-select.tsx +0 -119
  218. package/src/UserCenter/components/user-info/user-basic-info.tsx +0 -292
  219. package/src/UserCenter/components/user-info/user-info-item.tsx +0 -54
  220. package/src/UserCenter/components/user-info/user-info.tsx +0 -91
  221. package/src/UserCenter/components/user-info/user-status.tsx +0 -234
  222. package/src/UserCenter/components/user-info/utils.ts +0 -320
  223. package/src/UserCenter/components/webhook-item.tsx +0 -248
  224. package/src/UserCenter/index.tsx +0 -1
  225. package/src/UserCenter/libs/locales.ts +0 -378
  226. package/src/UserCenter/libs/utils.ts +0 -30
  227. package/src/UserSessions/components/user-session-info.tsx +0 -78
  228. package/src/UserSessions/components/user-sessions.tsx +0 -545
  229. package/src/UserSessions/index.tsx +0 -1
  230. package/src/UserSessions/libs/locales.ts +0 -60
  231. package/src/UserSessions/libs/utils.ts +0 -82
  232. package/src/blocklets.js +0 -195
  233. package/src/common/domain-warning.jsx +0 -178
  234. package/src/common/header-addons.jsx +0 -119
  235. package/src/common/link-blocker.jsx +0 -20
  236. package/src/common/notification-addon.jsx +0 -135
  237. package/src/common/org-switch/avatar-uploader.jsx +0 -271
  238. package/src/common/org-switch/create.jsx +0 -267
  239. package/src/common/org-switch/index.jsx +0 -407
  240. package/src/common/org-switch/locales.js +0 -52
  241. package/src/common/org-switch/use-org.jsx +0 -79
  242. package/src/common/overridable-theme-provider.jsx +0 -17
  243. package/src/common/wallet-hidden-topbar.js +0 -14
  244. package/src/common/wizard-modal.jsx +0 -200
  245. package/src/common/ws.js +0 -68
  246. package/src/contexts/config-user-space.tsx +0 -88
  247. package/src/contexts/user-followers.tsx +0 -54
  248. package/src/hooks/use-follow.tsx +0 -75
  249. package/src/hooks/use-mobile.tsx +0 -6
  250. package/src/index.ts +0 -16
  251. package/src/libs/constant.ts +0 -1
  252. package/src/libs/spaces.tsx +0 -18
  253. package/src/libs/with-hide-when-embed.tsx +0 -24
  254. package/src/types.js +0 -45
  255. package/src/utils.js +0 -161
  256. package/vite.config.mjs +0 -34
@@ -1,787 +0,0 @@
1
- import { Children, cloneElement, isValidElement, use, useMemo } from 'react';
2
- import { Box, CircularProgress, Divider, Typography } from '@mui/material';
3
- import type { BoxProps, Theme } from '@mui/material';
4
- import { useCreation, useMemoizedFn, useRequest } from 'ahooks';
5
- import pWaitFor from 'p-wait-for';
6
- import Helmet from 'react-helmet';
7
-
8
- import { SessionContext } from '@arcblock/did-connect-react/lib/Session';
9
- import Tabs from '@arcblock/ux/lib/Tabs';
10
- import Empty from '@arcblock/ux/lib/Empty';
11
- import Button from '@arcblock/ux/lib/Button';
12
- import Result from '@arcblock/ux/lib/Result';
13
- import { useConfirm } from '@arcblock/ux/lib/Dialog';
14
- import { translate } from '@arcblock/ux/lib/Locale/util';
15
- import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
16
- import { ErrorFallback } from '@arcblock/ux/lib/ErrorBoundary';
17
- import { styled } from '@arcblock/ux/lib/Theme';
18
- import cloneDeep from 'lodash/cloneDeep';
19
- import { getQuery, withQuery, joinURL, withoutTrailingSlash, parseURL } from 'ufo';
20
- import type { AxiosError } from 'axios';
21
- import { getBlockletSDK, type UserPublicInfo } from '@blocklet/js-sdk';
22
- import { isSupportFollow } from '@arcblock/ux/lib/Util';
23
-
24
- import { PROFILE_URL } from '@arcblock/ux/lib/Util/constant';
25
- import Footer from '../../Footer';
26
- import Header from '../../Header';
27
- import { translations } from '../libs/locales';
28
- import { UserBasicInfo } from './user-info';
29
- import type { SessionContext as TSessionContext, User, UserCenterTab } from '../../@types';
30
- // @ts-ignore
31
- import { formatBlockletInfo, getLink, getLocalizedNavigation } from '../../blocklets';
32
- import Passport from './passport';
33
- import Settings from './settings';
34
- import useMobile from '../../hooks/use-mobile';
35
- import { ConfigUserSpaceProvider } from '../../contexts/config-user-space';
36
- import DidSpace from './storage';
37
- import Nft from './nft';
38
- import { UserFollowersProvider } from '../../contexts/user-followers';
39
- import Fallback from './fallback';
40
-
41
- const nftsLink = joinURL(PROFILE_URL, '/nfts');
42
- const settingsLink = joinURL(PROFILE_URL, '/settings');
43
- const didSpacesLink = joinURL(PROFILE_URL, '/did-spaces');
44
- const userFollowersLink = joinURL(PROFILE_URL, '/user-followers');
45
-
46
- const hasSystemMenusInjected = (menus: Array<any>) => {
47
- return menus.some(
48
- (menu: any) =>
49
- ['/userCenter/nfts', '/userCenter/user-followers', '/userCenter/settings', '/userCenter/did-spaces'].includes(
50
- menu.id
51
- ) || [nftsLink, settingsLink, didSpacesLink, userFollowersLink].includes(menu.link)
52
- );
53
- };
54
-
55
- interface NavigationTabProps {
56
- label: string;
57
- protected: boolean;
58
- isPrivate: boolean;
59
- value: string;
60
- url: string;
61
- [key: string]: any;
62
- }
63
-
64
- const Main = styled(Box)(({ theme }) => ({
65
- flex: 1,
66
- boxSizing: 'border-box',
67
- padding: '0 16px',
68
- width: '100%',
69
- maxWidth: 1600,
70
- margin: '0 auto',
71
- display: 'flex',
72
- alignItems: 'stretch',
73
- gap: 2.5,
74
- flexDirection: 'column',
75
- [theme.breakpoints.up('md')]: {
76
- flexDirection: 'row',
77
- },
78
- }));
79
-
80
- const ContentWrapper = styled(Box)(({ theme }) => ({
81
- overflow: 'hidden',
82
- flex: 'revert',
83
- [theme.breakpoints.up('md')]: {
84
- flex: 1,
85
- },
86
- '@media (min-width: 960px) and (max-width: 1050px)': {
87
- '& .user-center-tabs': {
88
- maxWidth: '500px',
89
- },
90
- },
91
- }));
92
-
93
- export default function UserCenter({
94
- children,
95
- notLoginContent = null,
96
- currentTab,
97
- contentProps = {},
98
- disableAutoRedirect = false,
99
- hideFooter = false,
100
- headerProps = {},
101
- footerProps = {},
102
- userDid = undefined,
103
- stickySidebar = false,
104
- embed = false,
105
- onlyProfile = false, // 只显示 profile 页面,用于 ArcSphere 只需要显示 Profile 的内容
106
- onDestroySelf = undefined,
107
- }: {
108
- readonly children: any;
109
- readonly notLoginContent?: any;
110
- readonly currentTab: string;
111
- readonly contentProps?: BoxProps;
112
- readonly disableAutoRedirect?: boolean;
113
- readonly autoPopupSetting?: boolean;
114
- readonly hideFooter?: boolean;
115
- // FIXME: @zhanghan 将 header 和 footer 改为 ts 后,去掉这个 any
116
- readonly headerProps?: any;
117
- readonly footerProps?: any;
118
- readonly userDid?: string;
119
- readonly stickySidebar?: boolean;
120
- readonly embed?: boolean;
121
- readonly onlyProfile?: boolean;
122
- readonly onDestroySelf?: () => void;
123
- }) {
124
- const client = getBlockletSDK();
125
- const { locale, defaultLocale } = useLocaleContext();
126
- const isMobile = useMobile({ key: 'md' });
127
- const t = useMemoizedFn((key, data = {}) => {
128
- return translate(translations, key, locale, 'en', data);
129
- });
130
- const sessionCtx: TSessionContext = use(SessionContext);
131
- const session = sessionCtx?.session;
132
-
133
- const currentDid = useCreation(() => {
134
- if (userDid) {
135
- return userDid;
136
- }
137
-
138
- const currentUrl = window.location.href;
139
- const query = getQuery(currentUrl);
140
- if (query?.did) {
141
- if (Array.isArray(query.did)) {
142
- return query.did[0];
143
- }
144
- return query.did;
145
- }
146
- return session?.user?.did;
147
- }, [session?.user?.did, userDid]);
148
-
149
- const isMyself = useCreation(() => {
150
- if (session?.user) {
151
- return currentDid === session?.user?.did;
152
- }
153
- return false;
154
- }, [currentDid, session?.user?.did]);
155
-
156
- const isShowSocialActions = useMemo((): boolean => {
157
- return !!session?.user && isSupportFollow();
158
- }, [session?.user]);
159
-
160
- const userState = useRequest<UserPublicInfo | User | undefined, []>(
161
- // eslint-disable-next-line consistent-return
162
- async () => {
163
- await pWaitFor(() => session?.initialized);
164
- if (isMyself) {
165
- return session.user as User;
166
- }
167
- if (currentDid) {
168
- return client.user.getUserPublicInfo({ did: currentDid });
169
- }
170
- },
171
- {
172
- refreshDeps: [currentDid, isMyself, session?.initialized, session?.user],
173
- }
174
- );
175
-
176
- const onRefreshUser = useMemoizedFn(() => {
177
- if (isMyself) {
178
- return session.refresh();
179
- }
180
- return userState.refresh();
181
- });
182
-
183
- const privacyState = useRequest(
184
- async () => {
185
- if (userState.data && currentTab) {
186
- const config = await client.user.getUserPrivacyConfig({ did: currentDid as string });
187
- return config;
188
- }
189
- return null;
190
- },
191
- {
192
- refreshDeps: [currentDid, userState.data, currentTab],
193
- loadingDelay: 300,
194
- }
195
- );
196
-
197
- const { confirmHolder } = useConfirm({
198
- fullScreen: isMobile,
199
- sx: {
200
- '.MuiDialog-paper': {
201
- borderRadius: 1,
202
- maxWidth: 1200,
203
- },
204
- '.ux-dialog_title': {
205
- fontWeight: 600,
206
- },
207
- '.ux-dialog_closeButton': {
208
- p: 1,
209
- },
210
- '.MuiDialogActions-root': {
211
- display: {
212
- xs: 'none',
213
- md: 'flex',
214
- },
215
- },
216
- },
217
- });
218
-
219
- const formattedBlocklet = useCreation(() => {
220
- const blocklet = cloneDeep(window.blocklet);
221
- try {
222
- return formatBlockletInfo(blocklet);
223
- } catch (e) {
224
- console.error('Failed to format blocklet info', e, blocklet);
225
- return blocklet;
226
- }
227
- }, []);
228
-
229
- const defaultTabs = useCreation((): NavigationTabProps[] => {
230
- const nftTab = {
231
- label: t('common.nft'),
232
- protected: false,
233
- isPrivate: false, // true: 隐私数据,仅自己可见
234
- value: nftsLink,
235
- url: getLink(nftsLink, locale, defaultLocale),
236
- };
237
- const userFollowersTab = {
238
- label: t('userFollowers'),
239
- protected: false,
240
- isPrivate: false,
241
- value: userFollowersLink,
242
- url: getLink(userFollowersLink, locale, defaultLocale),
243
- };
244
- let tabs: NavigationTabProps[] = isShowSocialActions ? [nftTab, userFollowersTab] : [nftTab];
245
- if (isMyself) {
246
- const restTabs = [
247
- nftTab,
248
- {
249
- label: t('common.setting'),
250
- protected: true,
251
- isPrivate: true,
252
- value: settingsLink,
253
- url: getLink(settingsLink, locale, defaultLocale),
254
- },
255
- {
256
- label: t('storageManagement'),
257
- protected: true,
258
- isPrivate: true,
259
- value: didSpacesLink,
260
- url: getLink(didSpacesLink, locale, defaultLocale),
261
- },
262
- ];
263
- tabs = isShowSocialActions ? [...restTabs, userFollowersTab] : restTabs;
264
- }
265
- return tabs;
266
- }, [isMyself, locale, isShowSocialActions]);
267
-
268
- const userCenterTabs = useCreation<UserCenterTab[]>(() => {
269
- const menus = formattedBlocklet?.navigation?.userCenter || [];
270
- const hasSystemMenus = hasSystemMenusInjected(menus);
271
- const localizedMenus = getLocalizedNavigation({ navigation: menus, locale, defaultLocale }) || [];
272
- const allMenus = localizedMenus.concat(hasSystemMenus ? [] : defaultTabs);
273
- return allMenus
274
- .map((x: any) => {
275
- const value = x.value ?? x._rawLink ?? x.link ?? x.url;
276
-
277
- return {
278
- value,
279
- label: x.title || x.label,
280
- url: x.link || x.url,
281
- protected: privacyState?.data?.[value] ?? false,
282
- isPrivate:
283
- x.isPrivate || x.private || (getLink(x._rawLink, locale, defaultLocale)?.includes?.('/customer') ?? false), // FIXME: HACK: 隐藏 /customer 菜单, 需要一个通用的解决方案,在嵌入的时候就决定是否是私有的
284
- followersOnly: x.component === 'did-comments', // 是否开启仅粉丝可查看的功能,目前只对 discuss kit 开启
285
- // icon: x.icon,
286
- };
287
- })
288
- .filter((x: any) => isMyself || !x.isPrivate);
289
- }, [formattedBlocklet, userState.data, privacyState?.data, locale, defaultTabs, isMyself]);
290
-
291
- const currentActiveTab = useCreation(() => {
292
- return userCenterTabs.find((x) => {
293
- try {
294
- return (
295
- parseURL(withoutTrailingSlash(getLink(x.value, locale, defaultLocale))).pathname ===
296
- parseURL(withoutTrailingSlash(currentTab)).pathname
297
- );
298
- } catch {
299
- return false;
300
- }
301
- });
302
- }, [userCenterTabs]);
303
-
304
- const htmlTitle = useCreation(() => {
305
- const blockletName = window.blocklet?.appName;
306
- const titleGroup = [currentActiveTab?.label, t('userCenter.title')].filter(Boolean);
307
- const title = titleGroup.join('-');
308
- if (blockletName) {
309
- return `${title} | ${blockletName}`;
310
- }
311
- return title;
312
- }, [currentActiveTab, t]);
313
-
314
- // 辅助函数:给 children 注入 userCenterTabs 属性
315
- const renderChildrenWithProps = useMemoizedFn((childrenToRender: any) => {
316
- if (!childrenToRender) return childrenToRender;
317
-
318
- // 如果 children 是单个 React 元素,克隆并注入 props
319
- if (isValidElement(childrenToRender)) {
320
- return cloneElement(childrenToRender as any, {
321
- ...(childrenToRender.props || {}),
322
- userCenterTabs,
323
- });
324
- }
325
-
326
- // 如果 children 是数组,遍历每个元素并注入 props
327
- return Children.map(childrenToRender, (child) => {
328
- if (isValidElement(child)) {
329
- return cloneElement(child as any, {
330
- ...(child.props || {}),
331
- userCenterTabs,
332
- });
333
- }
334
- return child;
335
- });
336
- });
337
-
338
- const handleChangeTab = useMemoizedFn((value) => {
339
- const findTab = userCenterTabs.find((x) => x.value === value);
340
- if (findTab) {
341
- // 这里是安全的
342
- window.location.href = withQuery(findTab.url, {
343
- did: isMyself ? undefined : currentDid,
344
- });
345
- }
346
- });
347
-
348
- const settingContent = useCreation(() => {
349
- return (
350
- <Settings
351
- user={userState.data as User}
352
- settings={{ userCenterTabs }}
353
- onSave={async (type: 'privacy' | 'profile') => {
354
- if (type === 'privacy') {
355
- await privacyState.runAsync();
356
- return privacyState.data;
357
- }
358
- if (type === 'profile') {
359
- await session.refresh();
360
- }
361
- return null;
362
- }}
363
- isMobile={isMobile}
364
- onDestroySelf={onDestroySelf}
365
- />
366
- );
367
- }, [userState.data, userCenterTabs, privacyState.data, privacyState.runAsync]);
368
-
369
- const isSettingTab = useCreation(() => {
370
- return currentActiveTab && currentActiveTab?.value === settingsLink;
371
- }, [currentActiveTab]);
372
-
373
- const isNftsTab = useCreation(() => {
374
- return (
375
- (currentActiveTab && currentActiveTab?.value === joinURL(PROFILE_URL, '/profile')) ||
376
- currentActiveTab?.value === nftsLink
377
- );
378
- }, [currentActiveTab]);
379
-
380
- const isDidSpaceTab = useCreation(() => {
381
- return currentActiveTab && currentActiveTab?.value === didSpacesLink;
382
- }, [currentActiveTab]);
383
-
384
- const oauth = session.useOAuth();
385
- const passkey = session.usePasskey();
386
- const handleSwitchPassport = useMemoizedFn(() => {
387
- if (session?.user?.sourceProvider === 'passkey') {
388
- passkey.switchPassport(session.user);
389
- } else if (['google', 'apple', 'email', 'github'].includes(session?.user?.sourceProvider ?? '')) {
390
- oauth.switchOAuthPassport(session.user);
391
- } else if (session) {
392
- session.switchPassport();
393
- }
394
- });
395
-
396
- const renderDefaultTab = useCreation(() => {
397
- if (isNftsTab) {
398
- return (
399
- <Box
400
- sx={{
401
- display: 'flex',
402
- flexDirection: 'column',
403
- gap: 2.5,
404
- }}>
405
- {isMyself ? (
406
- <Box sx={{ border: '1px solid', borderColor: 'divider', borderRadius: 1.5, p: 2 }}>
407
- <Typography
408
- sx={{
409
- color: 'text.primary',
410
- fontWeight: 600,
411
- mb: 2.5,
412
- }}>
413
- {t('passport')}
414
- </Typography>
415
- <Passport user={userState.data as User} />
416
- </Box>
417
- ) : null}
418
- <Nft user={userState.data as User} />
419
- </Box>
420
- );
421
- }
422
- if (isSettingTab && isMyself) {
423
- return settingContent;
424
- }
425
- if (isDidSpaceTab && isMyself) {
426
- return (
427
- <ConfigUserSpaceProvider>
428
- <DidSpace />
429
- </ConfigUserSpaceProvider>
430
- );
431
- }
432
- return null;
433
- }, [isSettingTab, isNftsTab, userState, isMyself, stickySidebar, settingContent]);
434
-
435
- const emptyContent = useCreation(() => {
436
- return (
437
- <Box
438
- sx={{
439
- display: {
440
- xs: isMyself ? 'none' : 'block',
441
- md: 'block',
442
- },
443
- py: 3,
444
- }}>
445
- <Empty>{t('emptyContent')}</Empty>
446
- </Box>
447
- );
448
- }, [isMyself, locale]);
449
-
450
- const tabContent = useCreation(() => {
451
- if (!privacyState.data || privacyState.loading) {
452
- return (
453
- <Box
454
- sx={{
455
- height: '100%',
456
- minWidth: '160px',
457
- minHeight: '160px',
458
- display: 'flex',
459
- justifyContent: 'center',
460
- alignItems: 'center',
461
- flex: 1,
462
- }}>
463
- <CircularProgress />
464
- </Box>
465
- );
466
- }
467
-
468
- return (
469
- <Box sx={{ flex: 1 }}>
470
- <Fallback
471
- isSupportFollow={isShowSocialActions}
472
- currentActiveTab={currentActiveTab as UserCenterTab}
473
- isMyself={isMyself}>
474
- {children ? <Box {...contentProps}>{renderChildrenWithProps(children)}</Box> : renderDefaultTab}
475
- </Fallback>
476
- </Box>
477
- );
478
- }, [privacyState, currentActiveTab, isMyself, children, contentProps, renderDefaultTab, locale]);
479
-
480
- const content = useCreation(() => {
481
- if (userState.loading || session.loading) {
482
- return null;
483
- }
484
-
485
- if (userState.error) {
486
- const errorStatus = (userState.error as AxiosError<{ error: string }>)?.response?.status;
487
- if (errorStatus === 404) {
488
- return (
489
- <Box sx={{ width: '100%' }}>
490
- <Result status={404} description={t('noUserFound')} />
491
- </Box>
492
- );
493
- }
494
-
495
- const errorMessage =
496
- (userState.error as AxiosError<{ error: string }>).response?.data?.error ||
497
- userState.error.message ||
498
- 'error occurred';
499
-
500
- const formatError = {
501
- message: errorMessage,
502
- };
503
- return (
504
- <Box sx={{ width: '100%' }}>
505
- <ErrorFallback error={formatError} />
506
- </Box>
507
- );
508
- }
509
-
510
- if (!currentDid && !userState.data) {
511
- if (notLoginContent) {
512
- return notLoginContent;
513
- }
514
- return (
515
- <Box sx={{ width: '100%' }}>
516
- <Box
517
- sx={{
518
- display: 'flex',
519
- flexDirection: 'column',
520
- justifyContent: 'center',
521
- alignItems: 'center',
522
- gap: 1,
523
- }}>
524
- <Empty>{t('viewAfterLogin')}</Empty>
525
- <Button size="small" variant="contained" onClick={() => session.login()}>
526
- {t('loginNow')}
527
- </Button>
528
- </Box>
529
- </Box>
530
- );
531
- }
532
-
533
- if (currentDid && !userState.data && !userState.loading) {
534
- return (
535
- <Empty style={{ width: '100%', paddingTop: 16 }}>
536
- <Box sx={{ textAlign: 'center', width: '100%' }}>
537
- <Typography variant="body1" sx={{ fontSize: 18, fontWeight: 500 }}>
538
- {t('userNotFound')}
539
- </Typography>
540
- <Typography variant="body1" color="text.secondary">
541
- {t('userNotFoundDescription')}
542
- </Typography>
543
- </Box>
544
- </Empty>
545
- );
546
- }
547
-
548
- // 嵌入时,只显示 tabContent
549
- if (embed) {
550
- return (
551
- <ContentWrapper>
552
- {userCenterTabs.length > 0 && currentTab ? (
553
- <Box
554
- sx={{
555
- display: isMobile ? 'block' : 'flex',
556
- height: '100%',
557
- overflow: 'auto',
558
- padding: '1px',
559
- }}>
560
- {tabContent}
561
- </Box>
562
- ) : null}
563
- {userCenterTabs.length === 0 && emptyContent}
564
- </ContentWrapper>
565
- );
566
- }
567
-
568
- if (onlyProfile) {
569
- return (
570
- <ContentWrapper display="flex" flexDirection={isMobile ? 'column' : 'row'}>
571
- <UserBasicInfo
572
- isMobile={isMobile}
573
- order={isMobile ? 1 : 'unset'}
574
- isMyself={isMyself}
575
- switchPassport={handleSwitchPassport}
576
- switchProfile={session.switchProfile}
577
- user={userState.data as User}
578
- showFullDid={false}
579
- onlyProfile={onlyProfile}
580
- refreshProfile={onRefreshUser}
581
- isShowSocialActions={isShowSocialActions}
582
- sx={{
583
- padding: !isMobile ? '40px 24px 24px 40px' : '16px 0 0 0',
584
- ...(!isMobile ? { width: 320, maxWidth: 320, flexShrink: 0 } : {}),
585
- boxSizing: 'content-box',
586
- }}
587
- />
588
- </ContentWrapper>
589
- );
590
- }
591
-
592
- return (
593
- <ContentWrapper display="flex" flexDirection={isMobile ? 'column' : 'row'}>
594
- <Box
595
- className="user-center-tabs"
596
- sx={{
597
- flex: '1',
598
- order: isMobile ? 2 : 'unset',
599
- width: !isMobile ? 'calc(100% - 424px)' : 'unset',
600
- }}>
601
- {userCenterTabs.length > 0 && currentTab ? (
602
- <Box
603
- sx={{
604
- display: 'flex',
605
- flexDirection: 'column',
606
- height: '100%',
607
- overflow: 'auto',
608
- padding: '1px',
609
- }}>
610
- <Tabs
611
- orientation="horizontal"
612
- variant="line"
613
- tabs={userCenterTabs}
614
- current={currentActiveTab?.value ?? currentTab}
615
- onChange={handleChangeTab}
616
- enableTabClick
617
- sx={{
618
- mb: (theme: Theme) => `${theme.spacing(3)} !important`,
619
- '.MuiTabs-flexContainer': {
620
- gap: 3,
621
- '.MuiButtonBase-root': {
622
- padding: isMobile ? '16px 4px' : '32px 4px 16px 4px',
623
- fontSize: 16,
624
- },
625
- '.MuiTab-root': {
626
- display: 'block',
627
- textAlign: 'left',
628
- alignItems: 'flex-start',
629
- justifyContent: 'flex-start',
630
- fontWeight: 400,
631
- mr: 0,
632
- },
633
- },
634
- '.MuiTabs-indicator': {
635
- zIndex: 2,
636
- },
637
- '.MuiTabs-scroller': {
638
- '&:after': {
639
- content: '""',
640
- display: 'block',
641
- position: 'sticky',
642
- left: 0,
643
- zIndex: 1,
644
- width: '100%',
645
- height: '1px',
646
- backgroundColor: 'divider',
647
- },
648
- },
649
- }}
650
- />
651
- {tabContent}
652
- </Box>
653
- ) : null}
654
- {userCenterTabs.length === 0 && emptyContent}
655
- </Box>
656
- {!isMobile && <Divider orientation="vertical" sx={{ ml: 5 }} />}
657
- <UserBasicInfo
658
- isMobile={isMobile}
659
- order={isMobile ? 1 : 'unset'}
660
- isMyself={isMyself}
661
- switchPassport={handleSwitchPassport}
662
- switchProfile={session.switchProfile}
663
- user={userState.data as User}
664
- refreshProfile={onRefreshUser}
665
- showFullDid={false}
666
- isShowSocialActions={isShowSocialActions}
667
- sx={{
668
- padding: !isMobile ? '40px 24px 24px 40px' : '16px 0 0 0',
669
- ...(!isMobile ? { width: 320, maxWidth: 320, flexShrink: 0 } : {}),
670
- boxSizing: 'content-box',
671
- }}
672
- />
673
- </ContentWrapper>
674
- );
675
- }, [
676
- userState,
677
- userCenterTabs,
678
- isMyself,
679
- currentActiveTab,
680
- privacyState,
681
- currentTab,
682
- stickySidebar,
683
- renderDefaultTab,
684
- ]);
685
-
686
- // 如果当前URL指向的是 private tab需要重定向
687
- const isPrivateActive = useCreation(() => {
688
- if (isMyself) {
689
- return false;
690
- }
691
- return currentActiveTab?.isPrivate;
692
- }, [isMyself, currentActiveTab]);
693
-
694
- // 判断是否需要重定向到第一个可用的标签页
695
- const shouldRedirect = useCreation(() => {
696
- // 非 Profile 模式下才考虑重定向
697
- // 非登录状态下不进行重定向
698
- // 没有可用标签页时不进行重定向
699
- if (onlyProfile || embed || !userCenterTabs.length || !session.user) {
700
- return false;
701
- }
702
-
703
- // 以下任一条件满足时需要重定向:
704
- // 1. 自动重定向开启且当前无激活标签但有可用标签
705
- const noActiveTabButHasTabs = !disableAutoRedirect && !currentTab && userCenterTabs?.length > 0;
706
- // 2. 当前标签页不存在
707
- const invalidCurrentTab = !currentActiveTab;
708
- // 3. 当前是私有标签页但用户在查看他人信息
709
- const accessingPrivateTab = isPrivateActive;
710
-
711
- return noActiveTabButHasTabs || invalidCurrentTab || accessingPrivateTab;
712
- }, [
713
- disableAutoRedirect,
714
- currentTab,
715
- userCenterTabs,
716
- currentActiveTab,
717
- isPrivateActive,
718
- onlyProfile,
719
- session.user,
720
- embed,
721
- ]);
722
-
723
- if (shouldRedirect) {
724
- // 获取第一个可用的标签页URL
725
- const firstUserCenterUrl = userCenterTabs[0]?.url;
726
- const firstTab = userCenterTabs.find((x) => x.value === firstUserCenterUrl);
727
- // 如果存在非私有的第一个标签页,则重定向
728
- if (firstUserCenterUrl && !firstTab?.isPrivate) {
729
- window.location.replace(
730
- withQuery(firstUserCenterUrl, {
731
- did: isMyself ? undefined : currentDid,
732
- })
733
- );
734
- }
735
- return null;
736
- }
737
-
738
- // 嵌入其它页面内时,只展示 content
739
- if (embed || onlyProfile) {
740
- return (
741
- <Box>
742
- {/* 在 Arcsphere 中,需要渲染 header 用于注入某些 bridge 方法,设置为隐藏状态 */}
743
- <Helmet>
744
- <title>{htmlTitle}</title>
745
- </Helmet>
746
- <Header style={{ display: 'none' }} />
747
- <Main>
748
- <UserFollowersProvider isMySelf={isMyself} userDid={userState.data?.did ?? ''}>
749
- {content}
750
- </UserFollowersProvider>
751
- {confirmHolder}
752
- </Main>
753
- </Box>
754
- );
755
- }
756
-
757
- return (
758
- <Box
759
- sx={{
760
- minHeight: '100vh',
761
- display: 'flex',
762
- flexDirection: 'column',
763
- }}>
764
- <Helmet>
765
- <title>{htmlTitle}</title>
766
- </Helmet>
767
- <Header bordered {...headerProps} maxWidth="100%" />
768
- <Main>
769
- <UserFollowersProvider isMySelf={isMyself} userDid={userState.data?.did ?? ''}>
770
- {content}
771
- </UserFollowersProvider>
772
- {confirmHolder}
773
- </Main>
774
- {hideFooter ? null : (
775
- <Footer
776
- bordered
777
- {...footerProps}
778
- sx={{
779
- '.MuiContainer-root': {
780
- maxWidth: 1600,
781
- },
782
- }}
783
- />
784
- )}
785
- </Box>
786
- );
787
- }