@blocklet/ui-react 2.12.16 → 2.12.18

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.
@@ -498,7 +498,16 @@ export default function UserCenter({
498
498
  }
499
499
  return currentActiveTab?.isPrivate;
500
500
  }, [isMyself, currentActiveTab]);
501
- if (!disableAutoRedirect && !currentTab && userCenterTabs?.length > 0 || !currentActiveTab || isPrivateActive) {
501
+ const shouldRedirect = useCreation(() => {
502
+ if (onlyProfile) {
503
+ return false;
504
+ }
505
+ const noActiveTabButHasTabs = !disableAutoRedirect && !currentTab && userCenterTabs?.length > 0;
506
+ const invalidCurrentTab = !currentActiveTab;
507
+ const accessingPrivateTab = isPrivateActive;
508
+ return noActiveTabButHasTabs || invalidCurrentTab || accessingPrivateTab;
509
+ }, [disableAutoRedirect, currentTab, userCenterTabs, currentActiveTab, isPrivateActive, onlyProfile]);
510
+ if (shouldRedirect) {
502
511
  const firstUserCenterUrl = userCenterTabs[0]?.url;
503
512
  const firstTab = userCenterTabs.find((x) => x.value === firstUserCenterUrl);
504
513
  if (firstUserCenterUrl && !firstTab?.isPrivate) {
@@ -510,7 +519,7 @@ export default function UserCenter({
510
519
  }
511
520
  return null;
512
521
  }
513
- if (embed) {
522
+ if (embed || onlyProfile) {
514
523
  return /* @__PURE__ */ jsxs(Main, { children: [
515
524
  content,
516
525
  confirmHolder
@@ -1,4 +1,3 @@
1
- export default function Clock({ timezone, locale }: {
2
- timezone?: string;
3
- locale?: string;
1
+ export default function Clock({ value }: {
2
+ value: string;
4
3
  }): import("react").JSX.Element;
@@ -1,24 +1,48 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { useState, useEffect } from "react";
3
- import dayjs from "dayjs";
4
- import utc from "dayjs/plugin/utc";
5
- import timezonePlugin from "dayjs/plugin/timezone";
6
- import { formatToDatetime } from "@arcblock/ux/lib/Util";
7
- import { useCreation } from "ahooks";
8
- import { currentTimezone } from "./utils.js";
9
- dayjs.extend(utc);
10
- dayjs.extend(timezonePlugin);
11
- export default function Clock({ timezone = currentTimezone, locale = "zh" }) {
12
- const [time, setTime] = useState(dayjs().tz(timezone || currentTimezone));
13
- useEffect(() => {
14
- const timerId = setInterval(() => {
15
- setTime(dayjs().tz(timezone));
16
- }, 6e3);
17
- return () => clearInterval(timerId);
18
- }, [timezone]);
19
- const formatTime = useCreation(() => {
20
- const localeOption = locale === "zh" ? "zh-cn" : "en-us";
21
- return formatToDatetime(time.toDate(), { tz: timezone, locale: localeOption });
22
- }, [time, timezone, locale]);
23
- return /* @__PURE__ */ jsx("div", { children: formatTime });
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
+ children: /* @__PURE__ */ jsxs(Typography, { component: "span", fontSize: 14, children: [
38
+ "(",
39
+ locale === "zh" ? `${t(`profile.timezonePhase.${timeInfo.phase}`)} ` : "",
40
+ timeInfo.formattedTime,
41
+ ")"
42
+ ] })
43
+ }
44
+ )
45
+ ]
46
+ }
47
+ );
24
48
  }
@@ -20,8 +20,8 @@ import { translations } from "../../libs/locales.js";
20
20
  import EditableField from "../editable-field.js";
21
21
  import { LinkPreviewInput } from "./link-preview-input.js";
22
22
  import { currentTimezone, defaultButtonStyle, primaryButtonStyle } from "./utils.js";
23
- import Clock from "./clock.js";
24
23
  import { TimezoneSelect } from "./timezone-select.js";
24
+ import Clock from "./clock.js";
25
25
  const LocationIcon = lazy(() => import("@arcblock/icons/lib/Location"));
26
26
  const TimezoneIcon = lazy(() => import("@arcblock/icons/lib/Timezone"));
27
27
  const EmailIcon = lazy(() => import("@arcblock/icons/lib/Email"));
@@ -245,10 +245,9 @@ export default function UserMetadataComponent({
245
245
  placeholder: "timezone",
246
246
  icon: /* @__PURE__ */ jsx(TimezoneIcon, { ...iconSize }),
247
247
  label: t("profile.timezone"),
248
- tooltip: /* @__PURE__ */ jsxs("p", { style: { display: "flex", margin: 0 }, children: [
249
- /* @__PURE__ */ jsx("span", { style: { marginRight: "4px" }, children: t("profile.localTime") }),
250
- /* @__PURE__ */ jsx(Clock, { timezone: metadata.timezone, locale })
251
- ] }),
248
+ renderValue: (value) => {
249
+ return /* @__PURE__ */ jsx(Clock, { value });
250
+ },
252
251
  children: /* @__PURE__ */ jsx(
253
252
  TimezoneSelect,
254
253
  {
@@ -131,6 +131,12 @@ export declare const translations: {
131
131
  quickSettings: string;
132
132
  selectEndTime: string;
133
133
  pleaseSelectTime: string;
134
+ timezonePhase: {
135
+ dawn: string;
136
+ morning: string;
137
+ afternoon: string;
138
+ night: string;
139
+ };
134
140
  };
135
141
  };
136
142
  en: {
@@ -266,6 +272,12 @@ export declare const translations: {
266
272
  pleaseSelectTime: string;
267
273
  cleanStatus: string;
268
274
  quickSettings: string;
275
+ timezonePhase: {
276
+ dawn: string;
277
+ morning: string;
278
+ afternoon: string;
279
+ night: string;
280
+ };
269
281
  };
270
282
  };
271
283
  };
@@ -130,7 +130,13 @@ export const translations = {
130
130
  cleanStatus: "\u6E05\u9664\u72B6\u6001",
131
131
  quickSettings: "\u5FEB\u6377\u8BBE\u7F6E",
132
132
  selectEndTime: "\u9009\u62E9\u7ED3\u675F\u65F6\u95F4",
133
- pleaseSelectTime: "\u8BF7\u9009\u62E9\u65F6\u95F4"
133
+ pleaseSelectTime: "\u8BF7\u9009\u62E9\u65F6\u95F4",
134
+ timezonePhase: {
135
+ dawn: "\u51CC\u6668",
136
+ morning: "\u4E0A\u5348",
137
+ afternoon: "\u4E0B\u5348",
138
+ night: "\u665A\u4E0A"
139
+ }
134
140
  }
135
141
  },
136
142
  en: {
@@ -265,7 +271,13 @@ export const translations = {
265
271
  selectEndTime: "Select end time",
266
272
  pleaseSelectTime: "Please select time",
267
273
  cleanStatus: "Clean status",
268
- quickSettings: "Quick Settings"
274
+ quickSettings: "Quick Settings",
275
+ timezonePhase: {
276
+ dawn: "AM",
277
+ morning: "AM",
278
+ afternoon: "PM",
279
+ night: "PM"
280
+ }
269
281
  }
270
282
  }
271
283
  };
@@ -0,0 +1,9 @@
1
+ import 'dayjs/locale/zh-cn';
2
+ import 'dayjs/locale/en';
3
+ export declare function useClock(timezone?: any, locale?: string): {
4
+ formattedTime: any;
5
+ fullDateTime: any;
6
+ phase: string;
7
+ icon: string;
8
+ rawTime: any;
9
+ };
@@ -0,0 +1,49 @@
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/ui-react",
3
- "version": "2.12.16",
3
+ "version": "2.12.18",
4
4
  "description": "Some useful front-end web components that can be used in Blocklets.",
5
5
  "keywords": [
6
6
  "react",
@@ -33,10 +33,10 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@abtnode/constant": "^1.16.39",
36
- "@arcblock/bridge": "^2.12.16",
37
- "@arcblock/react-hooks": "^2.12.16",
36
+ "@arcblock/bridge": "^2.12.18",
37
+ "@arcblock/react-hooks": "^2.12.18",
38
38
  "@arcblock/ws": "^1.19.15",
39
- "@blocklet/did-space-react": "^1.0.30",
39
+ "@blocklet/did-space-react": "^1.0.31",
40
40
  "@iconify-icons/logos": "^1.2.36",
41
41
  "@iconify-icons/material-symbols": "^1.2.58",
42
42
  "@iconify/react": "^4.1.1",
@@ -87,5 +87,5 @@
87
87
  "jest": "^29.7.0",
88
88
  "unbuild": "^2.0.0"
89
89
  },
90
- "gitHead": "02b619f243bf6a0c6847349edf513e18b212bcd7"
90
+ "gitHead": "e5629cf0ff51b952d35fe85d05065740f740048b"
91
91
  }
@@ -566,10 +566,29 @@ export default function UserCenter({
566
566
  return currentActiveTab?.isPrivate;
567
567
  }, [isMyself, currentActiveTab]);
568
568
 
569
- // TODO 过滤 !mySelf 是的URL跳转
570
- if ((!disableAutoRedirect && !currentTab && userCenterTabs?.length > 0) || !currentActiveTab || isPrivateActive) {
569
+ // 判断是否需要重定向到第一个可用的标签页
570
+ const shouldRedirect = useCreation(() => {
571
+ // 非 Profile 模式下才考虑重定向
572
+ if (onlyProfile) {
573
+ return false;
574
+ }
575
+
576
+ // 以下任一条件满足时需要重定向:
577
+ // 1. 自动重定向开启且当前无激活标签但有可用标签
578
+ const noActiveTabButHasTabs = !disableAutoRedirect && !currentTab && userCenterTabs?.length > 0;
579
+ // 2. 当前标签页不存在
580
+ const invalidCurrentTab = !currentActiveTab;
581
+ // 3. 当前是私有标签页但用户在查看他人信息
582
+ const accessingPrivateTab = isPrivateActive;
583
+
584
+ return noActiveTabButHasTabs || invalidCurrentTab || accessingPrivateTab;
585
+ }, [disableAutoRedirect, currentTab, userCenterTabs, currentActiveTab, isPrivateActive, onlyProfile]);
586
+
587
+ if (shouldRedirect) {
588
+ // 获取第一个可用的标签页URL
571
589
  const firstUserCenterUrl = userCenterTabs[0]?.url;
572
590
  const firstTab = userCenterTabs.find((x) => x.value === firstUserCenterUrl);
591
+ // 如果存在非私有的第一个标签页,则重定向
573
592
  if (firstUserCenterUrl && !firstTab?.isPrivate) {
574
593
  window.location.replace(
575
594
  withQuery(firstUserCenterUrl, {
@@ -581,7 +600,7 @@ export default function UserCenter({
581
600
  }
582
601
 
583
602
  // 嵌入其它页面内时,只展示 content
584
- if (embed) {
603
+ if (embed || onlyProfile) {
585
604
  return (
586
605
  <Main>
587
606
  {content}
@@ -1,30 +1,41 @@
1
- import { useState, useEffect } from 'react';
2
- import dayjs from 'dayjs';
3
- import utc from 'dayjs/plugin/utc';
4
- import timezonePlugin from 'dayjs/plugin/timezone';
5
- import { formatToDatetime } from '@arcblock/ux/lib/Util';
6
- import { useCreation } from 'ahooks';
7
- import { currentTimezone } from './utils';
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';
8
9
 
9
- dayjs.extend(utc);
10
- dayjs.extend(timezonePlugin);
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);
11
16
 
12
- export default function Clock({ timezone = currentTimezone, locale = 'zh' }: { timezone?: string; locale?: string }) {
13
- const [time, setTime] = useState(dayjs().tz(timezone || currentTimezone));
14
-
15
- useEffect(() => {
16
- const timerId = setInterval(() => {
17
- setTime(dayjs().tz(timezone));
18
- }, 6000); // 每分钟更新一次
19
-
20
- // 清除定时器
21
- return () => clearInterval(timerId);
22
- }, [timezone]);
23
-
24
- const formatTime = useCreation(() => {
25
- const localeOption = locale === 'zh' ? 'zh-cn' : 'en-us';
26
- return formatToDatetime(time.toDate(), { tz: timezone, locale: localeOption });
27
- }, [time, timezone, locale]);
28
-
29
- return <div>{formatTime}</div>;
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
+ <Typography component="span" fontSize={14}>
35
+ ({locale === 'zh' ? `${t(`profile.timezonePhase.${timeInfo.phase}`)} ` : ''}
36
+ {timeInfo.formattedTime})
37
+ </Typography>
38
+ </Tooltip>
39
+ </Box>
40
+ );
30
41
  }
@@ -18,15 +18,14 @@ import { translate } from '@arcblock/ux/lib/Locale/util';
18
18
  import isEmail from 'validator/lib/isEmail';
19
19
  import { temp as colors } from '@arcblock/ux/lib/Colors';
20
20
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
21
-
22
21
  import { useBrowser } from '@arcblock/react-hooks';
23
22
  import { translations } from '../../libs/locales';
24
23
  import type { User, UserMetadata, UserPhoneProps } from '../../../@types';
25
24
  import EditableField from '../editable-field';
26
25
  import { LinkPreviewInput } from './link-preview-input';
27
26
  import { currentTimezone, defaultButtonStyle, primaryButtonStyle } from './utils';
28
- import Clock from './clock';
29
27
  import { TimezoneSelect } from './timezone-select';
28
+ import Clock from './clock';
30
29
 
31
30
  const LocationIcon = lazy(() => import('@arcblock/icons/lib/Location'));
32
31
  const TimezoneIcon = lazy(() => import('@arcblock/icons/lib/Timezone'));
@@ -270,12 +269,9 @@ export default function UserMetadataComponent({
270
269
  placeholder="timezone"
271
270
  icon={<TimezoneIcon {...iconSize} />}
272
271
  label={t('profile.timezone')}
273
- tooltip={
274
- <p style={{ display: 'flex', margin: 0 }}>
275
- <span style={{ marginRight: '4px' }}>{t('profile.localTime')}</span>
276
- <Clock timezone={metadata.timezone} locale={locale} />
277
- </p>
278
- }>
272
+ renderValue={(value) => {
273
+ return <Clock value={value} />;
274
+ }}>
279
275
  <TimezoneSelect
280
276
  value={metadata.timezone || currentTimezone}
281
277
  onChange={(value) => onChange(value, 'timezone')}
@@ -133,6 +133,12 @@ export const translations = {
133
133
  quickSettings: '快捷设置',
134
134
  selectEndTime: '选择结束时间',
135
135
  pleaseSelectTime: '请选择时间',
136
+ timezonePhase: {
137
+ dawn: '凌晨',
138
+ morning: '上午',
139
+ afternoon: '下午',
140
+ night: '晚上',
141
+ },
136
142
  },
137
143
  },
138
144
  en: {
@@ -270,6 +276,12 @@ export const translations = {
270
276
  pleaseSelectTime: 'Please select time',
271
277
  cleanStatus: 'Clean status',
272
278
  quickSettings: 'Quick Settings',
279
+ timezonePhase: {
280
+ dawn: 'AM',
281
+ morning: 'AM',
282
+ afternoon: 'PM',
283
+ night: 'PM',
284
+ },
273
285
  },
274
286
  },
275
287
  };
@@ -0,0 +1,61 @@
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
+ }