@blocklet/ui-react 3.4.15 → 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 (255) hide show
  1. package/lib/common/org-switch/use-org.d.ts +4 -4
  2. package/package.json +9 -6
  3. package/.aigne/doc-smith/.local/afs-storage.sqlite3 +0 -0
  4. package/.aigne/doc-smith/config.yaml +0 -78
  5. package/.aigne/doc-smith/history.yaml +0 -14
  6. package/.aigne/doc-smith/media-description.yaml +0 -11
  7. package/.aigne/doc-smith/output/structure-plan.json +0 -255
  8. package/.aigne/doc-smith/translation-cache.yaml +0 -11
  9. package/.aigne/doc-smith/upload-cache.yaml +0 -528
  10. package/build.config.ts +0 -24
  11. package/docs/_sidebar.md +0 -19
  12. package/docs/assets/diagram/component-installer-diagram-0.ja.jpg +0 -0
  13. package/docs/assets/diagram/component-installer-diagram-0.jpg +0 -0
  14. package/docs/assets/diagram/component-installer-diagram-0.zh-TW.jpg +0 -0
  15. package/docs/assets/diagram/component-installer-diagram-0.zh.jpg +0 -0
  16. package/docs/assets/diagram/component-management-diagram-0.ja.jpg +0 -0
  17. package/docs/assets/diagram/component-management-diagram-0.jpg +0 -0
  18. package/docs/assets/diagram/component-management-diagram-0.zh-TW.jpg +0 -0
  19. package/docs/assets/diagram/component-management-diagram-0.zh.jpg +0 -0
  20. package/docs/assets/diagram/core-concepts-diagram-0.ja.jpg +0 -0
  21. package/docs/assets/diagram/core-concepts-diagram-0.jpg +0 -0
  22. package/docs/assets/diagram/core-concepts-diagram-0.zh-TW.jpg +0 -0
  23. package/docs/assets/diagram/core-concepts-diagram-0.zh.jpg +0 -0
  24. package/docs/assets/diagram/dashboard-diagram-0.ja.jpg +0 -0
  25. package/docs/assets/diagram/dashboard-diagram-0.jpg +0 -0
  26. package/docs/assets/diagram/dashboard-diagram-0.zh-TW.jpg +0 -0
  27. package/docs/assets/diagram/dashboard-diagram-0.zh.jpg +0 -0
  28. package/docs/assets/diagram/header-diagram-0.ja.jpg +0 -0
  29. package/docs/assets/diagram/header-diagram-0.jpg +0 -0
  30. package/docs/assets/diagram/header-diagram-0.zh-TW.jpg +0 -0
  31. package/docs/assets/diagram/header-diagram-0.zh.jpg +0 -0
  32. package/docs/assets/diagram/layout-diagram-0.ja.jpg +0 -0
  33. package/docs/assets/diagram/layout-diagram-0.jpg +0 -0
  34. package/docs/assets/diagram/layout-diagram-0.zh-TW.jpg +0 -0
  35. package/docs/assets/diagram/layout-diagram-0.zh.jpg +0 -0
  36. package/docs/assets/diagram/notifications-diagram-0.ja.jpg +0 -0
  37. package/docs/assets/diagram/notifications-diagram-0.jpg +0 -0
  38. package/docs/assets/diagram/notifications-diagram-0.zh-TW.jpg +0 -0
  39. package/docs/assets/diagram/notifications-diagram-0.zh.jpg +0 -0
  40. package/docs/assets/diagram/overview-diagram-0.ja.jpg +0 -0
  41. package/docs/assets/diagram/overview-diagram-0.jpg +0 -0
  42. package/docs/assets/diagram/overview-diagram-0.zh-TW.jpg +0 -0
  43. package/docs/assets/diagram/overview-diagram-0.zh.jpg +0 -0
  44. package/docs/assets/diagram/user-center-diagram-0.ja.jpg +0 -0
  45. package/docs/assets/diagram/user-center-diagram-0.jpg +0 -0
  46. package/docs/assets/diagram/user-center-diagram-0.zh-TW.jpg +0 -0
  47. package/docs/assets/diagram/user-center-diagram-0.zh.jpg +0 -0
  48. package/docs/assets/diagram/user-management-diagram-0.ja.jpg +0 -0
  49. package/docs/assets/diagram/user-management-diagram-0.jpg +0 -0
  50. package/docs/assets/diagram/user-management-diagram-0.zh-TW.jpg +0 -0
  51. package/docs/assets/diagram/user-management-diagram-0.zh.jpg +0 -0
  52. package/docs/assets/diagram/user-sessions-diagram-0.ja.jpg +0 -0
  53. package/docs/assets/diagram/user-sessions-diagram-0.jpg +0 -0
  54. package/docs/assets/diagram/user-sessions-diagram-0.zh-TW.jpg +0 -0
  55. package/docs/assets/diagram/user-sessions-diagram-0.zh.jpg +0 -0
  56. package/docs/components-component-management-blocklet-studio.ja.md +0 -194
  57. package/docs/components-component-management-blocklet-studio.md +0 -194
  58. package/docs/components-component-management-blocklet-studio.zh-TW.md +0 -194
  59. package/docs/components-component-management-blocklet-studio.zh.md +0 -194
  60. package/docs/components-component-management-component-installer.ja.md +0 -182
  61. package/docs/components-component-management-component-installer.md +0 -182
  62. package/docs/components-component-management-component-installer.zh-TW.md +0 -182
  63. package/docs/components-component-management-component-installer.zh.md +0 -182
  64. package/docs/components-component-management.ja.md +0 -30
  65. package/docs/components-component-management.md +0 -30
  66. package/docs/components-component-management.zh-TW.md +0 -30
  67. package/docs/components-component-management.zh.md +0 -30
  68. package/docs/components-layout-dashboard.ja.md +0 -185
  69. package/docs/components-layout-dashboard.md +0 -187
  70. package/docs/components-layout-dashboard.zh-TW.md +0 -185
  71. package/docs/components-layout-dashboard.zh.md +0 -185
  72. package/docs/components-layout-footer.ja.md +0 -165
  73. package/docs/components-layout-footer.md +0 -165
  74. package/docs/components-layout-footer.zh-TW.md +0 -165
  75. package/docs/components-layout-footer.zh.md +0 -165
  76. package/docs/components-layout-header.ja.md +0 -183
  77. package/docs/components-layout-header.md +0 -183
  78. package/docs/components-layout-header.zh-TW.md +0 -183
  79. package/docs/components-layout-header.zh.md +0 -183
  80. package/docs/components-layout.ja.md +0 -31
  81. package/docs/components-layout.md +0 -31
  82. package/docs/components-layout.zh-TW.md +0 -31
  83. package/docs/components-layout.zh.md +0 -31
  84. package/docs/components-notifications.ja.md +0 -125
  85. package/docs/components-notifications.md +0 -125
  86. package/docs/components-notifications.zh-TW.md +0 -125
  87. package/docs/components-notifications.zh.md +0 -125
  88. package/docs/components-user-management-user-center.ja.md +0 -148
  89. package/docs/components-user-management-user-center.md +0 -147
  90. package/docs/components-user-management-user-center.zh-TW.md +0 -148
  91. package/docs/components-user-management-user-center.zh.md +0 -148
  92. package/docs/components-user-management-user-sessions.ja.md +0 -121
  93. package/docs/components-user-management-user-sessions.md +0 -123
  94. package/docs/components-user-management-user-sessions.zh-TW.md +0 -121
  95. package/docs/components-user-management-user-sessions.zh.md +0 -121
  96. package/docs/components-user-management.ja.md +0 -49
  97. package/docs/components-user-management.md +0 -51
  98. package/docs/components-user-management.zh-TW.md +0 -49
  99. package/docs/components-user-management.zh.md +0 -49
  100. package/docs/components-utilities-icon.ja.md +0 -106
  101. package/docs/components-utilities-icon.md +0 -106
  102. package/docs/components-utilities-icon.zh-TW.md +0 -106
  103. package/docs/components-utilities-icon.zh.md +0 -106
  104. package/docs/components-utilities.ja.md +0 -136
  105. package/docs/components-utilities.md +0 -136
  106. package/docs/components-utilities.zh-TW.md +0 -136
  107. package/docs/components-utilities.zh.md +0 -136
  108. package/docs/components.ja.md +0 -27
  109. package/docs/components.md +0 -27
  110. package/docs/components.zh-TW.md +0 -27
  111. package/docs/components.zh.md +0 -27
  112. package/docs/core-concepts.ja.md +0 -134
  113. package/docs/core-concepts.md +0 -135
  114. package/docs/core-concepts.zh-TW.md +0 -134
  115. package/docs/core-concepts.zh.md +0 -134
  116. package/docs/getting-started.ja.md +0 -132
  117. package/docs/getting-started.md +0 -132
  118. package/docs/getting-started.zh-TW.md +0 -132
  119. package/docs/getting-started.zh.md +0 -132
  120. package/docs/hooks-api.ja.md +0 -214
  121. package/docs/hooks-api.md +0 -214
  122. package/docs/hooks-api.zh-TW.md +0 -214
  123. package/docs/hooks-api.zh.md +0 -214
  124. package/docs/how-to-guides.ja.md +0 -413
  125. package/docs/how-to-guides.md +0 -413
  126. package/docs/how-to-guides.zh-TW.md +0 -413
  127. package/docs/how-to-guides.zh.md +0 -413
  128. package/docs/overview.ja.md +0 -51
  129. package/docs/overview.md +0 -51
  130. package/docs/overview.zh-TW.md +0 -51
  131. package/docs/overview.zh.md +0 -51
  132. package/glossary.md +0 -12
  133. package/src/@types/index.ts +0 -230
  134. package/src/@types/shims.d.ts +0 -18
  135. package/src/BlockletStudio/README.md +0 -116
  136. package/src/BlockletStudio/index.tsx +0 -145
  137. package/src/ComponentInstaller/ComponentInstaller.stories.jsx +0 -16
  138. package/src/ComponentInstaller/index.jsx +0 -207
  139. package/src/ComponentInstaller/installer-item.jsx +0 -129
  140. package/src/ComponentInstaller/locales.js +0 -22
  141. package/src/ComponentInstaller/use-component-installed.js +0 -88
  142. package/src/ComponentManager/components/add-component.tsx +0 -136
  143. package/src/ComponentManager/components/check-component.tsx +0 -3
  144. package/src/ComponentManager/components/publish-component.tsx +0 -90
  145. package/src/ComponentManager/components/resource-dialog.tsx +0 -91
  146. package/src/ComponentManager/index.tsx +0 -3
  147. package/src/ComponentManager/libs/locales.ts +0 -15
  148. package/src/Dashboard/Dashboard.stories.jsx +0 -20
  149. package/src/Dashboard/app-shell/app-badge.stories.tsx +0 -64
  150. package/src/Dashboard/app-shell/app-badge.tsx +0 -94
  151. package/src/Dashboard/app-shell/app-header.tsx +0 -104
  152. package/src/Dashboard/app-shell/app-info-context.tsx +0 -182
  153. package/src/Dashboard/app-shell/badges/app-badge-default.tsx +0 -130
  154. package/src/Dashboard/app-shell/badges/app-badge-did.tsx +0 -28
  155. package/src/Dashboard/app-shell/badges/app-badge-state.tsx +0 -40
  156. package/src/Dashboard/app-shell/badges/app-badge-switch.tsx +0 -72
  157. package/src/Dashboard/app-shell/badges/app-badge-version.tsx +0 -60
  158. package/src/Dashboard/app-shell/index.ts +0 -5
  159. package/src/Dashboard/index.jsx +0 -184
  160. package/src/Footer/Footer.stories.jsx +0 -33
  161. package/src/Footer/brand.jsx +0 -81
  162. package/src/Footer/copyright.jsx +0 -22
  163. package/src/Footer/index.jsx +0 -111
  164. package/src/Footer/internal-footer.jsx +0 -139
  165. package/src/Footer/layout/plain.jsx +0 -55
  166. package/src/Footer/layout/row.jsx +0 -43
  167. package/src/Footer/layout/standard.jsx +0 -114
  168. package/src/Footer/links.jsx +0 -321
  169. package/src/Footer/social-media.jsx +0 -55
  170. package/src/Header/Header.stories.jsx +0 -30
  171. package/src/Header/index.tsx +0 -259
  172. package/src/Icon/Icon.stories.jsx +0 -12
  173. package/src/Icon/index.tsx +0 -87
  174. package/src/Notifications/Snackbar.tsx +0 -261
  175. package/src/Notifications/hooks/use-title.tsx +0 -254
  176. package/src/Notifications/hooks/use-width.tsx +0 -16
  177. package/src/Notifications/utils.ts +0 -246
  178. package/src/UserCenter/assets/banner.png +0 -0
  179. package/src/UserCenter/components/config-inviter.tsx +0 -48
  180. package/src/UserCenter/components/config-profile.tsx +0 -99
  181. package/src/UserCenter/components/danger-zone.tsx +0 -82
  182. package/src/UserCenter/components/editable-field.tsx +0 -273
  183. package/src/UserCenter/components/fallback.tsx +0 -57
  184. package/src/UserCenter/components/nft-preview.tsx +0 -84
  185. package/src/UserCenter/components/nft.tsx +0 -279
  186. package/src/UserCenter/components/notification.tsx +0 -319
  187. package/src/UserCenter/components/passport.tsx +0 -107
  188. package/src/UserCenter/components/privacy.tsx +0 -120
  189. package/src/UserCenter/components/settings.tsx +0 -170
  190. package/src/UserCenter/components/status-dialog/date-picker.tsx +0 -77
  191. package/src/UserCenter/components/status-dialog/index.tsx +0 -293
  192. package/src/UserCenter/components/status-selector/duration-menu.tsx +0 -90
  193. package/src/UserCenter/components/status-selector/index.tsx +0 -58
  194. package/src/UserCenter/components/status-selector/menu-item.tsx +0 -56
  195. package/src/UserCenter/components/storage/action.tsx +0 -49
  196. package/src/UserCenter/components/storage/connected.tsx +0 -61
  197. package/src/UserCenter/components/storage/delete.tsx +0 -72
  198. package/src/UserCenter/components/storage/disconnect.tsx +0 -40
  199. package/src/UserCenter/components/storage/icons/empty-spaces-nft.svg +0 -1
  200. package/src/UserCenter/components/storage/icons/long-arrow.svg +0 -5
  201. package/src/UserCenter/components/storage/icons/space-connected.svg +0 -3
  202. package/src/UserCenter/components/storage/icons/space-disconnect.svg +0 -3
  203. package/src/UserCenter/components/storage/index.tsx +0 -41
  204. package/src/UserCenter/components/storage/preview-nft.tsx +0 -72
  205. package/src/UserCenter/components/third-party-login/index.tsx +0 -199
  206. package/src/UserCenter/components/third-party-login/third-party-item.tsx +0 -296
  207. package/src/UserCenter/components/user-center.tsx +0 -787
  208. package/src/UserCenter/components/user-info/address.tsx +0 -143
  209. package/src/UserCenter/components/user-info/index.tsx +0 -4
  210. package/src/UserCenter/components/user-info/link-preview-input.tsx +0 -274
  211. package/src/UserCenter/components/user-info/metadata.tsx +0 -658
  212. package/src/UserCenter/components/user-info/social-actions/chat.tsx +0 -43
  213. package/src/UserCenter/components/user-info/social-actions/follow.tsx +0 -23
  214. package/src/UserCenter/components/user-info/social-actions/index.tsx +0 -17
  215. package/src/UserCenter/components/user-info/switch-role.tsx +0 -42
  216. package/src/UserCenter/components/user-info/timezone-select.tsx +0 -119
  217. package/src/UserCenter/components/user-info/user-basic-info.tsx +0 -292
  218. package/src/UserCenter/components/user-info/user-info-item.tsx +0 -54
  219. package/src/UserCenter/components/user-info/user-info.tsx +0 -91
  220. package/src/UserCenter/components/user-info/user-status.tsx +0 -234
  221. package/src/UserCenter/components/user-info/utils.ts +0 -320
  222. package/src/UserCenter/components/webhook-item.tsx +0 -248
  223. package/src/UserCenter/index.tsx +0 -1
  224. package/src/UserCenter/libs/locales.ts +0 -378
  225. package/src/UserCenter/libs/utils.ts +0 -30
  226. package/src/UserSessions/components/user-session-info.tsx +0 -78
  227. package/src/UserSessions/components/user-sessions.tsx +0 -545
  228. package/src/UserSessions/index.tsx +0 -1
  229. package/src/UserSessions/libs/locales.ts +0 -60
  230. package/src/UserSessions/libs/utils.ts +0 -82
  231. package/src/blocklets.js +0 -195
  232. package/src/common/domain-warning.jsx +0 -178
  233. package/src/common/header-addons.jsx +0 -119
  234. package/src/common/link-blocker.jsx +0 -20
  235. package/src/common/notification-addon.jsx +0 -135
  236. package/src/common/org-switch/avatar-uploader.jsx +0 -271
  237. package/src/common/org-switch/create.jsx +0 -267
  238. package/src/common/org-switch/index.jsx +0 -407
  239. package/src/common/org-switch/locales.js +0 -52
  240. package/src/common/org-switch/use-org.jsx +0 -79
  241. package/src/common/overridable-theme-provider.jsx +0 -17
  242. package/src/common/wallet-hidden-topbar.js +0 -14
  243. package/src/common/wizard-modal.jsx +0 -200
  244. package/src/common/ws.js +0 -68
  245. package/src/contexts/config-user-space.tsx +0 -88
  246. package/src/contexts/user-followers.tsx +0 -54
  247. package/src/hooks/use-follow.tsx +0 -75
  248. package/src/hooks/use-mobile.tsx +0 -6
  249. package/src/index.ts +0 -16
  250. package/src/libs/constant.ts +0 -1
  251. package/src/libs/spaces.tsx +0 -18
  252. package/src/libs/with-hide-when-embed.tsx +0 -24
  253. package/src/types.js +0 -45
  254. package/src/utils.js +0 -161
  255. package/vite.config.mjs +0 -34
