@blocklet/ui-react 3.4.14 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/common/org-switch/use-org.d.ts +4 -4
- package/lib/common/ws.d.ts +22 -1
- package/package.json +10 -7
- package/.aigne/doc-smith/.local/afs-storage.sqlite3 +0 -0
- package/.aigne/doc-smith/config.yaml +0 -78
- package/.aigne/doc-smith/history.yaml +0 -14
- package/.aigne/doc-smith/media-description.yaml +0 -11
- package/.aigne/doc-smith/output/structure-plan.json +0 -255
- package/.aigne/doc-smith/translation-cache.yaml +0 -11
- package/.aigne/doc-smith/upload-cache.yaml +0 -528
- package/build.config.ts +0 -24
- package/docs/_sidebar.md +0 -19
- package/docs/assets/diagram/component-installer-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/component-installer-diagram-0.jpg +0 -0
- package/docs/assets/diagram/component-installer-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/component-installer-diagram-0.zh.jpg +0 -0
- package/docs/assets/diagram/component-management-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/component-management-diagram-0.jpg +0 -0
- package/docs/assets/diagram/component-management-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/component-management-diagram-0.zh.jpg +0 -0
- package/docs/assets/diagram/core-concepts-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/core-concepts-diagram-0.jpg +0 -0
- package/docs/assets/diagram/core-concepts-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/core-concepts-diagram-0.zh.jpg +0 -0
- package/docs/assets/diagram/dashboard-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/dashboard-diagram-0.jpg +0 -0
- package/docs/assets/diagram/dashboard-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/dashboard-diagram-0.zh.jpg +0 -0
- package/docs/assets/diagram/header-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/header-diagram-0.jpg +0 -0
- package/docs/assets/diagram/header-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/header-diagram-0.zh.jpg +0 -0
- package/docs/assets/diagram/layout-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/layout-diagram-0.jpg +0 -0
- package/docs/assets/diagram/layout-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/layout-diagram-0.zh.jpg +0 -0
- package/docs/assets/diagram/notifications-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/notifications-diagram-0.jpg +0 -0
- package/docs/assets/diagram/notifications-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/notifications-diagram-0.zh.jpg +0 -0
- package/docs/assets/diagram/overview-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/overview-diagram-0.jpg +0 -0
- package/docs/assets/diagram/overview-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/overview-diagram-0.zh.jpg +0 -0
- package/docs/assets/diagram/user-center-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/user-center-diagram-0.jpg +0 -0
- package/docs/assets/diagram/user-center-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/user-center-diagram-0.zh.jpg +0 -0
- package/docs/assets/diagram/user-management-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/user-management-diagram-0.jpg +0 -0
- package/docs/assets/diagram/user-management-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/user-management-diagram-0.zh.jpg +0 -0
- package/docs/assets/diagram/user-sessions-diagram-0.ja.jpg +0 -0
- package/docs/assets/diagram/user-sessions-diagram-0.jpg +0 -0
- package/docs/assets/diagram/user-sessions-diagram-0.zh-TW.jpg +0 -0
- package/docs/assets/diagram/user-sessions-diagram-0.zh.jpg +0 -0
- package/docs/components-component-management-blocklet-studio.ja.md +0 -194
- package/docs/components-component-management-blocklet-studio.md +0 -194
- package/docs/components-component-management-blocklet-studio.zh-TW.md +0 -194
- package/docs/components-component-management-blocklet-studio.zh.md +0 -194
- package/docs/components-component-management-component-installer.ja.md +0 -182
- package/docs/components-component-management-component-installer.md +0 -182
- package/docs/components-component-management-component-installer.zh-TW.md +0 -182
- package/docs/components-component-management-component-installer.zh.md +0 -182
- package/docs/components-component-management.ja.md +0 -30
- package/docs/components-component-management.md +0 -30
- package/docs/components-component-management.zh-TW.md +0 -30
- package/docs/components-component-management.zh.md +0 -30
- package/docs/components-layout-dashboard.ja.md +0 -185
- package/docs/components-layout-dashboard.md +0 -187
- package/docs/components-layout-dashboard.zh-TW.md +0 -185
- package/docs/components-layout-dashboard.zh.md +0 -185
- package/docs/components-layout-footer.ja.md +0 -165
- package/docs/components-layout-footer.md +0 -165
- package/docs/components-layout-footer.zh-TW.md +0 -165
- package/docs/components-layout-footer.zh.md +0 -165
- package/docs/components-layout-header.ja.md +0 -183
- package/docs/components-layout-header.md +0 -183
- package/docs/components-layout-header.zh-TW.md +0 -183
- package/docs/components-layout-header.zh.md +0 -183
- package/docs/components-layout.ja.md +0 -31
- package/docs/components-layout.md +0 -31
- package/docs/components-layout.zh-TW.md +0 -31
- package/docs/components-layout.zh.md +0 -31
- package/docs/components-notifications.ja.md +0 -125
- package/docs/components-notifications.md +0 -125
- package/docs/components-notifications.zh-TW.md +0 -125
- package/docs/components-notifications.zh.md +0 -125
- package/docs/components-user-management-user-center.ja.md +0 -148
- package/docs/components-user-management-user-center.md +0 -147
- package/docs/components-user-management-user-center.zh-TW.md +0 -148
- package/docs/components-user-management-user-center.zh.md +0 -148
- package/docs/components-user-management-user-sessions.ja.md +0 -121
- package/docs/components-user-management-user-sessions.md +0 -123
- package/docs/components-user-management-user-sessions.zh-TW.md +0 -121
- package/docs/components-user-management-user-sessions.zh.md +0 -121
- package/docs/components-user-management.ja.md +0 -49
- package/docs/components-user-management.md +0 -51
- package/docs/components-user-management.zh-TW.md +0 -49
- package/docs/components-user-management.zh.md +0 -49
- package/docs/components-utilities-icon.ja.md +0 -106
- package/docs/components-utilities-icon.md +0 -106
- package/docs/components-utilities-icon.zh-TW.md +0 -106
- package/docs/components-utilities-icon.zh.md +0 -106
- package/docs/components-utilities.ja.md +0 -136
- package/docs/components-utilities.md +0 -136
- package/docs/components-utilities.zh-TW.md +0 -136
- package/docs/components-utilities.zh.md +0 -136
- package/docs/components.ja.md +0 -27
- package/docs/components.md +0 -27
- package/docs/components.zh-TW.md +0 -27
- package/docs/components.zh.md +0 -27
- package/docs/core-concepts.ja.md +0 -134
- package/docs/core-concepts.md +0 -135
- package/docs/core-concepts.zh-TW.md +0 -134
- package/docs/core-concepts.zh.md +0 -134
- package/docs/getting-started.ja.md +0 -132
- package/docs/getting-started.md +0 -132
- package/docs/getting-started.zh-TW.md +0 -132
- package/docs/getting-started.zh.md +0 -132
- package/docs/hooks-api.ja.md +0 -214
- package/docs/hooks-api.md +0 -214
- package/docs/hooks-api.zh-TW.md +0 -214
- package/docs/hooks-api.zh.md +0 -214
- package/docs/how-to-guides.ja.md +0 -413
- package/docs/how-to-guides.md +0 -413
- package/docs/how-to-guides.zh-TW.md +0 -413
- package/docs/how-to-guides.zh.md +0 -413
- package/docs/overview.ja.md +0 -51
- package/docs/overview.md +0 -51
- package/docs/overview.zh-TW.md +0 -51
- package/docs/overview.zh.md +0 -51
- package/glossary.md +0 -12
- package/src/@types/index.ts +0 -230
- package/src/@types/shims.d.ts +0 -18
- package/src/BlockletStudio/README.md +0 -116
- package/src/BlockletStudio/index.tsx +0 -145
- package/src/ComponentInstaller/ComponentInstaller.stories.jsx +0 -16
- package/src/ComponentInstaller/index.jsx +0 -207
- package/src/ComponentInstaller/installer-item.jsx +0 -129
- package/src/ComponentInstaller/locales.js +0 -22
- package/src/ComponentInstaller/use-component-installed.js +0 -88
- package/src/ComponentManager/components/add-component.tsx +0 -136
- package/src/ComponentManager/components/check-component.tsx +0 -3
- package/src/ComponentManager/components/publish-component.tsx +0 -90
- package/src/ComponentManager/components/resource-dialog.tsx +0 -91
- package/src/ComponentManager/index.tsx +0 -3
- package/src/ComponentManager/libs/locales.ts +0 -15
- package/src/Dashboard/Dashboard.stories.jsx +0 -20
- package/src/Dashboard/app-shell/app-badge.stories.tsx +0 -64
- package/src/Dashboard/app-shell/app-badge.tsx +0 -94
- package/src/Dashboard/app-shell/app-header.tsx +0 -104
- package/src/Dashboard/app-shell/app-info-context.tsx +0 -182
- package/src/Dashboard/app-shell/badges/app-badge-default.tsx +0 -130
- package/src/Dashboard/app-shell/badges/app-badge-did.tsx +0 -28
- package/src/Dashboard/app-shell/badges/app-badge-state.tsx +0 -40
- package/src/Dashboard/app-shell/badges/app-badge-switch.tsx +0 -72
- package/src/Dashboard/app-shell/badges/app-badge-version.tsx +0 -60
- package/src/Dashboard/app-shell/index.ts +0 -5
- package/src/Dashboard/index.jsx +0 -184
- package/src/Footer/Footer.stories.jsx +0 -33
- package/src/Footer/brand.jsx +0 -81
- package/src/Footer/copyright.jsx +0 -22
- package/src/Footer/index.jsx +0 -111
- package/src/Footer/internal-footer.jsx +0 -139
- package/src/Footer/layout/plain.jsx +0 -55
- package/src/Footer/layout/row.jsx +0 -43
- package/src/Footer/layout/standard.jsx +0 -114
- package/src/Footer/links.jsx +0 -321
- package/src/Footer/social-media.jsx +0 -55
- package/src/Header/Header.stories.jsx +0 -30
- package/src/Header/index.tsx +0 -259
- package/src/Icon/Icon.stories.jsx +0 -12
- package/src/Icon/index.tsx +0 -87
- package/src/Notifications/Snackbar.tsx +0 -261
- package/src/Notifications/hooks/use-title.tsx +0 -254
- package/src/Notifications/hooks/use-width.tsx +0 -16
- package/src/Notifications/utils.ts +0 -246
- package/src/UserCenter/assets/banner.png +0 -0
- package/src/UserCenter/components/config-inviter.tsx +0 -48
- package/src/UserCenter/components/config-profile.tsx +0 -99
- package/src/UserCenter/components/danger-zone.tsx +0 -82
- package/src/UserCenter/components/editable-field.tsx +0 -273
- package/src/UserCenter/components/fallback.tsx +0 -57
- package/src/UserCenter/components/nft-preview.tsx +0 -84
- package/src/UserCenter/components/nft.tsx +0 -279
- package/src/UserCenter/components/notification.tsx +0 -319
- package/src/UserCenter/components/passport.tsx +0 -107
- package/src/UserCenter/components/privacy.tsx +0 -120
- package/src/UserCenter/components/settings.tsx +0 -170
- package/src/UserCenter/components/status-dialog/date-picker.tsx +0 -77
- package/src/UserCenter/components/status-dialog/index.tsx +0 -293
- package/src/UserCenter/components/status-selector/duration-menu.tsx +0 -90
- package/src/UserCenter/components/status-selector/index.tsx +0 -58
- package/src/UserCenter/components/status-selector/menu-item.tsx +0 -56
- package/src/UserCenter/components/storage/action.tsx +0 -49
- package/src/UserCenter/components/storage/connected.tsx +0 -61
- package/src/UserCenter/components/storage/delete.tsx +0 -72
- package/src/UserCenter/components/storage/disconnect.tsx +0 -40
- package/src/UserCenter/components/storage/icons/empty-spaces-nft.svg +0 -1
- package/src/UserCenter/components/storage/icons/long-arrow.svg +0 -5
- package/src/UserCenter/components/storage/icons/space-connected.svg +0 -3
- package/src/UserCenter/components/storage/icons/space-disconnect.svg +0 -3
- package/src/UserCenter/components/storage/index.tsx +0 -41
- package/src/UserCenter/components/storage/preview-nft.tsx +0 -72
- package/src/UserCenter/components/third-party-login/index.tsx +0 -199
- package/src/UserCenter/components/third-party-login/third-party-item.tsx +0 -296
- package/src/UserCenter/components/user-center.tsx +0 -787
- package/src/UserCenter/components/user-info/address.tsx +0 -143
- package/src/UserCenter/components/user-info/index.tsx +0 -4
- package/src/UserCenter/components/user-info/link-preview-input.tsx +0 -274
- package/src/UserCenter/components/user-info/metadata.tsx +0 -658
- package/src/UserCenter/components/user-info/social-actions/chat.tsx +0 -43
- package/src/UserCenter/components/user-info/social-actions/follow.tsx +0 -23
- package/src/UserCenter/components/user-info/social-actions/index.tsx +0 -17
- package/src/UserCenter/components/user-info/switch-role.tsx +0 -42
- package/src/UserCenter/components/user-info/timezone-select.tsx +0 -119
- package/src/UserCenter/components/user-info/user-basic-info.tsx +0 -292
- package/src/UserCenter/components/user-info/user-info-item.tsx +0 -54
- package/src/UserCenter/components/user-info/user-info.tsx +0 -91
- package/src/UserCenter/components/user-info/user-status.tsx +0 -234
- package/src/UserCenter/components/user-info/utils.ts +0 -320
- package/src/UserCenter/components/webhook-item.tsx +0 -248
- package/src/UserCenter/index.tsx +0 -1
- package/src/UserCenter/libs/locales.ts +0 -378
- package/src/UserCenter/libs/utils.ts +0 -30
- package/src/UserSessions/components/user-session-info.tsx +0 -78
- package/src/UserSessions/components/user-sessions.tsx +0 -545
- package/src/UserSessions/index.tsx +0 -1
- package/src/UserSessions/libs/locales.ts +0 -60
- package/src/UserSessions/libs/utils.ts +0 -82
- package/src/blocklets.js +0 -195
- package/src/common/domain-warning.jsx +0 -178
- package/src/common/header-addons.jsx +0 -119
- package/src/common/link-blocker.jsx +0 -20
- package/src/common/notification-addon.jsx +0 -135
- package/src/common/org-switch/avatar-uploader.jsx +0 -271
- package/src/common/org-switch/create.jsx +0 -267
- package/src/common/org-switch/index.jsx +0 -407
- package/src/common/org-switch/locales.js +0 -52
- package/src/common/org-switch/use-org.jsx +0 -79
- package/src/common/overridable-theme-provider.jsx +0 -17
- package/src/common/wallet-hidden-topbar.js +0 -14
- package/src/common/wizard-modal.jsx +0 -200
- package/src/common/ws.js +0 -68
- package/src/contexts/config-user-space.tsx +0 -88
- package/src/contexts/user-followers.tsx +0 -54
- package/src/hooks/use-follow.tsx +0 -75
- package/src/hooks/use-mobile.tsx +0 -6
- package/src/index.ts +0 -16
- package/src/libs/constant.ts +0 -1
- package/src/libs/spaces.tsx +0 -18
- package/src/libs/with-hide-when-embed.tsx +0 -24
- package/src/types.js +0 -45
- package/src/utils.js +0 -161
- package/vite.config.mjs +0 -34
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
import { Box, Badge, Tooltip, type SvgIconProps } from '@mui/material';
|
|
2
|
-
import styled from '@emotion/styled';
|
|
3
|
-
import { lazy, Suspense, useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
|
-
import { useCreation, useMemoizedFn, useInterval, useUnmount } from 'ahooks';
|
|
5
|
-
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
6
|
-
import { translate } from '@arcblock/ux/lib/Locale/util';
|
|
7
|
-
import { formatToDatetime } from '@arcblock/ux/lib/Util';
|
|
8
|
-
import type { UserMetadata } from '../../../@types';
|
|
9
|
-
import { DurationEnum, StatusEnum } from '../../../@types';
|
|
10
|
-
import StatusDialog from '../status-dialog';
|
|
11
|
-
import { translations } from '../../libs/locales';
|
|
12
|
-
import { getTimeRemaining, isNotClear, isWithinTimeRange } from './utils';
|
|
13
|
-
import { StatusItem } from '../status-selector/menu-item';
|
|
14
|
-
|
|
15
|
-
const MeetingIcon = lazy(() => import('@arcblock/icons/lib/Meeting'));
|
|
16
|
-
const CommunityIcon = lazy(() => import('@arcblock/icons/lib/Community'));
|
|
17
|
-
const HolidayIcon = lazy(() => import('@arcblock/icons/lib/Holiday'));
|
|
18
|
-
const OffSickIcon = lazy(() => import('@arcblock/icons/lib/OffSick'));
|
|
19
|
-
const WorkingRemotelyIcon = lazy(() => import('@arcblock/icons/lib/WorkingRemotely'));
|
|
20
|
-
|
|
21
|
-
const StatusIconMap: Record<StatusEnum, React.FC<SvgIconProps> | undefined> = {
|
|
22
|
-
[StatusEnum.Meeting]: MeetingIcon,
|
|
23
|
-
[StatusEnum.Community]: CommunityIcon,
|
|
24
|
-
[StatusEnum.Holiday]: HolidayIcon,
|
|
25
|
-
[StatusEnum.OffSick]: OffSickIcon,
|
|
26
|
-
[StatusEnum.WorkingRemotely]: WorkingRemotelyIcon,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const QuickSettings = {
|
|
30
|
-
[StatusEnum.Meeting]: {
|
|
31
|
-
duration: DurationEnum.OneHour,
|
|
32
|
-
durationName: 'userStatus.duration.OneHour',
|
|
33
|
-
},
|
|
34
|
-
[StatusEnum.Community]: {
|
|
35
|
-
duration: DurationEnum.ThirtyMinutes,
|
|
36
|
-
durationName: 'userStatus.duration.ThirtyMinutes',
|
|
37
|
-
},
|
|
38
|
-
[StatusEnum.Holiday]: {
|
|
39
|
-
duration: DurationEnum.ThisWeek,
|
|
40
|
-
durationName: 'userStatus.duration.ThisWeek',
|
|
41
|
-
},
|
|
42
|
-
[StatusEnum.OffSick]: {
|
|
43
|
-
duration: DurationEnum.Today,
|
|
44
|
-
durationName: 'userStatus.duration.Today',
|
|
45
|
-
},
|
|
46
|
-
[StatusEnum.WorkingRemotely]: {
|
|
47
|
-
duration: DurationEnum.ThisWeek,
|
|
48
|
-
durationName: 'userStatus.duration.ThisWeek',
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export default function UserStatus({
|
|
53
|
-
isMobile = undefined,
|
|
54
|
-
size,
|
|
55
|
-
isMyself,
|
|
56
|
-
status,
|
|
57
|
-
onChange,
|
|
58
|
-
timezone = undefined,
|
|
59
|
-
}: {
|
|
60
|
-
isMobile?: boolean;
|
|
61
|
-
size: number;
|
|
62
|
-
isMyself: boolean;
|
|
63
|
-
status: UserMetadata['status'];
|
|
64
|
-
onChange: (v: UserMetadata['status']) => void;
|
|
65
|
-
timezone?: string;
|
|
66
|
-
}) {
|
|
67
|
-
const { locale } = useLocaleContext();
|
|
68
|
-
const t = useMemoizedFn((key, data = {}) => {
|
|
69
|
-
return translate(translations, key, locale, 'en', data);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const [interval, setInterval] = useState<number | undefined>(undefined);
|
|
73
|
-
|
|
74
|
-
const pauseInterval = useMemoizedFn(() => {
|
|
75
|
-
setInterval(undefined);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
setInterval(1000);
|
|
80
|
-
}, [status]);
|
|
81
|
-
|
|
82
|
-
const clear = useInterval(() => {
|
|
83
|
-
if (status?.value && status?.dateRange?.length === 2) {
|
|
84
|
-
const notClear = isNotClear(status);
|
|
85
|
-
if (notClear) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
const isWithin = isWithinTimeRange(status.dateRange as [Date, Date]);
|
|
89
|
-
if (!isWithin) {
|
|
90
|
-
pauseInterval();
|
|
91
|
-
onChange(undefined);
|
|
92
|
-
} else {
|
|
93
|
-
// 根据距离结束时间长度,设置 interval
|
|
94
|
-
const timeRemaining = getTimeRemaining(status.dateRange[1]);
|
|
95
|
-
if (timeRemaining > 0) {
|
|
96
|
-
setInterval(timeRemaining);
|
|
97
|
-
} else {
|
|
98
|
-
pauseInterval();
|
|
99
|
-
onChange(undefined);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
pauseInterval();
|
|
104
|
-
}
|
|
105
|
-
}, interval);
|
|
106
|
-
|
|
107
|
-
useUnmount(() => {
|
|
108
|
-
clear();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const [anchorEl, setAnchorEl] = useState<HTMLDivElement | null>(null);
|
|
112
|
-
|
|
113
|
-
const getDurationData = useCallback(() => {
|
|
114
|
-
const data = Object.keys(DurationEnum).map((key) => ({
|
|
115
|
-
id: DurationEnum[key as keyof typeof DurationEnum],
|
|
116
|
-
name: t(`userStatus.duration.${key}`),
|
|
117
|
-
}));
|
|
118
|
-
return data;
|
|
119
|
-
}, [t]);
|
|
120
|
-
|
|
121
|
-
const statusData = useCreation(() => {
|
|
122
|
-
const durationData = getDurationData();
|
|
123
|
-
return Object.keys(StatusEnum).map((key) => {
|
|
124
|
-
const quickSetting = QuickSettings[StatusEnum[key as keyof typeof StatusEnum]];
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
id: StatusEnum[key as keyof typeof StatusEnum],
|
|
128
|
-
name: t(`userStatus.${key}`),
|
|
129
|
-
icon: StatusIconMap[StatusEnum[key as keyof typeof StatusEnum]],
|
|
130
|
-
...(quickSetting ? { duration: quickSetting.duration, durationName: t(quickSetting.durationName) } : {}),
|
|
131
|
-
children: durationData,
|
|
132
|
-
};
|
|
133
|
-
});
|
|
134
|
-
}, [t, getDurationData, locale]);
|
|
135
|
-
|
|
136
|
-
const onOpenStatusSelector = (event: React.MouseEvent<HTMLDivElement>) => {
|
|
137
|
-
if (!isMyself) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
setAnchorEl(event.currentTarget);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const onCloseStatusSelector = () => {
|
|
144
|
-
setAnchorEl(null);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const onStatusChange = (v?: UserMetadata['status']) => {
|
|
148
|
-
if (!isMyself) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
onChange(v);
|
|
152
|
-
onCloseStatusSelector();
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const StatusIcon = StatusIconMap[status?.value as keyof typeof StatusIconMap];
|
|
156
|
-
|
|
157
|
-
const tooltipTitle = useMemo(() => {
|
|
158
|
-
const currentStatus = statusData.find((item) => item.id === status?.value);
|
|
159
|
-
if (currentStatus) {
|
|
160
|
-
const localeOption = locale === 'zh' ? 'zh-cn' : 'en-us';
|
|
161
|
-
let range;
|
|
162
|
-
const notClear = isNotClear(status);
|
|
163
|
-
if (!notClear) {
|
|
164
|
-
range = status?.dateRange?.map((item) => {
|
|
165
|
-
return formatToDatetime(item, { locale: localeOption });
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
if (range && range.length > 0) {
|
|
169
|
-
return `${currentStatus.name}: ${range.join('~')}`;
|
|
170
|
-
}
|
|
171
|
-
return currentStatus.name;
|
|
172
|
-
}
|
|
173
|
-
return null;
|
|
174
|
-
}, [status, statusData, locale]);
|
|
175
|
-
|
|
176
|
-
const open = Boolean(anchorEl);
|
|
177
|
-
|
|
178
|
-
return (
|
|
179
|
-
<StatusDiv size={size} isMobile={isMobile}>
|
|
180
|
-
<Tooltip title={tooltipTitle}>
|
|
181
|
-
<Box
|
|
182
|
-
className="status-icon"
|
|
183
|
-
onClick={onOpenStatusSelector}
|
|
184
|
-
sx={{
|
|
185
|
-
display: 'flex',
|
|
186
|
-
alignItems: 'center',
|
|
187
|
-
justifyContent: 'center',
|
|
188
|
-
}}>
|
|
189
|
-
{StatusIcon ? (
|
|
190
|
-
<Suspense fallback={null}>
|
|
191
|
-
<StatusIcon style={{ width: 16, height: 16 }} />
|
|
192
|
-
</Suspense>
|
|
193
|
-
) : (
|
|
194
|
-
<Badge color="success" variant="dot" />
|
|
195
|
-
)}
|
|
196
|
-
</Box>
|
|
197
|
-
</Tooltip>
|
|
198
|
-
{open && (
|
|
199
|
-
<StatusDialog
|
|
200
|
-
selected={status}
|
|
201
|
-
data={statusData as StatusItem[]}
|
|
202
|
-
open={open}
|
|
203
|
-
onSelect={onStatusChange}
|
|
204
|
-
onClose={onCloseStatusSelector}
|
|
205
|
-
timezone={timezone}
|
|
206
|
-
/>
|
|
207
|
-
)}
|
|
208
|
-
</StatusDiv>
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const StatusDiv = styled('div')<{ size: number; isMobile?: boolean }>`
|
|
213
|
-
position: absolute;
|
|
214
|
-
left: ${({ size }) => `${(size * 3) / 4}px`};
|
|
215
|
-
top: ${({ size }) => `${size * 0.65}px`};
|
|
216
|
-
width: ${({ isMobile }) => (isMobile ? '22px' : '32px')};
|
|
217
|
-
height: ${({ isMobile }) => (isMobile ? '22px' : '32px')};
|
|
218
|
-
border-radius: ${({ isMobile }) => (isMobile ? '11px' : '16px')};
|
|
219
|
-
display: flex;
|
|
220
|
-
align-items: center;
|
|
221
|
-
justify-content: center;
|
|
222
|
-
background-color: #ffffff;
|
|
223
|
-
overflow: hidden;
|
|
224
|
-
white-space: nowrap;
|
|
225
|
-
cursor: pointer;
|
|
226
|
-
|
|
227
|
-
.status-icon {
|
|
228
|
-
flex-shrink: 0;
|
|
229
|
-
width: ${({ isMobile }) => (isMobile ? '16px' : '26px')};
|
|
230
|
-
height: ${({ isMobile }) => (isMobile ? '16px' : '26px')};
|
|
231
|
-
border-radius: ${({ isMobile }) => (isMobile ? '8px' : '13px')};
|
|
232
|
-
background-color: #eff1f5;
|
|
233
|
-
}
|
|
234
|
-
`;
|
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
import isUrl from 'is-url';
|
|
2
|
-
import { withHttps } from 'ufo';
|
|
3
|
-
import dayjs from 'dayjs';
|
|
4
|
-
import timezone from 'dayjs/plugin/timezone';
|
|
5
|
-
import utc from 'dayjs/plugin/utc';
|
|
6
|
-
import { DurationEnum, UserMetadata } from '../../../@types';
|
|
7
|
-
|
|
8
|
-
// 扩展 dayjs 插件
|
|
9
|
-
dayjs.extend(utc);
|
|
10
|
-
dayjs.extend(timezone);
|
|
11
|
-
|
|
12
|
-
const HOUR = 3600;
|
|
13
|
-
const MINUTES_30 = 1800;
|
|
14
|
-
const MINUTES_10 = 600;
|
|
15
|
-
const MINUTES_5 = 300;
|
|
16
|
-
const MINUTES_1 = 60;
|
|
17
|
-
const SECOND = 1;
|
|
18
|
-
|
|
19
|
-
export const currentTimezone = dayjs.tz.guess();
|
|
20
|
-
|
|
21
|
-
// 常用时区列表,作为兼容性 fallback
|
|
22
|
-
const COMMON_TIMEZONES = [
|
|
23
|
-
'America/New_York',
|
|
24
|
-
'America/Chicago',
|
|
25
|
-
'America/Denver',
|
|
26
|
-
'America/Los_Angeles',
|
|
27
|
-
'Europe/London',
|
|
28
|
-
'Europe/Paris',
|
|
29
|
-
'Europe/Berlin',
|
|
30
|
-
'Europe/Rome',
|
|
31
|
-
'Asia/Tokyo',
|
|
32
|
-
'Asia/Shanghai',
|
|
33
|
-
'Asia/Hong_Kong',
|
|
34
|
-
'Asia/Singapore',
|
|
35
|
-
'Asia/Seoul',
|
|
36
|
-
'Asia/Kolkata',
|
|
37
|
-
'Australia/Sydney',
|
|
38
|
-
'Australia/Melbourne',
|
|
39
|
-
'Pacific/Auckland',
|
|
40
|
-
'America/Sao_Paulo',
|
|
41
|
-
'America/Mexico_City',
|
|
42
|
-
'Africa/Cairo',
|
|
43
|
-
'UTC',
|
|
44
|
-
];
|
|
45
|
-
|
|
46
|
-
// 获取时区列表的兼容性函数
|
|
47
|
-
const getTimezoneList = () => {
|
|
48
|
-
// 优先使用现代 API
|
|
49
|
-
if (typeof Intl !== 'undefined' && Intl.supportedValuesOf) {
|
|
50
|
-
try {
|
|
51
|
-
return Intl.supportedValuesOf('timeZone');
|
|
52
|
-
} catch (error) {
|
|
53
|
-
console.warn('Intl.supportedValuesOf not supported, falling back to common timezones');
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 尝试使用 Intl.DateTimeFormat 获取时区
|
|
58
|
-
if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) {
|
|
59
|
-
try {
|
|
60
|
-
// 使用 resolvedOptions 检测当前时区是否可用
|
|
61
|
-
const formatter = new Intl.DateTimeFormat('en', { timeZone: 'UTC' });
|
|
62
|
-
if (formatter.resolvedOptions().timeZone) {
|
|
63
|
-
return COMMON_TIMEZONES;
|
|
64
|
-
}
|
|
65
|
-
} catch (error) {
|
|
66
|
-
console.warn('Intl.DateTimeFormat timezone support limited');
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// 最后的 fallback
|
|
71
|
-
return COMMON_TIMEZONES;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export const getTimezones = () => {
|
|
75
|
-
const timezones = getTimezoneList();
|
|
76
|
-
|
|
77
|
-
const formattedTimezones = timezones
|
|
78
|
-
.map((tz) => {
|
|
79
|
-
try {
|
|
80
|
-
const offset = dayjs.tz(dayjs(), tz).utcOffset() / 60; // 计算 UTC 偏移 (小时)
|
|
81
|
-
const hours = Math.floor(offset);
|
|
82
|
-
const minutes = (offset % 1) * 60;
|
|
83
|
-
const label = `GMT${hours >= 0 ? '+' : ''}${hours}:${minutes === 30 ? '30' : '00'}`;
|
|
84
|
-
|
|
85
|
-
return { label, value: tz };
|
|
86
|
-
} catch (error) {
|
|
87
|
-
// 如果时区不支持,跳过
|
|
88
|
-
console.warn(`Timezone ${tz} not supported, skipping`);
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
.filter((tz): tz is { label: string; value: string } => tz !== null); // 类型守卫
|
|
93
|
-
|
|
94
|
-
return formattedTimezones
|
|
95
|
-
.sort((a, b) => {
|
|
96
|
-
const [hoursA, minutesA] = a.label.replace('GMT', '').split(':').map(Number);
|
|
97
|
-
const [hoursB, minutesB] = b.label.replace('GMT', '').split(':').map(Number);
|
|
98
|
-
const totalOffsetA = hoursA * 60 + minutesA; // 统一为分钟数
|
|
99
|
-
const totalOffsetB = hoursB * 60 + minutesB;
|
|
100
|
-
return totalOffsetB - totalOffsetA; // **降序排列**
|
|
101
|
-
})
|
|
102
|
-
.map((tz) => ({
|
|
103
|
-
label: `(${tz.label}) ${tz.value}`,
|
|
104
|
-
value: tz.value,
|
|
105
|
-
}));
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
export const isValidUrl = (url: string) => {
|
|
109
|
-
return isUrl(withHttps(url)); // 补充协议后在进行验证是否是合法的url
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
export const isDuplicateUrl = (url1: string, url2: string) => {
|
|
113
|
-
if (!url1 || !url2) {
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
if (!isValidUrl(url1) || !isValidUrl(url2)) {
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const parsedUrl1 = withHttps(url1.trim());
|
|
121
|
-
const parsedUrl2 = withHttps(url2.trim());
|
|
122
|
-
return parsedUrl1.toLowerCase() === parsedUrl2.toLowerCase();
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* 根据 duration 类型,计算出date range
|
|
127
|
-
* @param status
|
|
128
|
-
* @returns
|
|
129
|
-
*/
|
|
130
|
-
export const getStatusDuration = (status: UserMetadata['status']) => {
|
|
131
|
-
let dateRange: dayjs.Dayjs[] = status?.dateRange?.map((d) => dayjs(d)) ?? [];
|
|
132
|
-
const current = dayjs();
|
|
133
|
-
switch (status?.duration) {
|
|
134
|
-
case DurationEnum.ThirtyMinutes:
|
|
135
|
-
dateRange = [current, current.add(30, 'minutes')];
|
|
136
|
-
break;
|
|
137
|
-
case DurationEnum.OneHour:
|
|
138
|
-
dateRange = [current, current.add(1, 'hour')];
|
|
139
|
-
break;
|
|
140
|
-
case DurationEnum.FourHours:
|
|
141
|
-
dateRange = [current, current.add(4, 'hours')];
|
|
142
|
-
break;
|
|
143
|
-
case DurationEnum.Today:
|
|
144
|
-
dateRange = [current, current.endOf('day')];
|
|
145
|
-
break;
|
|
146
|
-
case DurationEnum.ThisWeek:
|
|
147
|
-
dateRange = [current, current.endOf('week')];
|
|
148
|
-
break;
|
|
149
|
-
case DurationEnum.NoClear:
|
|
150
|
-
dateRange = [current, current];
|
|
151
|
-
break;
|
|
152
|
-
default:
|
|
153
|
-
break;
|
|
154
|
-
}
|
|
155
|
-
return dateRange.map((d) => d.toDate());
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* 根据状态的 duration,判断是否在时间范围内
|
|
160
|
-
* @param status
|
|
161
|
-
* @returns
|
|
162
|
-
*/
|
|
163
|
-
export const isWithinTimeRange = (dateRange: [Date, Date]) => {
|
|
164
|
-
const current = dayjs();
|
|
165
|
-
return current.isAfter(dayjs(dateRange[0])) && current.isBefore(dayjs(dateRange[1]));
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* 判断状态持续时间是否为不可清除
|
|
170
|
-
* @param status
|
|
171
|
-
* @returns
|
|
172
|
-
*/
|
|
173
|
-
export const isNotClear = (status: UserMetadata['status']) => {
|
|
174
|
-
const { duration, dateRange } = status ?? {};
|
|
175
|
-
if (!duration || !dateRange) {
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
return duration === DurationEnum.NoClear || dayjs(dateRange?.[0]).isSame(dayjs(dateRange?.[1]));
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* 获取当前时间距离结束时间还有多久
|
|
183
|
-
*/
|
|
184
|
-
export const getTimeRemaining = (date: Date) => {
|
|
185
|
-
const now = dayjs();
|
|
186
|
-
const end = dayjs(date);
|
|
187
|
-
const diffSeconds = end.diff(now, 'seconds');
|
|
188
|
-
|
|
189
|
-
// 转换为毫秒
|
|
190
|
-
const toMilliseconds = (seconds: number) => seconds * 1000;
|
|
191
|
-
|
|
192
|
-
if (diffSeconds >= HOUR) {
|
|
193
|
-
return toMilliseconds(HOUR); // 1小时 = 3600000ms
|
|
194
|
-
}
|
|
195
|
-
if (diffSeconds >= MINUTES_30) {
|
|
196
|
-
return toMilliseconds(MINUTES_30); // 30分钟 = 1800000ms
|
|
197
|
-
}
|
|
198
|
-
if (diffSeconds >= MINUTES_10) {
|
|
199
|
-
return toMilliseconds(MINUTES_10); // 10分钟 = 600000ms
|
|
200
|
-
}
|
|
201
|
-
if (diffSeconds >= MINUTES_5) {
|
|
202
|
-
return toMilliseconds(MINUTES_5); // 5分钟 = 300000ms
|
|
203
|
-
}
|
|
204
|
-
if (diffSeconds >= MINUTES_1) {
|
|
205
|
-
return toMilliseconds(MINUTES_1); // 1分钟 = 60000ms
|
|
206
|
-
}
|
|
207
|
-
if (diffSeconds >= SECOND) {
|
|
208
|
-
return toMilliseconds(SECOND); // 1秒 = 1000ms
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return 0; // 如果时间已过期,返回0
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
// 只支持在 sx 中使用
|
|
215
|
-
export const defaultButtonStyle = {
|
|
216
|
-
color: 'text.primary',
|
|
217
|
-
borderColor: 'grey.100',
|
|
218
|
-
backgroundColor: 'background.default',
|
|
219
|
-
'&:hover': {
|
|
220
|
-
borderColor: 'grey.100',
|
|
221
|
-
backgroundColor: 'action.hover',
|
|
222
|
-
},
|
|
223
|
-
py: 0.5,
|
|
224
|
-
borderRadius: 1,
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
export const primaryButtonStyle = {
|
|
228
|
-
color: 'primary.contrastText',
|
|
229
|
-
borderColor: 'primary.main',
|
|
230
|
-
backgroundColor: 'primary.main',
|
|
231
|
-
'&:hover': {
|
|
232
|
-
borderColor: 'primary.main',
|
|
233
|
-
backgroundColor: 'primary.main',
|
|
234
|
-
},
|
|
235
|
-
py: 0.5,
|
|
236
|
-
borderRadius: 1,
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
// 域名关键词到平台标识符的映射表
|
|
240
|
-
const DOMAIN_PLATFORM_MAP: Record<string, { domains: string[]; options?: Record<string, any> }> = {
|
|
241
|
-
x: {
|
|
242
|
-
domains: ['twitter.com', 'x.com'],
|
|
243
|
-
},
|
|
244
|
-
facebook: {
|
|
245
|
-
domains: ['facebook.com', 'fb.com'],
|
|
246
|
-
options: { skipDarkInvert: true },
|
|
247
|
-
},
|
|
248
|
-
'linkedin-icon': {
|
|
249
|
-
domains: ['linkedin.com'],
|
|
250
|
-
options: { skipDarkInvert: true },
|
|
251
|
-
},
|
|
252
|
-
'github-icon': {
|
|
253
|
-
domains: ['github.com'],
|
|
254
|
-
},
|
|
255
|
-
'instagram-icon': {
|
|
256
|
-
domains: ['instagram.com'],
|
|
257
|
-
},
|
|
258
|
-
'youtube-icon': {
|
|
259
|
-
domains: ['youtube.com', 'youtu.be'],
|
|
260
|
-
options: { skipDarkInvert: true },
|
|
261
|
-
},
|
|
262
|
-
'tiktok-icon': {
|
|
263
|
-
domains: ['tiktok.com'],
|
|
264
|
-
},
|
|
265
|
-
'reddit-icon': {
|
|
266
|
-
domains: ['reddit.com'],
|
|
267
|
-
options: { skipDarkInvert: true },
|
|
268
|
-
},
|
|
269
|
-
'medium-icon': {
|
|
270
|
-
domains: ['medium.com'],
|
|
271
|
-
},
|
|
272
|
-
'discord-icon': {
|
|
273
|
-
domains: ['discord.com', 'discord.gg'],
|
|
274
|
-
options: { skipDarkInvert: true },
|
|
275
|
-
},
|
|
276
|
-
telegram: {
|
|
277
|
-
domains: ['telegram.org', 't.me'],
|
|
278
|
-
options: { skipDarkInvert: true },
|
|
279
|
-
},
|
|
280
|
-
'whatsapp-monochrome-icon': {
|
|
281
|
-
domains: ['whatsapp.com'],
|
|
282
|
-
},
|
|
283
|
-
producthunt: {
|
|
284
|
-
domains: ['producthunt.com'],
|
|
285
|
-
options: { skipDarkInvert: true },
|
|
286
|
-
},
|
|
287
|
-
ycombinator: {
|
|
288
|
-
domains: ['ycombinator.com', 'news.ycombinator.com'],
|
|
289
|
-
options: { skipDarkInvert: true },
|
|
290
|
-
},
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
export const getLogoByUrl = (url: string): { icon: string; options?: Record<string, any> } | undefined => {
|
|
294
|
-
try {
|
|
295
|
-
// 添加协议(如果没有)
|
|
296
|
-
const fullUrl = withHttps(url);
|
|
297
|
-
|
|
298
|
-
// 解析 URL
|
|
299
|
-
const urlObj = new URL(fullUrl);
|
|
300
|
-
const hostname = urlObj.hostname.toLowerCase();
|
|
301
|
-
|
|
302
|
-
// 移除 www. 前缀
|
|
303
|
-
const domain = hostname.replace(/^www\./, '');
|
|
304
|
-
|
|
305
|
-
// 根据域名返回对应的平台标识符
|
|
306
|
-
for (const [platform, { domains, options }] of Object.entries(DOMAIN_PLATFORM_MAP)) {
|
|
307
|
-
if (domains.some((d) => domain === d || domain.endsWith(`.${d}`))) {
|
|
308
|
-
return {
|
|
309
|
-
icon: `logos:${platform}`,
|
|
310
|
-
options,
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// 对于未匹配的域名,返回空字符串
|
|
316
|
-
return undefined;
|
|
317
|
-
} catch {
|
|
318
|
-
return undefined;
|
|
319
|
-
}
|
|
320
|
-
};
|