@blocklet/ui-react 2.11.44 → 2.11.46

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.
@@ -0,0 +1,17 @@
1
+ declare function DomainWarning({ locale, session }: {
2
+ locale: any;
3
+ session: any;
4
+ }): import("react").JSX.Element | null;
5
+ declare namespace DomainWarning {
6
+ namespace propTypes {
7
+ let locale: any;
8
+ let session: any;
9
+ }
10
+ namespace defaultProps {
11
+ let locale_1: string;
12
+ export { locale_1 as locale };
13
+ let session_1: {};
14
+ export { session_1 as session };
15
+ }
16
+ }
17
+ export default DomainWarning;
@@ -0,0 +1,161 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState, useCallback } from "react";
3
+ import PropTypes from "prop-types";
4
+ import Box from "@mui/material/Box";
5
+ import Typography from "@mui/material/Typography";
6
+ import Button from "@mui/material/Button";
7
+ import Dialog from "@mui/material/Dialog";
8
+ import DialogActions from "@mui/material/DialogActions";
9
+ import DialogContent from "@mui/material/DialogContent";
10
+ import { useMemoizedFn } from "ahooks";
11
+ import { translate } from "@arcblock/ux/lib/Locale/util";
12
+ import { joinURL } from "ufo";
13
+ import useMobile from "../hooks/use-mobile.js";
14
+ const isAdmin = ["admin", "owner"];
15
+ const isIpEcho = (hostname) => {
16
+ return hostname.endsWith(".ip.abtnet.io");
17
+ };
18
+ const isDidDomain = (hostname) => {
19
+ return hostname.endsWith(".did.abtnet.io");
20
+ };
21
+ const translations = {
22
+ en: {
23
+ guest: {
24
+ title: "Notice: You are using a temporary domain",
25
+ description: "You are accessing this site through a temporary domain. For a better experience, please contact the site administrator to bind a custom domain. Using a custom domain not only makes access more convenient but also ensures your access is more secure."
26
+ },
27
+ owner: {
28
+ title: "Enhance Website Security",
29
+ description: "Dear administrator, we recommend binding your custom domain soon, which will:",
30
+ benefits1: "Automatically obtain HTTPS certificates to ensure secure data transmission",
31
+ benefits2: "Create an exclusive brand image and increase website credibility",
32
+ benefits3: "Get a shorter, more memorable access address",
33
+ benefits4: "Provide visitors with a more professional experience",
34
+ benefits5: "Domain binding takes just minutes to complete, taking your website to the next level!"
35
+ },
36
+ skip: "Remind Me Later",
37
+ bindDomain: "Bind Domain"
38
+ },
39
+ zh: {
40
+ guest: {
41
+ title: "\u6E29\u99A8\u63D0\u793A\uFF1A\u5F53\u524D\u4F7F\u7528\u7684\u662F\u4E34\u65F6\u57DF\u540D",
42
+ description: "\u60A8\u6B63\u5728\u901A\u8FC7\u4E34\u65F6\u57DF\u540D\u8BBF\u95EE\u672C\u7AD9\u70B9\u3002\u4E3A\u4E86\u83B7\u5F97\u66F4\u597D\u7684\u8BBF\u95EE\u4F53\u9A8C\uFF0C\u8BF7\u8054\u7CFB\u7AD9\u70B9\u7BA1\u7406\u5458\u7ED1\u5B9A\u81EA\u5B9A\u4E49\u57DF\u540D\u3002\u4F7F\u7528\u81EA\u5B9A\u4E49\u57DF\u540D\u4E0D\u4EC5\u8BBF\u95EE\u66F4\u4FBF\u6377\uFF0C\u8FD8\u80FD\u786E\u4FDD\u60A8\u7684\u8BBF\u95EE\u66F4\u52A0\u5B89\u5168\u3002"
43
+ },
44
+ owner: {
45
+ title: "\u63D0\u5347\u7F51\u7AD9\u5B89\u5168\u6027\u4E0E\u4E13\u4E1A\u5EA6",
46
+ description: "\u5C0A\u656C\u7684\u7BA1\u7406\u5458\uFF0C\u6211\u4EEC\u5EFA\u8BAE\u60A8\u5C3D\u5FEB\u7ED1\u5B9A\u81EA\u5B9A\u4E49\u57DF\u540D\uFF0C\u8FD9\u6837\u53EF\u4EE5\uFF1A",
47
+ benefits1: "\u81EA\u52A8\u83B7\u53D6 HTTPS \u8BC1\u4E66\uFF0C\u786E\u4FDD\u6570\u636E\u4F20\u8F93\u5B89\u5168",
48
+ benefits2: "\u6253\u9020\u4E13\u5C5E\u54C1\u724C\u5F62\u8C61\uFF0C\u63D0\u5347\u7F51\u7AD9\u53EF\u4FE1\u5EA6",
49
+ benefits3: "\u83B7\u5F97\u66F4\u7B80\u77ED\u3001\u6613\u8BB0\u7684\u8BBF\u95EE\u5730\u5740",
50
+ benefits4: "\u4E3A\u8BBF\u5BA2\u63D0\u4F9B\u66F4\u4E13\u4E1A\u7684\u8BBF\u95EE\u4F53\u9A8C",
51
+ benefits5: "\u53EA\u9700\u51E0\u5206\u949F\u5373\u53EF\u5B8C\u6210\u57DF\u540D\u7ED1\u5B9A\uFF0C\u8BA9\u60A8\u7684\u7F51\u7AD9\u66F4\u4E0A\u4E00\u5C42\u697C\uFF01"
52
+ },
53
+ skip: "\u7A0D\u540E\u63D0\u9192",
54
+ bindDomain: "\u7ED1\u5B9A\u57DF\u540D"
55
+ }
56
+ };
57
+ const ONE_MONTH = 1e3 * 60 * 60 * 24 * 30;
58
+ const DASHBOARD_DOMAIN = ".well-known/service/admin/domains";
59
+ export default function DomainWarning({ locale, session }) {
60
+ const user = session?.user;
61
+ const isMobile = useMobile();
62
+ const [open, setOpen] = useState(() => {
63
+ const skip = window.localStorage.getItem("domain-warning-skip");
64
+ if (!skip)
65
+ return true;
66
+ const now = +/* @__PURE__ */ new Date();
67
+ const skipTime = +new Date(skip);
68
+ return now - skipTime > ONE_MONTH;
69
+ });
70
+ const t = useMemoizedFn((key, data = {}) => {
71
+ return translate(translations, key, locale, "en", data);
72
+ });
73
+ const host = useMemo(() => {
74
+ try {
75
+ const { hostname } = new URL(window.location.href);
76
+ return hostname;
77
+ } catch (error) {
78
+ return "";
79
+ }
80
+ }, []);
81
+ const benefits = useMemo(
82
+ () => [
83
+ t("owner.benefits1"),
84
+ t("owner.benefits2"),
85
+ t("owner.benefits3"),
86
+ t("owner.benefits4"),
87
+ t("owner.benefits5")
88
+ ],
89
+ [t]
90
+ );
91
+ const handleSkip = useCallback(() => {
92
+ window.localStorage.setItem("domain-warning-skip", (/* @__PURE__ */ new Date()).toISOString());
93
+ setOpen(false);
94
+ }, []);
95
+ const handleDomainConfig = useCallback(() => {
96
+ const adminUrl = joinURL(window.location.origin, DASHBOARD_DOMAIN);
97
+ if (adminUrl.startsWith("http")) {
98
+ window.open(adminUrl, "_blank");
99
+ }
100
+ setOpen(false);
101
+ }, []);
102
+ const isOwner = user?.role && isAdmin.includes(user.role);
103
+ if (window.location.href.includes(DASHBOARD_DOMAIN)) {
104
+ return null;
105
+ }
106
+ if (isMobile) {
107
+ return null;
108
+ }
109
+ if (!isIpEcho(host) && !isDidDomain(host)) {
110
+ return null;
111
+ }
112
+ return /* @__PURE__ */ jsxs(Dialog, { open, disableEscapeKeyDown: true, fullWidth: true, maxWidth: "sm", onClose: () => setOpen(false), children: [
113
+ /* @__PURE__ */ jsxs(DialogContent, { sx: { padding: "20px !important" }, children: [
114
+ /* @__PURE__ */ jsx(
115
+ Typography,
116
+ {
117
+ sx: {
118
+ fontSize: "20px",
119
+ fontWeight: "500"
120
+ },
121
+ children: isOwner ? t("owner.title") : t("guest.title")
122
+ }
123
+ ),
124
+ /* @__PURE__ */ jsx(
125
+ Typography,
126
+ {
127
+ sx: {
128
+ marginTop: "20px",
129
+ fontSize: "14px",
130
+ color: "#9397A1"
131
+ },
132
+ children: isOwner ? t("owner.description") : t("guest.description")
133
+ }
134
+ ),
135
+ isOwner && /* @__PURE__ */ jsx(Box, { component: "ul", children: benefits.map((benefit) => /* @__PURE__ */ jsx(
136
+ Typography,
137
+ {
138
+ component: "li",
139
+ sx: {
140
+ fontSize: "14px",
141
+ color: "#9397A1"
142
+ },
143
+ children: benefit
144
+ },
145
+ benefit
146
+ )) })
147
+ ] }),
148
+ /* @__PURE__ */ jsxs(DialogActions, { sx: { px: "12px !important" }, children: [
149
+ /* @__PURE__ */ jsx(Button, { onClick: handleSkip, children: t("skip") }),
150
+ isOwner && /* @__PURE__ */ jsx(Button, { variant: "contained", onClick: handleDomainConfig, children: t("bindDomain") })
151
+ ] })
152
+ ] });
153
+ }
154
+ DomainWarning.propTypes = {
155
+ locale: PropTypes.string,
156
+ session: PropTypes.object
157
+ };
158
+ DomainWarning.defaultProps = {
159
+ locale: "en",
160
+ session: {}
161
+ };
@@ -1,6 +1,7 @@
1
- declare function HeaderAddons({ formattedBlocklet, addons, sessionManagerProps }: {
1
+ declare function HeaderAddons({ formattedBlocklet, addons, showDomainWarning, sessionManagerProps }: {
2
2
  formattedBlocklet: any;
3
3
  addons: any;
4
+ showDomainWarning: any;
4
5
  sessionManagerProps: any;
5
6
  }): import("react").FunctionComponentElement<{
6
7
  children?: import("react").ReactNode | undefined;
@@ -10,6 +11,7 @@ declare namespace HeaderAddons {
10
11
  export let formattedBlocklet: any;
11
12
  export let addons: any;
12
13
  export { SessionManagerProps as sessionManagerProps };
14
+ export let showDomainWarning: any;
13
15
  }
14
16
  namespace defaultProps {
15
17
  let addons_1: null;
@@ -17,6 +19,8 @@ declare namespace HeaderAddons {
17
19
  export namespace sessionManagerProps {
18
20
  let showRole: boolean;
19
21
  }
22
+ let showDomainWarning_1: boolean;
23
+ export { showDomainWarning_1 as showDomainWarning };
20
24
  }
21
25
  }
22
26
  export default HeaderAddons;
@@ -11,11 +11,12 @@ import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
11
11
  import { SessionManagerProps } from "../types.js";
12
12
  import { getLocalizedNavigation, filterNavByRole } from "../blocklets.js";
13
13
  import NotificationAddon from "./notification-addon.js";
14
+ import DomainWarning from "./domain-warning.js";
14
15
  const hasNotification = () => {
15
16
  const navigations = window?.blocklet?.navigation ?? [];
16
17
  return !!navigations.find((n) => n.id === "/userCenter/notification");
17
18
  };
18
- export default function HeaderAddons({ formattedBlocklet, addons, sessionManagerProps }) {
19
+ export default function HeaderAddons({ formattedBlocklet, addons, showDomainWarning, sessionManagerProps }) {
19
20
  const sessionCtx = useContext(SessionContext);
20
21
  const { locale, languages } = useLocaleContext() || {};
21
22
  const { enableConnect = true, enableLocale = true } = formattedBlocklet;
@@ -68,12 +69,12 @@ export default function HeaderAddons({ formattedBlocklet, addons, sessionManager
68
69
  return addonsArray;
69
70
  };
70
71
  const renderedAddons = renderAddons();
71
- const addonList = createElement(
72
- Fragment,
73
- null,
74
- ...Array.isArray(renderedAddons) ? renderedAddons : [renderedAddons]
75
- );
76
- return addonList;
72
+ const nodes = Array.isArray(renderedAddons) ? renderedAddons : [renderedAddons];
73
+ const mergedNodes = [
74
+ showDomainWarning ? /* @__PURE__ */ jsx(DomainWarning, { session: sessionCtx?.session, locale }) : null,
75
+ ...nodes
76
+ ].filter(Boolean);
77
+ return createElement(Fragment, null, ...mergedNodes);
77
78
  }
78
79
  HeaderAddons.propTypes = {
79
80
  formattedBlocklet: PropTypes.object.isRequired,
@@ -81,11 +82,13 @@ HeaderAddons.propTypes = {
81
82
  // - PropTypes.func: 可以把自定义 addons 插在 session-manager 或 locale-selector (如果存在的话) 前/中/后
82
83
  // - PropTypes.node: 将 addons 原样传给 UX Header 组件
83
84
  addons: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
84
- sessionManagerProps: SessionManagerProps
85
+ sessionManagerProps: SessionManagerProps,
86
+ showDomainWarning: PropTypes.bool
85
87
  };
86
88
  HeaderAddons.defaultProps = {
87
89
  addons: null,
88
90
  sessionManagerProps: {
89
91
  showRole: true
90
- }
92
+ },
93
+ showDomainWarning: true
91
94
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/ui-react",
3
- "version": "2.11.44",
3
+ "version": "2.11.46",
4
4
  "description": "Some useful front-end web components that can be used in Blocklets.",
5
5
  "keywords": [
6
6
  "react",
@@ -33,8 +33,8 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@abtnode/constant": "^1.16.38",
36
- "@arcblock/bridge": "^2.11.44",
37
- "@arcblock/react-hooks": "^2.11.44",
36
+ "@arcblock/bridge": "^2.11.46",
37
+ "@arcblock/react-hooks": "^2.11.46",
38
38
  "@arcblock/ws": "^1.19.9",
39
39
  "@blocklet/did-space-react": "^1.0.16",
40
40
  "@iconify-icons/logos": "^1.2.36",
@@ -84,5 +84,5 @@
84
84
  "jest": "^29.7.0",
85
85
  "unbuild": "^2.0.0"
86
86
  },
87
- "gitHead": "ac208031ad00b2175432f4af61978bfcf2b48d88"
87
+ "gitHead": "289036b9e8092ce23314bece14c61c6ae8a7c757"
88
88
  }
@@ -0,0 +1,190 @@
1
+ import { useMemo, useState, useCallback } from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ import Box from '@mui/material/Box';
5
+ import Typography from '@mui/material/Typography';
6
+ import Button from '@mui/material/Button';
7
+ import Dialog from '@mui/material/Dialog';
8
+ import DialogActions from '@mui/material/DialogActions';
9
+ import DialogContent from '@mui/material/DialogContent';
10
+
11
+ import { useMemoizedFn } from 'ahooks';
12
+ import { translate } from '@arcblock/ux/lib/Locale/util';
13
+ import { joinURL } from 'ufo';
14
+
15
+ import useMobile from '../hooks/use-mobile';
16
+
17
+ const isAdmin = ['admin', 'owner'];
18
+
19
+ const isIpEcho = (hostname) => {
20
+ return hostname.endsWith('.ip.abtnet.io');
21
+ };
22
+
23
+ const isDidDomain = (hostname) => {
24
+ return hostname.endsWith('.did.abtnet.io');
25
+ };
26
+
27
+ const translations = {
28
+ en: {
29
+ guest: {
30
+ title: 'Notice: You are using a temporary domain',
31
+ description:
32
+ 'You are accessing this site through a temporary domain. For a better experience, please contact the site administrator to bind a custom domain. Using a custom domain not only makes access more convenient but also ensures your access is more secure.',
33
+ },
34
+ owner: {
35
+ title: 'Enhance Website Security',
36
+ description: 'Dear administrator, we recommend binding your custom domain soon, which will:',
37
+ benefits1: 'Automatically obtain HTTPS certificates to ensure secure data transmission',
38
+ benefits2: 'Create an exclusive brand image and increase website credibility',
39
+ benefits3: 'Get a shorter, more memorable access address',
40
+ benefits4: 'Provide visitors with a more professional experience',
41
+ benefits5: 'Domain binding takes just minutes to complete, taking your website to the next level!',
42
+ },
43
+ skip: 'Remind Me Later',
44
+ bindDomain: 'Bind Domain',
45
+ },
46
+ zh: {
47
+ guest: {
48
+ title: '温馨提示:当前使用的是临时域名',
49
+ description:
50
+ '您正在通过临时域名访问本站点。为了获得更好的访问体验,请联系站点管理员绑定自定义域名。使用自定义域名不仅访问更便捷,还能确保您的访问更加安全。',
51
+ },
52
+ owner: {
53
+ title: '提升网站安全性与专业度',
54
+ description: '尊敬的管理员,我们建议您尽快绑定自定义域名,这样可以:',
55
+ benefits1: '自动获取 HTTPS 证书,确保数据传输安全',
56
+ benefits2: '打造专属品牌形象,提升网站可信度',
57
+ benefits3: '获得更简短、易记的访问地址',
58
+ benefits4: '为访客提供更专业的访问体验',
59
+ benefits5: '只需几分钟即可完成域名绑定,让您的网站更上一层楼!',
60
+ },
61
+ skip: '稍后提醒',
62
+ bindDomain: '绑定域名',
63
+ },
64
+ };
65
+
66
+ const ONE_MONTH = 1000 * 60 * 60 * 24 * 30;
67
+
68
+ const DASHBOARD_DOMAIN = '.well-known/service/admin/domains';
69
+
70
+ export default function DomainWarning({ locale, session }) {
71
+ const user = session?.user;
72
+ const isMobile = useMobile();
73
+
74
+ const [open, setOpen] = useState(() => {
75
+ const skip = window.localStorage.getItem('domain-warning-skip');
76
+ if (!skip) return true;
77
+
78
+ const now = +new Date();
79
+ const skipTime = +new Date(skip);
80
+ return now - skipTime > ONE_MONTH;
81
+ });
82
+
83
+ const t = useMemoizedFn((key, data = {}) => {
84
+ return translate(translations, key, locale, 'en', data);
85
+ });
86
+
87
+ const host = useMemo(() => {
88
+ try {
89
+ const { hostname } = new URL(window.location.href);
90
+ return hostname;
91
+ } catch (error) {
92
+ return '';
93
+ }
94
+ }, []);
95
+
96
+ const benefits = useMemo(
97
+ () => [
98
+ t('owner.benefits1'),
99
+ t('owner.benefits2'),
100
+ t('owner.benefits3'),
101
+ t('owner.benefits4'),
102
+ t('owner.benefits5'),
103
+ ],
104
+ [t]
105
+ );
106
+
107
+ const handleSkip = useCallback(() => {
108
+ window.localStorage.setItem('domain-warning-skip', new Date().toISOString());
109
+ setOpen(false);
110
+ }, []);
111
+
112
+ const handleDomainConfig = useCallback(() => {
113
+ const adminUrl = joinURL(window.location.origin, DASHBOARD_DOMAIN);
114
+ if (adminUrl.startsWith('http')) {
115
+ window.open(adminUrl, '_blank');
116
+ }
117
+ setOpen(false);
118
+ }, []);
119
+
120
+ const isOwner = user?.role && isAdmin.includes(user.role);
121
+
122
+ if (window.location.href.includes(DASHBOARD_DOMAIN)) {
123
+ return null;
124
+ }
125
+
126
+ if (isMobile) {
127
+ return null;
128
+ }
129
+
130
+ if (!isIpEcho(host) && !isDidDomain(host)) {
131
+ return null;
132
+ }
133
+
134
+ return (
135
+ <Dialog open={open} disableEscapeKeyDown fullWidth maxWidth="sm" onClose={() => setOpen(false)}>
136
+ <DialogContent sx={{ padding: '20px !important' }}>
137
+ <Typography
138
+ sx={{
139
+ fontSize: '20px',
140
+ fontWeight: '500',
141
+ }}>
142
+ {isOwner ? t('owner.title') : t('guest.title')}
143
+ </Typography>
144
+ <Typography
145
+ sx={{
146
+ marginTop: '20px',
147
+ fontSize: '14px',
148
+ color: '#9397A1',
149
+ }}>
150
+ {isOwner ? t('owner.description') : t('guest.description')}
151
+ </Typography>
152
+
153
+ {isOwner && (
154
+ <Box component="ul">
155
+ {benefits.map((benefit) => (
156
+ <Typography
157
+ component="li"
158
+ key={benefit}
159
+ sx={{
160
+ fontSize: '14px',
161
+ color: '#9397A1',
162
+ }}>
163
+ {benefit}
164
+ </Typography>
165
+ ))}
166
+ </Box>
167
+ )}
168
+ </DialogContent>
169
+ <DialogActions sx={{ px: '12px !important' }}>
170
+ <Button onClick={handleSkip}>{t('skip')}</Button>
171
+
172
+ {isOwner && (
173
+ <Button variant="contained" onClick={handleDomainConfig}>
174
+ {t('bindDomain')}
175
+ </Button>
176
+ )}
177
+ </DialogActions>
178
+ </Dialog>
179
+ );
180
+ }
181
+
182
+ DomainWarning.propTypes = {
183
+ locale: PropTypes.string,
184
+ session: PropTypes.object,
185
+ };
186
+
187
+ DomainWarning.defaultProps = {
188
+ locale: 'en',
189
+ session: {},
190
+ };
@@ -12,6 +12,7 @@ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
12
12
  import { SessionManagerProps } from '../types';
13
13
  import { getLocalizedNavigation, filterNavByRole } from '../blocklets';
14
14
  import NotificationAddon from './notification-addon';
15
+ import DomainWarning from './domain-warning';
15
16
 
16
17
  const hasNotification = () => {
17
18
  const navigations = window?.blocklet?.navigation ?? [];
@@ -19,7 +20,7 @@ const hasNotification = () => {
19
20
  };
20
21
 
21
22
  // eslint-disable-next-line no-shadow
22
- export default function HeaderAddons({ formattedBlocklet, addons, sessionManagerProps }) {
23
+ export default function HeaderAddons({ formattedBlocklet, addons, showDomainWarning, sessionManagerProps }) {
23
24
  const sessionCtx = useContext(SessionContext);
24
25
  const { locale, languages } = useLocaleContext() || {};
25
26
  const { enableConnect = true, enableLocale = true } = formattedBlocklet;
@@ -38,10 +39,12 @@ export default function HeaderAddons({ formattedBlocklet, addons, sessionManager
38
39
  if (hasNotification()) {
39
40
  addonsArray.push(<NotificationAddon key="notification-addon" session={sessionCtx.session} />);
40
41
  }
42
+
41
43
  // 启用了多语言,且检测到了 locale context,且有多种语言可以切换
42
44
  if (enableLocale && locale && languages.length > 1) {
43
45
  addonsArray.push(<LocaleSelector key="locale-selector" showText={false} />);
44
46
  }
47
+
45
48
  // 启用了连接钱包并且检测到了 session context
46
49
  if (enableConnect && sessionCtx) {
47
50
  const menu = [];
@@ -57,7 +60,9 @@ export default function HeaderAddons({ formattedBlocklet, addons, sessionManager
57
60
  });
58
61
  });
59
62
  }
63
+
60
64
  addonsArray.push(<SessionBlocklet key="session-blocklet" session={sessionCtx.session} locale={locale} />);
65
+
61
66
  addonsArray.push(
62
67
  <SessionUser
63
68
  key="session-user"
@@ -69,20 +74,22 @@ export default function HeaderAddons({ formattedBlocklet, addons, sessionManager
69
74
  />
70
75
  );
71
76
  }
72
- // 在内置 addons 基础上定制 addons
77
+
73
78
  if (typeof addons === 'function') {
74
79
  addonsArray = addons(addonsArray) || [];
75
80
  }
81
+
76
82
  return addonsArray;
77
83
  };
78
84
 
79
85
  const renderedAddons = renderAddons();
80
- const addonList = createElement(
81
- Fragment,
82
- null,
83
- ...(Array.isArray(renderedAddons) ? renderedAddons : [renderedAddons])
84
- );
85
- return addonList;
86
+ const nodes = Array.isArray(renderedAddons) ? renderedAddons : [renderedAddons];
87
+ const mergedNodes = [
88
+ showDomainWarning ? <DomainWarning session={sessionCtx?.session} locale={locale} /> : null,
89
+ ...nodes,
90
+ ].filter(Boolean);
91
+
92
+ return createElement(Fragment, null, ...mergedNodes);
86
93
  }
87
94
 
88
95
  HeaderAddons.propTypes = {
@@ -92,6 +99,7 @@ HeaderAddons.propTypes = {
92
99
  // - PropTypes.node: 将 addons 原样传给 UX Header 组件
93
100
  addons: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
94
101
  sessionManagerProps: SessionManagerProps,
102
+ showDomainWarning: PropTypes.bool,
95
103
  };
96
104
 
97
105
  HeaderAddons.defaultProps = {
@@ -99,4 +107,5 @@ HeaderAddons.defaultProps = {
99
107
  sessionManagerProps: {
100
108
  showRole: true,
101
109
  },
110
+ showDomainWarning: true,
102
111
  };