@@ -1,135 +0,0 @@
1
- import PropTypes from 'prop-types';
2
- import { useCallback, useEffect, useMemo } from 'react';
3
- import { IconButton, Badge } from '@mui/material';
4
- import { useSnackbar } from 'notistack';
5
- import NotificationsOutlinedIcon from '@arcblock/icons/lib/Notification';
6
- import { useCreation } from 'ahooks';
7
- import { EVENTS, WELLKNOWN_SERVICE_PATH_PREFIX, ROLES } from '@abtnode/constant';
8
- import useBrowser from '@arcblock/react-hooks/lib/useBrowser';
9
- import { compareVersions } from '@arcblock/ux/lib/Util';
10
- import { joinURL, withQuery } from 'ufo';
11
- import { useListenWsClient } from './ws';
12
- import NotificationSnackbar from '../Notifications/Snackbar';
13
-
14
- const viewAllUrl = joinURL(WELLKNOWN_SERVICE_PATH_PREFIX, 'user', 'notifications');
15
-
16
- const getNotificationLink = (notification) => {
17
- return withQuery(viewAllUrl, {
18
- id: notification.id,
19
- severity: notification.severity || 'all',
20
- componentDid: notification.source === 'system' ? 'system' : notification.componentDid || 'all',
21
- });
22
- };
23
-
24
- export default function NotificationAddon({ session = {} }) {
25
- const { unReadCount, user, setUnReadCount } = session;
26
- const userDid = useCreation(() => user?.did, [user]);
27
-
28
- const isAdmin = useCreation(() => user?.role && [ROLES.ADMIN, ROLES.OWNER].includes(user?.role), [user?.role]);
29
-
30
- const { enqueueSnackbar } = useSnackbar();
31
- const browser = useBrowser();
32
-
33
- const isPreviewUser = useMemo(() => {
34
- return !userDid || user?.isPreviewUser || userDid === window.blocklet.did;
35
- }, [userDid, user]);
36
-
37
- // 在 ArcSphere 和 Wallet 端隐藏, 消息的 toast 提示,因为有 native 的通知
38
- const hiddenNotificationToast = useMemo(() => {
39
- return browser.arcSphere || browser.wallet;
40
- }, [browser]);
41
-
42
- const serverVersion = useCreation(() => {
43
- return window.blocklet?.serverVersion;
44
- }, []);
45
-
46
- const wsClient = useListenWsClient('user');
47
-
48
- const listenEvent = useCreation(
49
- () => `${window.blocklet.did}/${userDid}/${EVENTS.NOTIFICATION_BLOCKLET_CREATE}`,
50
- [userDid]
51
- );
52
- const readListenEvent = useCreation(
53
- () => `${window.blocklet.did}/${userDid}/${EVENTS.NOTIFICATION_BLOCKLET_READ}`,
54
- [userDid]
55
- );
56
-
57
- const listenCallback = useCallback(
58
- (notification) => {
59
- const { receivers, source } = notification ?? {};
60
-
61
- const { receiver: notificationReceiver } = receivers[0] ?? {};
62
- const shouldUpdate = isAdmin || source !== 'system';
63
- if (notificationReceiver === userDid && shouldUpdate) {
64
- setUnReadCount((x) => x + 1);
65
- // 显示通知, 如果是系统通知则不需要显示, 如果是移动端不需要显示
66
- // 兼容代码,如果 server 没有升级那么不需要提示
67
- const isCompatible = compareVersions(serverVersion, '1.16.42-beta-20250407');
68
- if (!hiddenNotificationToast && notification.source === 'component' && isCompatible) {
69
- const link = getNotificationLink(notification);
70
- const { severity, description } = notification || {};
71
- const disableAutoHide = ['error', 'warning'].includes(severity) || notification.sticky;
72
- enqueueSnackbar(description, {
73
- variant: severity,
74
- autoHideDuration: disableAutoHide ? null : 5000,
75
- anchorOrigin: {
76
- vertical: 'top',
77
- horizontal: 'center',
78
- },
79
- // eslint-disable-next-line react/no-unstable-nested-components
80
- content: (key) => <NotificationSnackbar viewAllUrl={link} keyId={key} notification={notification} />,
81
- });
82
- }
83
- }
84
- },
85
- [userDid, setUnReadCount, enqueueSnackbar, serverVersion, hiddenNotificationToast, isAdmin]
86
- );
87
-
88
- const readListenCallback = useCallback(
89
- (data) => {
90
- const { receiver, readCount } = data ?? {};
91
-
92
- if (receiver === userDid) {
93
- setUnReadCount((x) => Math.max(x - readCount, 0));
94
- }
95
- },
96
- [userDid, setUnReadCount]
97
- );
98
-
99
- useEffect(() => {
100
- if (wsClient && !isPreviewUser) {
101
- wsClient.on(listenEvent, listenCallback);
102
- wsClient.on(readListenEvent, readListenCallback);
103
- }
104
- return () => {
105
- if (wsClient && !isPreviewUser) {
106
- wsClient.off(listenEvent, listenCallback);
107
- wsClient.off(readListenEvent, readListenCallback);
108
- }
109
- };
110
- }, [wsClient, setUnReadCount, listenCallback, listenEvent, readListenCallback, readListenEvent, isPreviewUser]);
111
-
112
- if (!session.user || !viewAllUrl) {
113
- return null;
114
- }
115
-
116
- return (
117
- <IconButton
118
- size="medium"
119
- variant="outlined"
120
- href={withQuery(viewAllUrl, { read: unReadCount <= 0 })}
121
- sx={{
122
- '&:hover': {
123
- borderRadius: '50%',
124
- },
125
- }}>
126
- <Badge badgeContent={unReadCount} color="error" invisible={unReadCount === 0}>
127
- <NotificationsOutlinedIcon style={{ width: 'auto', height: 24 }} />
128
- </Badge>
129
- </IconButton>
130
- );
131
- }
132
-
133
- NotificationAddon.propTypes = {
134
- session: PropTypes.object,
135
- };
@@ -1,271 +0,0 @@
1
- /**
2
- * AvatarUploader - 组织头像上传组件
3
- *
4
- * 用于创建和编辑组织时上传头像
5
- * 支持图片裁剪、旋转等编辑功能
6
- * 上传成功后返回文件路径供表单使用
7
- */
8
- import { lazy, Suspense, useRef, useMemo, useState } from 'react';
9
- import noop from 'lodash/noop';
10
- import PropTypes from 'prop-types';
11
- import { Box, CircularProgress } from '@mui/material';
12
- import { styled } from '@arcblock/ux/lib/Theme';
13
- import { joinURL } from 'ufo';
14
- import { useMemoizedFn } from 'ahooks';
15
- import CameraAltIcon from '@mui/icons-material/CameraAlt';
16
- import { translate } from '@arcblock/ux/lib/Locale/util';
17
- import Img from '@arcblock/ux/lib/Img';
18
-
19
- import translations from './locales';
20
-
21
- // eslint-disable-next-line import/no-unresolved
22
- const Uploader = lazy(() => import('@blocklet/uploader').then((res) => ({ default: res.Uploader })));
23
-
24
- // 配置常量
25
- const UPLOAD_PREFIX = '/blocklet';
26
- const ALLOWED_FILE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp', '.bmp', '.ico'];
27
- const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
28
- const MIN_CROP_SIZE = 256;
29
- const ICON_SIZE_RATIO = 3;
30
-
31
- /**
32
- * 构建组织头像显示 URL
33
- * @param {string} prefix - URL 前缀
34
- * @param {string} teamDid - 团队 DID
35
- * @param {string} orgId - 组织 ID
36
- * @param {string} avatar - 头像文件名
37
- * @returns {string} 完整的 URL 路径
38
- */
39
- function buildAvatarDisplayUrl(prefix, teamDid, avatar) {
40
- return joinURL(prefix, UPLOAD_PREFIX, teamDid, 'orgs', 'avatar', avatar);
41
- }
42
-
43
- /**
44
- * 构建头像上传 API URL(不需要 orgId)
45
- * @param {string} prefix - URL 前缀
46
- * @param {string} teamDid - 团队 DID
47
- * @returns {string} 上传 API URL
48
- */
49
- function buildAvatarUploadUrl(prefix, teamDid) {
50
- return joinURL(prefix, UPLOAD_PREFIX, teamDid, 'orgs', 'avatar', 'upload');
51
- }
52
-
53
- export default function AvatarUploader({
54
- org = null,
55
- size = 80,
56
- teamDid: teamDidProp = '',
57
- prefix = '/.well-known/service',
58
- locale = 'en',
59
- headers = noop,
60
- onChange = noop,
61
- onError = noop,
62
- editable = true,
63
- }) {
64
- const t = useMemoizedFn((key, data = {}) => {
65
- return translate(translations, key, locale, 'en', data);
66
- });
67
- const uploaderRef = useRef(null);
68
- const [uploading, setUploading] = useState(false);
69
- // 本地预览的头像路径(用于新上传的图片)
70
- const [localAvatar, setLocalAvatar] = useState('');
71
-
72
- // 获取 teamDid:优先使用 prop,否则从 window.blocklet.did 获取
73
- const teamDid = useMemo(() => {
74
- if (teamDidProp) return teamDidProp;
75
- // eslint-disable-next-line no-undef
76
- return typeof window !== 'undefined' ? window.blocklet?.did : '';
77
- }, [teamDidProp]);
78
-
79
- // 构建上传 API URL(不需要 orgId)
80
- const uploadApiUrl = useMemo(() => {
81
- if (!teamDid) return null;
82
- return buildAvatarUploadUrl(prefix, teamDid);
83
- }, [prefix, teamDid]);
84
-
85
- // 是否可以上传(需要有 teamDid 且 editable 为 true)
86
- const canUpload = editable && !!uploadApiUrl;
87
-
88
- // 打开上传弹窗
89
- const handleOpen = useMemoizedFn(() => {
90
- if (canUpload && !uploading) {
91
- uploaderRef.current?.open();
92
- }
93
- });
94
-
95
- // 上传完成处理 - 返回文件路径
96
- const handleFinish = useMemoizedFn((result) => {
97
- uploaderRef.current?.close();
98
- setUploading(false);
99
- const { avatarPath } = result.data;
100
- setLocalAvatar(result.uploadURL);
101
- onChange(avatarPath);
102
- });
103
-
104
- // 上传开始处理
105
- const handleUploadStart = useMemoizedFn(() => {
106
- setUploading(true);
107
- });
108
-
109
- // 错误处理
110
- const handleError = useMemoizedFn((error) => {
111
- setUploading(false);
112
- console.error('Avatar upload failed:', error);
113
- onError(error);
114
- });
115
-
116
- // 构建头像显示 URL
117
- const avatarUrl = useMemo(() => {
118
- // 优先使用本地上传的头像
119
- if (localAvatar) {
120
- return localAvatar;
121
- }
122
- const avatar = org?.avatar;
123
- if (!avatar) return null;
124
- if (org?.id) {
125
- return buildAvatarDisplayUrl(prefix, teamDid, avatar);
126
- }
127
- if (avatar.startsWith('http://') || avatar.startsWith('https://') || avatar.startsWith('/')) {
128
- return avatar;
129
- }
130
- return joinURL(prefix, avatar);
131
- }, [localAvatar, org?.avatar, org?.id, prefix, teamDid]);
132
-
133
- // 获取显示名称
134
- const displayName = org?.name || '';
135
-
136
- return (
137
- <Root $size={size} $editable={editable} $uploading={uploading} onClick={handleOpen}>
138
- {uploadApiUrl && (
139
- <Suspense fallback={null}>
140
- <Uploader
141
- ref={uploaderRef}
142
- locale={locale}
143
- popup
144
- onUploadFinish={handleFinish}
145
- onUploadStart={handleUploadStart}
146
- onError={handleError}
147
- plugins={['ImageEditor']}
148
- installerProps={{ disabled: true }}
149
- apiPathProps={{
150
- uploader: uploadApiUrl,
151
- disableMediaKitPrefix: true,
152
- disableMediaKitStatus: true,
153
- }}
154
- coreProps={{
155
- restrictions: {
156
- allowedFileExts: ALLOWED_FILE_EXTENSIONS,
157
- maxFileSize: MAX_FILE_SIZE,
158
- maxNumberOfFiles: 1,
159
- },
160
- }}
161
- dashboardProps={{
162
- autoOpen: 'imageEditor',
163
- }}
164
- imageEditorProps={{
165
- actions: {
166
- revert: true,
167
- rotate: true,
168
- granularRotate: true,
169
- flip: true,
170
- zoomIn: true,
171
- zoomOut: true,
172
- cropSquare: false,
173
- cropWidescreen: false,
174
- cropWidescreenVertical: false,
175
- },
176
- cropperOptions: {
177
- autoCrop: true,
178
- autoCropArea: 1,
179
- aspectRatio: 1,
180
- initialAspectRatio: 1,
181
- croppedCanvasOptions: {
182
- minWidth: MIN_CROP_SIZE,
183
- minHeight: MIN_CROP_SIZE,
184
- },
185
- },
186
- }}
187
- tusProps={{
188
- headers,
189
- }}
190
- />
191
- </Suspense>
192
- )}
193
- {/* 使用 Img 组件显示头像,自动处理加载错误和占位图片 */}
194
- <Img
195
- key={avatarUrl || 'no-avatar'}
196
- src={avatarUrl || ''}
197
- alt={displayName}
198
- width={size}
199
- height={size}
200
- ratio={1}
201
- size="cover"
202
- position="center"
203
- lazy={false}
204
- style={{
205
- borderRadius: '50%',
206
- overflow: 'hidden',
207
- }}
208
- />
209
- {editable && (
210
- <Box className="upload-overlay">
211
- {uploading ? (
212
- <CircularProgress size={size / ICON_SIZE_RATIO} sx={{ color: 'white' }} />
213
- ) : (
214
- <>
215
- <CameraAltIcon sx={{ fontSize: size / ICON_SIZE_RATIO, color: 'white' }} />
216
- <Box component="span" sx={{ fontSize: 12, color: 'white' }}>
217
- {t('upload')}
218
- </Box>
219
- </>
220
- )}
221
- </Box>
222
- )}
223
- </Root>
224
- );
225
- }
226
-
227
- AvatarUploader.propTypes = {
228
- org: PropTypes.shape({
229
- id: PropTypes.string,
230
- name: PropTypes.string,
231
- avatar: PropTypes.string,
232
- }),
233
- size: PropTypes.number,
234
- teamDid: PropTypes.string,
235
- prefix: PropTypes.string,
236
- headers: PropTypes.func,
237
- onChange: PropTypes.func,
238
- onError: PropTypes.func,
239
- editable: PropTypes.bool,
240
- locale: PropTypes.string,
241
- };
242
-
243
- const Root = styled(Box, {
244
- shouldForwardProp: (prop) => !['$size', '$editable', '$uploading'].includes(prop),
245
- })(({ $size, $editable, $uploading }) => ({
246
- position: 'relative',
247
- width: $size,
248
- height: $size,
249
- borderRadius: '50%',
250
- overflow: 'hidden',
251
- cursor: $editable && !$uploading ? 'pointer' : 'default',
252
-
253
- '.upload-overlay': {
254
- position: 'absolute',
255
- top: 0,
256
- left: 0,
257
- right: 0,
258
- bottom: 0,
259
- display: 'flex',
260
- flexDirection: 'column',
261
- alignItems: 'center',
262
- justifyContent: 'center',
263
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
264
- opacity: $uploading ? 1 : 0,
265
- transition: 'opacity 0.2s ease-in-out',
266
- },
267
-
268
- '&:hover .upload-overlay': {
269
- opacity: $editable ? 1 : 0,
270
- },
271
- }));
@@ -1,267 +0,0 @@
1
- /* eslint-disable @typescript-eslint/naming-convention */
2
- /**
3
- * OrgMutateDialog - 组织创建/编辑弹窗
4
- *
5
- * 支持两种模式:
6
- * - 创建模式:新建组织,头像必填
7
- * - 编辑模式:编辑已有组织信息
8
- */
9
- import { useState, useEffect } from 'react';
10
- import PropTypes from 'prop-types';
11
- import { useReactive, useMemoizedFn } from 'ahooks';
12
- import noop from 'lodash/noop';
13
- import Dialog from '@arcblock/ux/lib/Dialog';
14
- import { CircularProgress, DialogContentText, Typography, TextField, Alert, Box } from '@mui/material';
15
- import Toast from '@arcblock/ux/lib/Toast';
16
- import Button from '@arcblock/ux/lib/Button';
17
- import { translate } from '@arcblock/ux/lib/Locale/util';
18
-
19
- import { formatAxiosError } from '../../UserCenter/libs/utils';
20
- import useOrg from './use-org';
21
- import translations from './locales';
22
- import AvatarUploader from './avatar-uploader';
23
-
24
- // 操作模式
25
- const MODE_CREATE = 'create';
26
- const MODE_EDIT = 'edit';
27
-
28
- export default function OrgMutateDialog({
29
- mode = MODE_CREATE,
30
- org = null,
31
- onSuccess = noop,
32
- onCancel = noop,
33
- locale = 'en',
34
- teamDid: teamDidProp = '',
35
- prefix = '/.well-known/service',
36
- headers = noop,
37
- }) {
38
- const isEditMode = mode === MODE_EDIT;
39
- const [loading, setLoading] = useState(false);
40
- const [error, setError] = useState('');
41
- const { createOrg, updateOrg } = useOrg();
42
-
43
- // 获取 teamDid:优先使用 prop,否则从 window.blocklet.did 获取
44
- // eslint-disable-next-line no-undef
45
- const teamDid = teamDidProp || (typeof window !== 'undefined' ? window.blocklet?.did : '');
46
-
47
- const t = useMemoizedFn((key, data = {}) => {
48
- return translate(translations, key, locale, 'en', data);
49
- });
50
-
51
- const form = useReactive({
52
- name: '',
53
- description: '',
54
- avatar: '',
55
- });
56
-
57
- // 编辑模式下初始化表单数据
58
- useEffect(() => {
59
- if (isEditMode && org) {
60
- form.name = org.name || '';
61
- form.description = org.description || '';
62
- form.avatar = org.avatar || '';
63
- }
64
- // eslint-disable-next-line react-hooks/exhaustive-deps
65
- }, [isEditMode, org]);
66
-
67
- // 头像变化处理
68
- const handleAvatarChange = useMemoizedFn((avatarPath) => {
69
- setError('');
70
- form.avatar = avatarPath;
71
- });
72
-
73
- // 表单验证
74
- const validateForm = useMemoizedFn(() => {
75
- const _name = form.name.trim();
76
- if (!_name) {
77
- setError(t('nameEmpty'));
78
- return false;
79
- }
80
-
81
- if (_name.length > 25) {
82
- setError(t('nameTooLong', { length: 25 }));
83
- return false;
84
- }
85
-
86
- const _description = form.description.trim();
87
- if (_description.length > 255) {
88
- setError(t('descriptionTooLong', { length: 255 }));
89
- return false;
90
- }
91
-
92
- // 头像必填验证
93
- if (!form.avatar) {
94
- setError(t('avatarEmpty'));
95
- return false;
96
- }
97
-
98
- return true;
99
- });
100
-
101
- // 提交处理
102
- const onSubmit = async () => {
103
- if (!validateForm()) {
104
- return;
105
- }
106
-
107
- setError('');
108
- setLoading(true);
109
-
110
- const _name = form.name.trim();
111
- const _description = form.description.trim();
112
- const _avatar = form.avatar;
113
-
114
- try {
115
- if (isEditMode && org?.id) {
116
- // 编辑模式:更新组织
117
- await updateOrg(org.id, {
118
- name: _name,
119
- description: _description,
120
- avatar: _avatar,
121
- });
122
- } else {
123
- // 创建模式:新建组织
124
- await createOrg({
125
- name: _name,
126
- description: _description,
127
- avatar: _avatar,
128
- });
129
- }
130
- onSuccess();
131
- } catch (err) {
132
- console.error(err);
133
- const errMsg = formatAxiosError(err);
134
- setError(errMsg);
135
- Toast.error(errMsg);
136
- } finally {
137
- setLoading(false);
138
- }
139
- };
140
-
141
- // 对话框标题
142
- const dialogTitle = isEditMode ? t('mutate.title', { mode: t('edit') }) : t('mutate.title', { mode: t('create') });
143
-
144
- // 提交按钮文本
145
- const submitButtonText = isEditMode ? t('save') : t('create');
146
-
147
- return (
148
- <Dialog
149
- title={dialogTitle}
150
- fullWidth
151
- open
152
- onClose={onCancel}
153
- showCloseButton={false}
154
- actions={
155
- <>
156
- <Button onClick={onCancel} color="inherit">
157
- {t('cancel')}
158
- </Button>
159
- <Button
160
- data-cy="mutate-org-confirm"
161
- onClick={onSubmit}
162
- color="primary"
163
- disabled={loading}
164
- variant="contained"
165
- autoFocus>
166
- {loading && <CircularProgress size={16} sx={{ mr: 1 }} />}
167
- {submitButtonText}
168
- </Button>
169
- </>
170
- }>
171
- <DialogContentText component="div">
172
- {/* 头像上传区域 */}
173
- <Box
174
- sx={{
175
- display: 'flex',
176
- flexDirection: 'column',
177
- alignItems: 'center',
178
- py: 2,
179
- }}>
180
- <AvatarUploader
181
- org={isEditMode ? org : { name: form.name }}
182
- size={90}
183
- teamDid={teamDid}
184
- prefix={prefix}
185
- headers={headers}
186
- onChange={handleAvatarChange}
187
- value={form.avatar}
188
- editable
189
- />
190
- <Typography variant="caption" color="text.secondary" sx={{ mt: 1 }}>
191
- {t('avatar')}
192
- </Typography>
193
- </Box>
194
-
195
- {/* 表单字段 */}
196
- <Typography component="div" style={{ marginTop: 16 }}>
197
- <TextField
198
- label={t('mutate.name')}
199
- autoComplete="off"
200
- variant="outlined"
201
- name="name"
202
- data-cy="mutate-org-input-name"
203
- fullWidth
204
- autoFocus
205
- value={form.name}
206
- onChange={(e) => {
207
- setError('');
208
- form.name = e.target.value;
209
- }}
210
- disabled={loading}
211
- required
212
- />
213
- </Typography>
214
-
215
- <Typography component="div" style={{ marginTop: 16, marginBottom: 16 }}>
216
- <TextField
217
- label={t('mutate.description')}
218
- autoComplete="off"
219
- variant="outlined"
220
- name="description"
221
- data-cy="mutate-org-input-description"
222
- fullWidth
223
- value={form.description}
224
- onChange={(e) => {
225
- setError('');
226
- form.description = e.target.value;
227
- }}
228
- disabled={loading}
229
- multiline
230
- rows={3}
231
- />
232
- </Typography>
233
- </DialogContentText>
234
-
235
- {!!error && (
236
- <Alert severity="error" style={{ width: '100%', margin: 0 }}>
237
- {error}
238
- </Alert>
239
- )}
240
- </Dialog>
241
- );
242
- }
243
-
244
- OrgMutateDialog.propTypes = {
245
- /** 操作模式:'create' 创建 | 'edit' 编辑 */
246
- mode: PropTypes.oneOf([MODE_CREATE, MODE_EDIT]),
247
- /** 编辑模式下的组织数据 */
248
- org: PropTypes.shape({
249
- id: PropTypes.string,
250
- name: PropTypes.string,
251
- description: PropTypes.string,
252
- avatar: PropTypes.string,
253
- }),
254
- onSuccess: PropTypes.func,
255
- onCancel: PropTypes.func,
256
- locale: PropTypes.string,
257
- teamDid: PropTypes.string,
258
- prefix: PropTypes.string,
259
- headers: PropTypes.func,
260
- };
261
-
262
- // 导出模式常量供外部使用
263
- OrgMutateDialog.MODE_CREATE = MODE_CREATE;
264
- OrgMutateDialog.MODE_EDIT = MODE_EDIT;
265
-
266
- // 保持向后兼容的别名
267
- export { OrgMutateDialog as CreateOrgDialog };