@blocklet/ui-react 2.12.63 → 2.12.70
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.
- package/lib/Footer/index.js +6 -14
- package/lib/Footer/links.js +5 -5
- package/lib/Footer/social-media.js +2 -2
- package/lib/Header/index.js +8 -8
- package/lib/UserCenter/components/editable-field.d.ts +2 -1
- package/lib/UserCenter/components/editable-field.js +3 -2
- package/lib/UserCenter/components/user-info/metadata.js +6 -4
- package/lib/common/notification-addon.js +0 -2
- package/package.json +4 -4
- package/src/Footer/index.jsx +8 -13
- package/src/Footer/links.jsx +5 -5
- package/src/Footer/social-media.jsx +2 -2
- package/src/Header/index.tsx +9 -9
- package/src/UserCenter/components/editable-field.tsx +3 -1
- package/src/UserCenter/components/user-info/metadata.tsx +16 -12
- package/src/common/notification-addon.jsx +0 -2
- package/lib/UserCenter/components/user-info/clock.d.ts +0 -3
- package/lib/UserCenter/components/user-info/clock.js +0 -50
- package/lib/hooks/use-clock.d.ts +0 -9
- package/lib/hooks/use-clock.js +0 -49
- package/src/UserCenter/components/user-info/clock.tsx +0 -43
- package/src/hooks/use-clock.tsx +0 -61
package/lib/Footer/index.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
|
-
import { styled } from "@arcblock/ux/lib/Theme";
|
|
4
|
+
import { styled, useTheme, deepmerge, ThemeProvider } from "@arcblock/ux/lib/Theme";
|
|
5
5
|
import { withErrorBoundary } from "react-error-boundary";
|
|
6
6
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
7
7
|
import { ErrorFallback } from "@arcblock/ux/lib/ErrorBoundary";
|
|
8
8
|
import { temp as colors } from "@arcblock/ux/lib/Colors";
|
|
9
9
|
import omit from "lodash/omit";
|
|
10
|
-
import OverridableThemeProvider from "../common/overridable-theme-provider.js";
|
|
11
10
|
import InternalFooter from "./internal-footer.js";
|
|
12
11
|
import { mapRecursive } from "../utils.js";
|
|
13
12
|
import { formatBlockletInfo, getLocalizedNavigation } from "../blocklets.js";
|
|
@@ -15,6 +14,7 @@ import { BlockletMetaProps } from "../types.js";
|
|
|
15
14
|
import withHideWhenEmbed from "../libs/with-hide-when-embed.js";
|
|
16
15
|
function Footer({ meta, theme: themeOverrides, ...rest }) {
|
|
17
16
|
const { locale } = useLocaleContext() || {};
|
|
17
|
+
const parentTheme = useTheme();
|
|
18
18
|
const formattedBlocklet = useMemo(() => {
|
|
19
19
|
const blocklet = Object.assign({}, window.blocklet, meta);
|
|
20
20
|
try {
|
|
@@ -24,10 +24,12 @@ function Footer({ meta, theme: themeOverrides, ...rest }) {
|
|
|
24
24
|
return blocklet;
|
|
25
25
|
}
|
|
26
26
|
}, [meta]);
|
|
27
|
+
const mergeTheme = useMemo(() => deepmerge(parentTheme, themeOverrides), [parentTheme, themeOverrides]);
|
|
27
28
|
if (!formattedBlocklet.appName) {
|
|
28
29
|
return null;
|
|
29
30
|
}
|
|
30
|
-
const { appLogo, appLogoRect, appName, appDescription, description,
|
|
31
|
+
const { appLogo, appLogoRect, appName, appDescription, description, copyright } = formattedBlocklet;
|
|
32
|
+
const $bgColor = mergeTheme.palette.background.default;
|
|
31
33
|
const localized = {
|
|
32
34
|
footerNav: getLocalizedNavigation(formattedBlocklet?.navigation?.footer, locale) || [],
|
|
33
35
|
socialMedia: getLocalizedNavigation(formattedBlocklet?.navigation?.social, locale) || [],
|
|
@@ -52,15 +54,7 @@ function Footer({ meta, theme: themeOverrides, ...rest }) {
|
|
|
52
54
|
socialMedia: localized.socialMedia,
|
|
53
55
|
links: localized.links.map((item) => ({ ...item, label: item.title }))
|
|
54
56
|
};
|
|
55
|
-
return /* @__PURE__ */ jsx(
|
|
56
|
-
StyledInternalFooter,
|
|
57
|
-
{
|
|
58
|
-
...props,
|
|
59
|
-
...omit(rest, ["bordered"]),
|
|
60
|
-
$bordered: rest?.bordered,
|
|
61
|
-
$bgcolor: theme?.background?.footer
|
|
62
|
-
}
|
|
63
|
-
) });
|
|
57
|
+
return /* @__PURE__ */ jsx(ThemeProvider, { theme: mergeTheme, children: /* @__PURE__ */ jsx(StyledInternalFooter, { ...props, ...omit(rest, ["bordered"]), $bordered: rest?.bordered, $bgcolor: $bgColor }) });
|
|
64
58
|
}
|
|
65
59
|
Footer.propTypes = {
|
|
66
60
|
meta: BlockletMetaProps,
|
|
@@ -75,8 +69,6 @@ const StyledInternalFooter = styled(InternalFooter)`
|
|
|
75
69
|
${({ $bordered }) => `border-top: 1px solid ${$bordered ? colors.strokeSep : "#eee"};`}
|
|
76
70
|
color: ${(props) => props.theme.palette.grey[600]};
|
|
77
71
|
${({ $bgcolor }) => $bgcolor && `background-color: ${$bgcolor};`}
|
|
78
|
-
font-family: Inter, Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,
|
|
79
|
-
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
|
80
72
|
`;
|
|
81
73
|
export default withErrorBoundary(withHideWhenEmbed(Footer), {
|
|
82
74
|
FallbackComponent: ErrorFallback
|
package/lib/Footer/links.js
CHANGED
|
@@ -128,7 +128,7 @@ Links.defaultProps = {
|
|
|
128
128
|
};
|
|
129
129
|
const Root = styled("div")`
|
|
130
130
|
overflow: hidden;
|
|
131
|
-
color:
|
|
131
|
+
color: ${({ theme }) => theme.palette.grey[500]};
|
|
132
132
|
.footer-links-inner {
|
|
133
133
|
display: flex;
|
|
134
134
|
justify-content: space-between;
|
|
@@ -159,8 +159,8 @@ const Root = styled("div")`
|
|
|
159
159
|
font-size: 14px;
|
|
160
160
|
&--new::after {
|
|
161
161
|
content: 'New';
|
|
162
|
-
color:
|
|
163
|
-
background-color:
|
|
162
|
+
color: ${({ theme }) => theme.palette.info.main};
|
|
163
|
+
background-color: ${({ theme }) => theme.palette.info.light};
|
|
164
164
|
padding: 1px 8px;
|
|
165
165
|
border-radius: 10px/50%;
|
|
166
166
|
margin-left: 8px;
|
|
@@ -170,7 +170,7 @@ const Root = styled("div")`
|
|
|
170
170
|
.footer-links-group {
|
|
171
171
|
> .footer-links-item {
|
|
172
172
|
font-weight: 600;
|
|
173
|
-
color:
|
|
173
|
+
color: ${({ theme }) => theme.palette.text.primary};
|
|
174
174
|
}
|
|
175
175
|
.footer-links-sub {
|
|
176
176
|
margin-top: 8px;
|
|
@@ -184,7 +184,7 @@ const Root = styled("div")`
|
|
|
184
184
|
text-decoration: none;
|
|
185
185
|
transition: color 0.2s ease-in-out;
|
|
186
186
|
&:hover {
|
|
187
|
-
color:
|
|
187
|
+
color: ${({ theme }) => theme.palette.text.primary};
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
/* columns 布局 */
|
|
@@ -48,11 +48,11 @@ const Root = styled("div")`
|
|
|
48
48
|
justify-content: center;
|
|
49
49
|
gap: 20px;
|
|
50
50
|
a {
|
|
51
|
-
color: ${(props) => props.theme.palette.grey[
|
|
51
|
+
color: ${(props) => props.theme.palette.grey[500]};
|
|
52
52
|
text-decoration: none;
|
|
53
53
|
transition: color 0.2s ease-in-out;
|
|
54
54
|
&:hover {
|
|
55
|
-
color:
|
|
55
|
+
color: ${({ theme }) => theme.palette.text.primary};
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
${(props) => props.theme.breakpoints.down("md")} {
|
package/lib/Header/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { useMemo } from "react";
|
|
|
3
3
|
import { useMemoizedFn } from "ahooks";
|
|
4
4
|
import { withErrorBoundary } from "react-error-boundary";
|
|
5
5
|
import { ErrorFallback } from "@arcblock/ux/lib/ErrorBoundary";
|
|
6
|
-
import { styled } from "@arcblock/ux/lib/Theme";
|
|
6
|
+
import { styled, useTheme, deepmerge, ThemeProvider } from "@arcblock/ux/lib/Theme";
|
|
7
7
|
import { ResponsiveHeader } from "@arcblock/ux/lib/Header";
|
|
8
8
|
import NavMenu, { Products } from "@arcblock/ux/lib/NavMenu";
|
|
9
9
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
@@ -13,7 +13,6 @@ import omit from "lodash/omit";
|
|
|
13
13
|
import isFinite from "lodash/isFinite";
|
|
14
14
|
import clsx from "clsx";
|
|
15
15
|
import Icon from "../Icon/index.js";
|
|
16
|
-
import OverridableThemeProvider from "../common/overridable-theme-provider.js";
|
|
17
16
|
import { mapRecursive, flatRecursive, matchPaths } from "../utils.js";
|
|
18
17
|
import { publicPath, formatBlockletInfo, getLocalizedNavigation } from "../blocklets.js";
|
|
19
18
|
import HeaderAddons from "../common/header-addons.js";
|
|
@@ -76,6 +75,7 @@ function Header({
|
|
|
76
75
|
...rest
|
|
77
76
|
}) {
|
|
78
77
|
useWalletHiddenTopbar();
|
|
78
|
+
const parentTheme = useTheme();
|
|
79
79
|
const { locale } = useLocaleContext() || {};
|
|
80
80
|
const t = useMemoizedFn((key, data = {}) => {
|
|
81
81
|
return translate(translations, key, locale, "en", data);
|
|
@@ -90,10 +90,11 @@ function Header({
|
|
|
90
90
|
}
|
|
91
91
|
}, [meta]);
|
|
92
92
|
const isMobileDevice = useMobile();
|
|
93
|
+
const mergeTheme = useMemo(() => deepmerge(parentTheme, themeOverrides), [parentTheme, themeOverrides]);
|
|
93
94
|
if (!formattedBlocklet.appName) {
|
|
94
95
|
return null;
|
|
95
96
|
}
|
|
96
|
-
const { appLogo, appLogoRect
|
|
97
|
+
const { appLogo, appLogoRect } = formattedBlocklet;
|
|
97
98
|
const navigation = getLocalizedNavigation(formattedBlocklet?.navigation?.header, locale);
|
|
98
99
|
const parsedNavigation = parseNavigation(navigation);
|
|
99
100
|
const { navItems, activeId } = parsedNavigation;
|
|
@@ -118,7 +119,7 @@ function Header({
|
|
|
118
119
|
}
|
|
119
120
|
)
|
|
120
121
|
);
|
|
121
|
-
return /* @__PURE__ */ jsx(
|
|
122
|
+
return /* @__PURE__ */ jsx(ThemeProvider, { theme: mergeTheme, children: /* @__PURE__ */ jsx(
|
|
122
123
|
StyledUxHeader,
|
|
123
124
|
{
|
|
124
125
|
homeLink,
|
|
@@ -126,7 +127,7 @@ function Header({
|
|
|
126
127
|
addons: headerAddons,
|
|
127
128
|
...omit(rest, ["bordered"]),
|
|
128
129
|
$bordered: rest?.bordered,
|
|
129
|
-
$bgcolor:
|
|
130
|
+
$bgcolor: mergeTheme.palette.background.default,
|
|
130
131
|
className: clsx("blocklet__header", rest.className),
|
|
131
132
|
children: hideNavMenu || !navItems?.length ? null : ({ isMobile }) => (
|
|
132
133
|
// @ts-ignore
|
|
@@ -138,7 +139,8 @@ function Header({
|
|
|
138
139
|
items: navItems,
|
|
139
140
|
className: "header-nav",
|
|
140
141
|
bgColor: "transparent",
|
|
141
|
-
textColor:
|
|
142
|
+
textColor: mergeTheme.palette.grey[500],
|
|
143
|
+
activeTextColor: mergeTheme.palette.text.primary
|
|
142
144
|
}
|
|
143
145
|
)
|
|
144
146
|
)
|
|
@@ -147,8 +149,6 @@ function Header({
|
|
|
147
149
|
}
|
|
148
150
|
const StyledUxHeader = styled(ResponsiveHeader)`
|
|
149
151
|
${({ $bgcolor }) => `background-color: ${$bgcolor || "#fff"};`}
|
|
150
|
-
font-family: Inter, Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,
|
|
151
|
-
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
|
152
152
|
.header-logo {
|
|
153
153
|
min-width: 44px;
|
|
154
154
|
}
|
|
@@ -19,6 +19,7 @@ interface EditableFieldProps {
|
|
|
19
19
|
verified?: boolean;
|
|
20
20
|
errorMsg?: string;
|
|
21
21
|
canEdit?: boolean;
|
|
22
|
+
hidePreview?: boolean;
|
|
22
23
|
renderValue?: (value: string) => React.ReactNode;
|
|
23
24
|
}
|
|
24
25
|
export declare const commonInputStyle: {
|
|
@@ -47,5 +48,5 @@ export declare const inputFieldStyle: {
|
|
|
47
48
|
borderColor: string;
|
|
48
49
|
};
|
|
49
50
|
};
|
|
50
|
-
declare function EditableField({ value, onChange, onValueValidate, errorMsg, editable, component, placeholder, rows, maxLength, icon, label, children, tooltip, inline, style, verified, canEdit, renderValue, disabled, }: EditableFieldProps): JSX.Element | null;
|
|
51
|
+
declare function EditableField({ value, onChange, onValueValidate, errorMsg, editable, component, placeholder, rows, maxLength, icon, label, children, tooltip, inline, style, verified, canEdit, renderValue, disabled, hidePreview, }: EditableFieldProps): JSX.Element | null;
|
|
51
52
|
export default EditableField;
|
|
@@ -55,7 +55,8 @@ function EditableField({
|
|
|
55
55
|
verified = false,
|
|
56
56
|
canEdit = true,
|
|
57
57
|
renderValue,
|
|
58
|
-
disabled = false
|
|
58
|
+
disabled = false,
|
|
59
|
+
hidePreview = false
|
|
59
60
|
}) {
|
|
60
61
|
const { locale } = useLocaleContext();
|
|
61
62
|
const t = useMemoizedFn((key, data = {}) => {
|
|
@@ -145,7 +146,7 @@ function EditableField({
|
|
|
145
146
|
return null;
|
|
146
147
|
}
|
|
147
148
|
if (!editable) {
|
|
148
|
-
return value ? /* @__PURE__ */ jsx(
|
|
149
|
+
return value && !hidePreview ? /* @__PURE__ */ jsx(
|
|
149
150
|
Tooltip,
|
|
150
151
|
{
|
|
151
152
|
open: Boolean(mousePosition),
|
|
@@ -22,12 +22,12 @@ import isEmail from "validator/lib/isEmail";
|
|
|
22
22
|
import isPostalCode from "validator/lib/isPostalCode";
|
|
23
23
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
24
24
|
import { useBrowser } from "@arcblock/react-hooks";
|
|
25
|
+
import Clock from "@arcblock/ux/lib/UserCard/Content/clock";
|
|
25
26
|
import { translations } from "../../libs/locales.js";
|
|
26
27
|
import EditableField, { commonInputStyle, inputFieldStyle } from "../editable-field.js";
|
|
27
28
|
import { LinkPreviewInput } from "./link-preview-input.js";
|
|
28
29
|
import { currentTimezone, defaultButtonStyle, primaryButtonStyle } from "./utils.js";
|
|
29
30
|
import { TimezoneSelect } from "./timezone-select.js";
|
|
30
|
-
import Clock from "./clock.js";
|
|
31
31
|
import AddressEditor from "./address.js";
|
|
32
32
|
const LocationIcon = lazy(() => import("@arcblock/icons/lib/Location"));
|
|
33
33
|
const TimezoneIcon = lazy(() => import("@arcblock/icons/lib/Timezone"));
|
|
@@ -363,6 +363,7 @@ export default function UserMetadataComponent({
|
|
|
363
363
|
{
|
|
364
364
|
value: metadata.email ?? user?.email ?? "",
|
|
365
365
|
editable: editing,
|
|
366
|
+
hidePreview: !isMyself,
|
|
366
367
|
disabled: user?.sourceProvider === LOGIN_PROVIDER.EMAIL,
|
|
367
368
|
canEdit: !emailVerified,
|
|
368
369
|
verified: emailVerified,
|
|
@@ -384,7 +385,7 @@ export default function UserMetadataComponent({
|
|
|
384
385
|
] }),
|
|
385
386
|
onChange: (value) => onChange(value, "email"),
|
|
386
387
|
errorMsg: validateMsg.email,
|
|
387
|
-
renderValue: (value) => /* @__PURE__ */ jsx(
|
|
388
|
+
renderValue: (value) => isMyself ? /* @__PURE__ */ jsx(
|
|
388
389
|
"a",
|
|
389
390
|
{
|
|
390
391
|
href: `mailto:${value}`,
|
|
@@ -394,7 +395,7 @@ export default function UserMetadataComponent({
|
|
|
394
395
|
},
|
|
395
396
|
children: value
|
|
396
397
|
}
|
|
397
|
-
),
|
|
398
|
+
) : null,
|
|
398
399
|
onValueValidate: (value) => {
|
|
399
400
|
let msg = "";
|
|
400
401
|
if (!!value && !isEmail(value)) {
|
|
@@ -409,6 +410,7 @@ export default function UserMetadataComponent({
|
|
|
409
410
|
{
|
|
410
411
|
value: phoneValue.phone,
|
|
411
412
|
editable: editing,
|
|
413
|
+
hidePreview: !isMyself,
|
|
412
414
|
canEdit: !phoneVerified,
|
|
413
415
|
verified: phoneVerified,
|
|
414
416
|
placeholder: "Phone",
|
|
@@ -416,7 +418,7 @@ export default function UserMetadataComponent({
|
|
|
416
418
|
onChange: (value) => onChange(value, "phone"),
|
|
417
419
|
label: t("profile.phone"),
|
|
418
420
|
renderValue: () => {
|
|
419
|
-
return /* @__PURE__ */ jsx(PhoneInput, { value: phoneValue, preview: true });
|
|
421
|
+
return isMyself ? /* @__PURE__ */ jsx(PhoneInput, { value: phoneValue, preview: true }) : null;
|
|
420
422
|
},
|
|
421
423
|
children: /* @__PURE__ */ jsx(
|
|
422
424
|
PhoneInput,
|
|
@@ -2,7 +2,6 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import Badge from "@mui/material/Badge";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { useCallback, useEffect } from "react";
|
|
5
|
-
import { temp as colors } from "@arcblock/ux/lib/Colors";
|
|
6
5
|
import { IconButton } from "@mui/material";
|
|
7
6
|
import { useSnackbar } from "notistack";
|
|
8
7
|
import NotificationsOutlinedIcon from "@arcblock/icons/lib/Notification";
|
|
@@ -89,7 +88,6 @@ export default function NotificationAddon({ session = {} }) {
|
|
|
89
88
|
variant: "outlined",
|
|
90
89
|
href: viewAllUrl,
|
|
91
90
|
sx: {
|
|
92
|
-
borderColor: colors.lineBorderStrong,
|
|
93
91
|
"&:hover": {
|
|
94
92
|
borderRadius: "50%"
|
|
95
93
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/ui-react",
|
|
3
|
-
"version": "2.12.
|
|
3
|
+
"version": "2.12.70",
|
|
4
4
|
"description": "Some useful front-end web components that can be used in Blocklets.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@abtnode/constant": "^1.16.41",
|
|
36
36
|
"@abtnode/util": "^1.16.41",
|
|
37
|
-
"@arcblock/bridge": "^2.12.
|
|
38
|
-
"@arcblock/react-hooks": "^2.12.
|
|
37
|
+
"@arcblock/bridge": "^2.12.70",
|
|
38
|
+
"@arcblock/react-hooks": "^2.12.70",
|
|
39
39
|
"@arcblock/ws": "^1.19.19",
|
|
40
40
|
"@blocklet/constant": "^1.16.42-beta-20250408-072924-4b6a877a",
|
|
41
41
|
"@blocklet/did-space-react": "^1.0.43",
|
|
@@ -94,5 +94,5 @@
|
|
|
94
94
|
"jest": "^29.7.0",
|
|
95
95
|
"unbuild": "^2.0.0"
|
|
96
96
|
},
|
|
97
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "89e31e2599d8ab7462307771239849a6edd19d7f"
|
|
98
98
|
}
|
package/src/Footer/index.jsx
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import { styled } from '@arcblock/ux/lib/Theme';
|
|
3
|
+
import { styled, useTheme, deepmerge, ThemeProvider } from '@arcblock/ux/lib/Theme';
|
|
4
4
|
import { withErrorBoundary } from 'react-error-boundary';
|
|
5
5
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
6
6
|
import { ErrorFallback } from '@arcblock/ux/lib/ErrorBoundary';
|
|
7
7
|
import { temp as colors } from '@arcblock/ux/lib/Colors';
|
|
8
8
|
import omit from 'lodash/omit';
|
|
9
9
|
|
|
10
|
-
import OverridableThemeProvider from '../common/overridable-theme-provider';
|
|
11
10
|
import InternalFooter from './internal-footer';
|
|
12
11
|
import { mapRecursive } from '../utils';
|
|
13
12
|
import { formatBlockletInfo, getLocalizedNavigation } from '../blocklets';
|
|
@@ -19,6 +18,7 @@ import withHideWhenEmbed from '../libs/with-hide-when-embed';
|
|
|
19
18
|
*/
|
|
20
19
|
function Footer({ meta, theme: themeOverrides, ...rest }) {
|
|
21
20
|
const { locale } = useLocaleContext() || {};
|
|
21
|
+
const parentTheme = useTheme();
|
|
22
22
|
const formattedBlocklet = useMemo(() => {
|
|
23
23
|
const blocklet = Object.assign({}, window.blocklet, meta);
|
|
24
24
|
try {
|
|
@@ -28,12 +28,14 @@ function Footer({ meta, theme: themeOverrides, ...rest }) {
|
|
|
28
28
|
return blocklet;
|
|
29
29
|
}
|
|
30
30
|
}, [meta]);
|
|
31
|
+
const mergeTheme = useMemo(() => deepmerge(parentTheme, themeOverrides), [parentTheme, themeOverrides]);
|
|
31
32
|
|
|
32
33
|
if (!formattedBlocklet.appName) {
|
|
33
34
|
return null;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
const { appLogo, appLogoRect, appName, appDescription, description,
|
|
37
|
+
const { appLogo, appLogoRect, appName, appDescription, description, copyright } = formattedBlocklet;
|
|
38
|
+
const $bgColor = mergeTheme.palette.background.default;
|
|
37
39
|
|
|
38
40
|
const localized = {
|
|
39
41
|
footerNav: getLocalizedNavigation(formattedBlocklet?.navigation?.footer, locale) || [],
|
|
@@ -62,14 +64,9 @@ function Footer({ meta, theme: themeOverrides, ...rest }) {
|
|
|
62
64
|
};
|
|
63
65
|
|
|
64
66
|
return (
|
|
65
|
-
<
|
|
66
|
-
<StyledInternalFooter
|
|
67
|
-
|
|
68
|
-
{...omit(rest, ['bordered'])}
|
|
69
|
-
$bordered={rest?.bordered}
|
|
70
|
-
$bgcolor={theme?.background?.footer}
|
|
71
|
-
/>
|
|
72
|
-
</OverridableThemeProvider>
|
|
67
|
+
<ThemeProvider theme={mergeTheme}>
|
|
68
|
+
<StyledInternalFooter {...props} {...omit(rest, ['bordered'])} $bordered={rest?.bordered} $bgcolor={$bgColor} />
|
|
69
|
+
</ThemeProvider>
|
|
73
70
|
);
|
|
74
71
|
}
|
|
75
72
|
|
|
@@ -88,8 +85,6 @@ const StyledInternalFooter = styled(InternalFooter)`
|
|
|
88
85
|
${({ $bordered }) => `border-top: 1px solid ${$bordered ? colors.strokeSep : '#eee'};`}
|
|
89
86
|
color: ${(props) => props.theme.palette.grey[600]};
|
|
90
87
|
${({ $bgcolor }) => $bgcolor && `background-color: ${$bgcolor};`}
|
|
91
|
-
font-family: Inter, Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,
|
|
92
|
-
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
|
93
88
|
`;
|
|
94
89
|
|
|
95
90
|
export default withErrorBoundary(withHideWhenEmbed(Footer), {
|
package/src/Footer/links.jsx
CHANGED
|
@@ -167,7 +167,7 @@ Links.defaultProps = {
|
|
|
167
167
|
|
|
168
168
|
const Root = styled('div')`
|
|
169
169
|
overflow: hidden;
|
|
170
|
-
color:
|
|
170
|
+
color: ${({ theme }) => theme.palette.grey[500]};
|
|
171
171
|
.footer-links-inner {
|
|
172
172
|
display: flex;
|
|
173
173
|
justify-content: space-between;
|
|
@@ -198,8 +198,8 @@ const Root = styled('div')`
|
|
|
198
198
|
font-size: 14px;
|
|
199
199
|
&--new::after {
|
|
200
200
|
content: 'New';
|
|
201
|
-
color:
|
|
202
|
-
background-color:
|
|
201
|
+
color: ${({ theme }) => theme.palette.info.main};
|
|
202
|
+
background-color: ${({ theme }) => theme.palette.info.light};
|
|
203
203
|
padding: 1px 8px;
|
|
204
204
|
border-radius: 10px/50%;
|
|
205
205
|
margin-left: 8px;
|
|
@@ -209,7 +209,7 @@ const Root = styled('div')`
|
|
|
209
209
|
.footer-links-group {
|
|
210
210
|
> .footer-links-item {
|
|
211
211
|
font-weight: 600;
|
|
212
|
-
color:
|
|
212
|
+
color: ${({ theme }) => theme.palette.text.primary};
|
|
213
213
|
}
|
|
214
214
|
.footer-links-sub {
|
|
215
215
|
margin-top: 8px;
|
|
@@ -223,7 +223,7 @@ const Root = styled('div')`
|
|
|
223
223
|
text-decoration: none;
|
|
224
224
|
transition: color 0.2s ease-in-out;
|
|
225
225
|
&:hover {
|
|
226
|
-
color:
|
|
226
|
+
color: ${({ theme }) => theme.palette.text.primary};
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
/* columns 布局 */
|
|
@@ -52,11 +52,11 @@ const Root = styled('div')`
|
|
|
52
52
|
justify-content: center;
|
|
53
53
|
gap: 20px;
|
|
54
54
|
a {
|
|
55
|
-
color: ${(props) => props.theme.palette.grey[
|
|
55
|
+
color: ${(props) => props.theme.palette.grey[500]};
|
|
56
56
|
text-decoration: none;
|
|
57
57
|
transition: color 0.2s ease-in-out;
|
|
58
58
|
&:hover {
|
|
59
|
-
color:
|
|
59
|
+
color: ${({ theme }) => theme.palette.text.primary};
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
${(props) => props.theme.breakpoints.down('md')} {
|
package/src/Header/index.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { useMemo } from 'react';
|
|
|
2
2
|
import { useMemoizedFn } from 'ahooks';
|
|
3
3
|
import { withErrorBoundary } from 'react-error-boundary';
|
|
4
4
|
import { ErrorFallback } from '@arcblock/ux/lib/ErrorBoundary';
|
|
5
|
-
import { styled } from '@arcblock/ux/lib/Theme';
|
|
5
|
+
import { styled, useTheme, deepmerge, ThemeProvider } from '@arcblock/ux/lib/Theme';
|
|
6
6
|
import { ResponsiveHeader } from '@arcblock/ux/lib/Header';
|
|
7
7
|
import NavMenu, { Products } from '@arcblock/ux/lib/NavMenu';
|
|
8
8
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
@@ -14,7 +14,6 @@ import type { BoxProps, Breakpoint } from '@mui/material';
|
|
|
14
14
|
import clsx from 'clsx';
|
|
15
15
|
|
|
16
16
|
import Icon from '../Icon';
|
|
17
|
-
import OverridableThemeProvider from '../common/overridable-theme-provider';
|
|
18
17
|
import { mapRecursive, flatRecursive, matchPaths } from '../utils';
|
|
19
18
|
import { publicPath, formatBlockletInfo, getLocalizedNavigation } from '../blocklets';
|
|
20
19
|
import HeaderAddons from '../common/header-addons';
|
|
@@ -115,6 +114,7 @@ function Header({
|
|
|
115
114
|
...rest
|
|
116
115
|
}: HeaderProps & Omit<BoxProps, keyof HeaderProps>) {
|
|
117
116
|
useWalletHiddenTopbar();
|
|
117
|
+
const parentTheme = useTheme();
|
|
118
118
|
const { locale } = useLocaleContext() || {};
|
|
119
119
|
const t = useMemoizedFn((key, data = {}) => {
|
|
120
120
|
return translate(translations, key, locale, 'en', data);
|
|
@@ -129,11 +129,12 @@ function Header({
|
|
|
129
129
|
}
|
|
130
130
|
}, [meta]);
|
|
131
131
|
const isMobileDevice = useMobile();
|
|
132
|
+
const mergeTheme = useMemo(() => deepmerge(parentTheme, themeOverrides), [parentTheme, themeOverrides]);
|
|
132
133
|
|
|
133
134
|
if (!formattedBlocklet.appName) {
|
|
134
135
|
return null;
|
|
135
136
|
}
|
|
136
|
-
const { appLogo, appLogoRect
|
|
137
|
+
const { appLogo, appLogoRect } = formattedBlocklet;
|
|
137
138
|
const navigation = getLocalizedNavigation(formattedBlocklet?.navigation?.header, locale);
|
|
138
139
|
const parsedNavigation = parseNavigation(navigation);
|
|
139
140
|
const { navItems, activeId } = parsedNavigation;
|
|
@@ -164,7 +165,7 @@ function Header({
|
|
|
164
165
|
);
|
|
165
166
|
|
|
166
167
|
return (
|
|
167
|
-
<
|
|
168
|
+
<ThemeProvider theme={mergeTheme}>
|
|
168
169
|
<StyledUxHeader
|
|
169
170
|
// @ts-ignore
|
|
170
171
|
homeLink={homeLink}
|
|
@@ -172,7 +173,7 @@ function Header({
|
|
|
172
173
|
addons={headerAddons}
|
|
173
174
|
{...omit(rest, ['bordered'])}
|
|
174
175
|
$bordered={rest?.bordered}
|
|
175
|
-
$bgcolor={
|
|
176
|
+
$bgcolor={mergeTheme.palette.background.default}
|
|
176
177
|
className={clsx('blocklet__header', rest.className)}>
|
|
177
178
|
{/* blocklet.yml 没有配置 navigation 时, 则为 children 传入 null, 此时 ResponsiveHeader 会渲染普通的不带 menu 的 Header */}
|
|
178
179
|
{hideNavMenu || !navItems?.length
|
|
@@ -185,11 +186,12 @@ function Header({
|
|
|
185
186
|
items={navItems}
|
|
186
187
|
className="header-nav"
|
|
187
188
|
bgColor="transparent"
|
|
188
|
-
textColor=
|
|
189
|
+
textColor={mergeTheme.palette.grey[500]}
|
|
190
|
+
activeTextColor={mergeTheme.palette.text.primary}
|
|
189
191
|
/>
|
|
190
192
|
)}
|
|
191
193
|
</StyledUxHeader>
|
|
192
|
-
</
|
|
194
|
+
</ThemeProvider>
|
|
193
195
|
);
|
|
194
196
|
}
|
|
195
197
|
|
|
@@ -200,8 +202,6 @@ type StyledUxHeaderProps = {
|
|
|
200
202
|
|
|
201
203
|
const StyledUxHeader = styled(ResponsiveHeader)<StyledUxHeaderProps>`
|
|
202
204
|
${({ $bgcolor }) => `background-color: ${$bgcolor || '#fff'};`}
|
|
203
|
-
font-family: Inter, Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,
|
|
204
|
-
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
|
205
205
|
.header-logo {
|
|
206
206
|
min-width: 44px;
|
|
207
207
|
}
|
|
@@ -27,6 +27,7 @@ interface EditableFieldProps {
|
|
|
27
27
|
verified?: boolean;
|
|
28
28
|
errorMsg?: string;
|
|
29
29
|
canEdit?: boolean;
|
|
30
|
+
hidePreview?: boolean;
|
|
30
31
|
renderValue?: (value: string) => React.ReactNode;
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -78,6 +79,7 @@ function EditableField({
|
|
|
78
79
|
canEdit = true,
|
|
79
80
|
renderValue,
|
|
80
81
|
disabled = false,
|
|
82
|
+
hidePreview = false,
|
|
81
83
|
}: EditableFieldProps) {
|
|
82
84
|
const { locale } = useLocaleContext();
|
|
83
85
|
const t = useMemoizedFn((key, data = {}) => {
|
|
@@ -174,7 +176,7 @@ function EditableField({
|
|
|
174
176
|
}
|
|
175
177
|
|
|
176
178
|
if (!editable) {
|
|
177
|
-
return value ? (
|
|
179
|
+
return value && !hidePreview ? (
|
|
178
180
|
<Tooltip
|
|
179
181
|
open={Boolean(mousePosition)}
|
|
180
182
|
title={tooltip}
|
|
@@ -25,6 +25,7 @@ import isEmail from 'validator/lib/isEmail';
|
|
|
25
25
|
import isPostalCode from 'validator/lib/isPostalCode';
|
|
26
26
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
27
27
|
import { useBrowser } from '@arcblock/react-hooks';
|
|
28
|
+
import Clock from '@arcblock/ux/lib/UserCard/Content/clock';
|
|
28
29
|
|
|
29
30
|
import { translations } from '../../libs/locales';
|
|
30
31
|
import type { User, UserAddress, UserMetadata, UserPhoneProps } from '../../../@types';
|
|
@@ -32,7 +33,6 @@ import EditableField, { commonInputStyle, inputFieldStyle } from '../editable-fi
|
|
|
32
33
|
import { LinkPreviewInput } from './link-preview-input';
|
|
33
34
|
import { currentTimezone, defaultButtonStyle, primaryButtonStyle } from './utils';
|
|
34
35
|
import { TimezoneSelect } from './timezone-select';
|
|
35
|
-
import Clock from './clock';
|
|
36
36
|
import AddressEditor from './address';
|
|
37
37
|
|
|
38
38
|
const LocationIcon = lazy(() => import('@arcblock/icons/lib/Location'));
|
|
@@ -429,6 +429,7 @@ export default function UserMetadataComponent({
|
|
|
429
429
|
<EditableField
|
|
430
430
|
value={metadata.email ?? user?.email ?? ''}
|
|
431
431
|
editable={editing}
|
|
432
|
+
hidePreview={!isMyself}
|
|
432
433
|
disabled={user?.sourceProvider === LOGIN_PROVIDER.EMAIL}
|
|
433
434
|
canEdit={!emailVerified}
|
|
434
435
|
verified={emailVerified}
|
|
@@ -453,16 +454,18 @@ export default function UserMetadataComponent({
|
|
|
453
454
|
}
|
|
454
455
|
onChange={(value) => onChange(value, 'email')}
|
|
455
456
|
errorMsg={validateMsg.email}
|
|
456
|
-
renderValue={(value) =>
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
457
|
+
renderValue={(value) =>
|
|
458
|
+
isMyself ? (
|
|
459
|
+
<a
|
|
460
|
+
href={`mailto:${value}`}
|
|
461
|
+
style={{
|
|
462
|
+
color: 'inherit',
|
|
463
|
+
textDecoration: 'none',
|
|
464
|
+
}}>
|
|
465
|
+
{value}
|
|
466
|
+
</a>
|
|
467
|
+
) : null
|
|
468
|
+
}
|
|
466
469
|
onValueValidate={(value) => {
|
|
467
470
|
let msg = '';
|
|
468
471
|
if (!!value && !isEmail(value)) {
|
|
@@ -475,6 +478,7 @@ export default function UserMetadataComponent({
|
|
|
475
478
|
<EditableField
|
|
476
479
|
value={phoneValue.phone}
|
|
477
480
|
editable={editing}
|
|
481
|
+
hidePreview={!isMyself}
|
|
478
482
|
canEdit={!phoneVerified}
|
|
479
483
|
verified={phoneVerified}
|
|
480
484
|
placeholder="Phone"
|
|
@@ -482,7 +486,7 @@ export default function UserMetadataComponent({
|
|
|
482
486
|
onChange={(value) => onChange(value, 'phone')}
|
|
483
487
|
label={t('profile.phone')}
|
|
484
488
|
renderValue={() => {
|
|
485
|
-
return <PhoneInput value={phoneValue} preview
|
|
489
|
+
return isMyself ? <PhoneInput value={phoneValue} preview /> : null;
|
|
486
490
|
}}>
|
|
487
491
|
<PhoneInput
|
|
488
492
|
variant="outlined"
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import Badge from '@mui/material/Badge';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { useCallback, useEffect } from 'react';
|
|
4
|
-
import { temp as colors } from '@arcblock/ux/lib/Colors';
|
|
5
4
|
import { IconButton } from '@mui/material';
|
|
6
5
|
import { useSnackbar } from 'notistack';
|
|
7
6
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
@@ -105,7 +104,6 @@ export default function NotificationAddon({ session = {} }) {
|
|
|
105
104
|
variant="outlined"
|
|
106
105
|
href={viewAllUrl}
|
|
107
106
|
sx={{
|
|
108
|
-
borderColor: colors.lineBorderStrong,
|
|
109
107
|
'&:hover': {
|
|
110
108
|
borderRadius: '50%',
|
|
111
109
|
},
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import Tooltip from "@mui/material/Tooltip";
|
|
3
|
-
import Typography from "@mui/material/Typography";
|
|
4
|
-
import Box from "@mui/material/Box";
|
|
5
|
-
import { translate } from "@arcblock/ux/lib/Locale/util";
|
|
6
|
-
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
7
|
-
import { useMemoizedFn } from "ahooks";
|
|
8
|
-
import { translations } from "../../libs/locales.js";
|
|
9
|
-
import { useClock } from "../../../hooks/use-clock.js";
|
|
10
|
-
export default function Clock({ value }) {
|
|
11
|
-
const { locale } = useLocaleContext();
|
|
12
|
-
const t = useMemoizedFn((key, data = {}) => {
|
|
13
|
-
return translate(translations, key, locale, "en", data);
|
|
14
|
-
});
|
|
15
|
-
const timeInfo = useClock(value, locale);
|
|
16
|
-
return /* @__PURE__ */ jsxs(
|
|
17
|
-
Box,
|
|
18
|
-
{
|
|
19
|
-
sx: {
|
|
20
|
-
whiteSpace: "nowrap",
|
|
21
|
-
overflow: "hidden",
|
|
22
|
-
textOverflow: "ellipsis"
|
|
23
|
-
},
|
|
24
|
-
display: "flex",
|
|
25
|
-
alignItems: "center",
|
|
26
|
-
gap: 1,
|
|
27
|
-
children: [
|
|
28
|
-
/* @__PURE__ */ jsx(Typography, { children: value }),
|
|
29
|
-
/* @__PURE__ */ jsx(
|
|
30
|
-
Tooltip,
|
|
31
|
-
{
|
|
32
|
-
title: /* @__PURE__ */ jsxs("span", { children: [
|
|
33
|
-
t("profile.localTime"),
|
|
34
|
-
" ",
|
|
35
|
-
timeInfo.fullDateTime
|
|
36
|
-
] }),
|
|
37
|
-
placement: "top",
|
|
38
|
-
arrow: true,
|
|
39
|
-
children: /* @__PURE__ */ jsxs(Typography, { component: "span", fontSize: 14, children: [
|
|
40
|
-
"(",
|
|
41
|
-
locale === "zh" ? `${t(`profile.timezonePhase.${timeInfo.phase}`)} ` : "",
|
|
42
|
-
timeInfo.formattedTime,
|
|
43
|
-
")"
|
|
44
|
-
] })
|
|
45
|
-
}
|
|
46
|
-
)
|
|
47
|
-
]
|
|
48
|
-
}
|
|
49
|
-
);
|
|
50
|
-
}
|
package/lib/hooks/use-clock.d.ts
DELETED
package/lib/hooks/use-clock.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useCallback } from "react";
|
|
2
|
-
import dayjs from "dayjs";
|
|
3
|
-
import utc from "dayjs/plugin/utc";
|
|
4
|
-
import timezonePlugin from "dayjs/plugin/timezone";
|
|
5
|
-
import localizedFormat from "dayjs/plugin/localizedFormat";
|
|
6
|
-
import { formatToDatetime } from "@arcblock/ux/lib/Util";
|
|
7
|
-
import "dayjs/locale/zh-cn";
|
|
8
|
-
import "dayjs/locale/en";
|
|
9
|
-
dayjs.extend(utc);
|
|
10
|
-
dayjs.extend(timezonePlugin);
|
|
11
|
-
dayjs.extend(localizedFormat);
|
|
12
|
-
const currentTimezone = dayjs.tz.guess();
|
|
13
|
-
const getTimePhase = (hour) => {
|
|
14
|
-
if (hour >= 0 && hour < 6)
|
|
15
|
-
return { phase: "dawn", icon: "\u{1F312}" };
|
|
16
|
-
if (hour >= 6 && hour < 12)
|
|
17
|
-
return { phase: "morning", icon: "\u{1F31E}" };
|
|
18
|
-
if (hour >= 12 && hour < 18)
|
|
19
|
-
return { phase: "afternoon", icon: "\u{1F31E}" };
|
|
20
|
-
return { phase: "night", icon: "\u{1F312}" };
|
|
21
|
-
};
|
|
22
|
-
export function useClock(timezone = currentTimezone, locale = "zh") {
|
|
23
|
-
const getLatestTimeInfo = useCallback(() => {
|
|
24
|
-
const currentLocale = locale === "zh" ? "zh-cn" : "en";
|
|
25
|
-
const localeOption = locale === "zh" ? "zh-cn" : "en-us";
|
|
26
|
-
dayjs.locale(currentLocale);
|
|
27
|
-
const now = dayjs().tz(timezone);
|
|
28
|
-
const hour = now.hour();
|
|
29
|
-
const { phase, icon } = getTimePhase(hour);
|
|
30
|
-
return {
|
|
31
|
-
formattedTime: now.format("LT"),
|
|
32
|
-
fullDateTime: formatToDatetime(now.toDate(), { tz: timezone, locale: localeOption }),
|
|
33
|
-
phase,
|
|
34
|
-
icon,
|
|
35
|
-
rawTime: now
|
|
36
|
-
};
|
|
37
|
-
}, [timezone, locale]);
|
|
38
|
-
const [timeInfo, setTimeInfo] = useState(getLatestTimeInfo());
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
setTimeInfo(getLatestTimeInfo());
|
|
41
|
-
const timerId = setInterval(() => {
|
|
42
|
-
setTimeInfo(getLatestTimeInfo());
|
|
43
|
-
}, 6e3);
|
|
44
|
-
return () => {
|
|
45
|
-
clearInterval(timerId);
|
|
46
|
-
};
|
|
47
|
-
}, [timezone, locale, getLatestTimeInfo]);
|
|
48
|
-
return timeInfo;
|
|
49
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import Tooltip from '@mui/material/Tooltip';
|
|
2
|
-
import Typography from '@mui/material/Typography';
|
|
3
|
-
import Box from '@mui/material/Box';
|
|
4
|
-
import { translate } from '@arcblock/ux/lib/Locale/util';
|
|
5
|
-
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
6
|
-
import { useMemoizedFn } from 'ahooks';
|
|
7
|
-
import { translations } from '../../libs/locales';
|
|
8
|
-
import { useClock } from '../../../hooks/use-clock';
|
|
9
|
-
|
|
10
|
-
export default function Clock({ value }: { value: string }) {
|
|
11
|
-
const { locale } = useLocaleContext();
|
|
12
|
-
const t = useMemoizedFn((key, data = {}) => {
|
|
13
|
-
return translate(translations, key, locale, 'en', data);
|
|
14
|
-
});
|
|
15
|
-
const timeInfo = useClock(value, locale);
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<Box
|
|
19
|
-
sx={{
|
|
20
|
-
whiteSpace: 'nowrap',
|
|
21
|
-
overflow: 'hidden',
|
|
22
|
-
textOverflow: 'ellipsis',
|
|
23
|
-
}}
|
|
24
|
-
display="flex"
|
|
25
|
-
alignItems="center"
|
|
26
|
-
gap={1}>
|
|
27
|
-
<Typography>{value}</Typography>
|
|
28
|
-
<Tooltip
|
|
29
|
-
title={
|
|
30
|
-
<span>
|
|
31
|
-
{t('profile.localTime')} {timeInfo.fullDateTime}
|
|
32
|
-
</span>
|
|
33
|
-
}
|
|
34
|
-
placement="top"
|
|
35
|
-
arrow>
|
|
36
|
-
<Typography component="span" fontSize={14}>
|
|
37
|
-
({locale === 'zh' ? `${t(`profile.timezonePhase.${timeInfo.phase}`)} ` : ''}
|
|
38
|
-
{timeInfo.formattedTime})
|
|
39
|
-
</Typography>
|
|
40
|
-
</Tooltip>
|
|
41
|
-
</Box>
|
|
42
|
-
);
|
|
43
|
-
}
|
package/src/hooks/use-clock.tsx
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
-
import dayjs from 'dayjs';
|
|
3
|
-
import utc from 'dayjs/plugin/utc';
|
|
4
|
-
import timezonePlugin from 'dayjs/plugin/timezone';
|
|
5
|
-
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
|
6
|
-
import { formatToDatetime } from '@arcblock/ux/lib/Util';
|
|
7
|
-
import 'dayjs/locale/zh-cn';
|
|
8
|
-
import 'dayjs/locale/en';
|
|
9
|
-
|
|
10
|
-
dayjs.extend(utc);
|
|
11
|
-
dayjs.extend(timezonePlugin);
|
|
12
|
-
dayjs.extend(localizedFormat);
|
|
13
|
-
|
|
14
|
-
const currentTimezone = dayjs.tz.guess();
|
|
15
|
-
|
|
16
|
-
// 获取时间段
|
|
17
|
-
const getTimePhase = (hour: number) => {
|
|
18
|
-
if (hour >= 0 && hour < 6) return { phase: 'dawn', icon: '🌒' }; // 凌晨 00:00-05:59
|
|
19
|
-
if (hour >= 6 && hour < 12) return { phase: 'morning', icon: '🌞' }; // 上午 06:00-11:59
|
|
20
|
-
if (hour >= 12 && hour < 18) return { phase: 'afternoon', icon: '🌞' }; // 下午 12:00-17:59
|
|
21
|
-
return { phase: 'night', icon: '🌒' }; // 晚上 18:00-23:59
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export function useClock(timezone = currentTimezone, locale = 'zh') {
|
|
25
|
-
const getLatestTimeInfo = useCallback(() => {
|
|
26
|
-
const currentLocale = locale === 'zh' ? 'zh-cn' : 'en';
|
|
27
|
-
const localeOption = locale === 'zh' ? 'zh-cn' : 'en-us';
|
|
28
|
-
dayjs.locale(currentLocale);
|
|
29
|
-
|
|
30
|
-
const now = dayjs().tz(timezone);
|
|
31
|
-
const hour = now.hour();
|
|
32
|
-
const { phase, icon } = getTimePhase(hour);
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
formattedTime: now.format('LT'),
|
|
36
|
-
fullDateTime: formatToDatetime(now.toDate(), { tz: timezone, locale: localeOption }),
|
|
37
|
-
phase,
|
|
38
|
-
icon,
|
|
39
|
-
rawTime: now,
|
|
40
|
-
};
|
|
41
|
-
}, [timezone, locale]);
|
|
42
|
-
|
|
43
|
-
const [timeInfo, setTimeInfo] = useState(getLatestTimeInfo());
|
|
44
|
-
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
// 立即更新一次,确保初始状态是最新的
|
|
47
|
-
setTimeInfo(getLatestTimeInfo());
|
|
48
|
-
|
|
49
|
-
// 设置定时器
|
|
50
|
-
const timerId = setInterval(() => {
|
|
51
|
-
setTimeInfo(getLatestTimeInfo());
|
|
52
|
-
}, 6000);
|
|
53
|
-
|
|
54
|
-
// 清理函数
|
|
55
|
-
return () => {
|
|
56
|
-
clearInterval(timerId);
|
|
57
|
-
};
|
|
58
|
-
}, [timezone, locale, getLatestTimeInfo]);
|
|
59
|
-
|
|
60
|
-
return timeInfo;
|
|
61
|
-
}
|