@blocklet/ui-react 2.12.13 → 2.12.14
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/@types/index.d.ts +2 -1
- package/lib/@types/index.js +1 -0
- package/lib/UserCenter/components/editable-field.d.ts +2 -1
- package/lib/UserCenter/components/editable-field.js +5 -1
- package/lib/UserCenter/components/status-dialog/date-picker.d.ts +10 -0
- package/lib/UserCenter/components/status-dialog/date-picker.js +52 -0
- package/lib/UserCenter/components/status-dialog/index.d.ts +12 -0
- package/lib/UserCenter/components/status-dialog/index.js +238 -0
- package/lib/UserCenter/components/status-selector/menu-item.d.ts +2 -0
- package/lib/UserCenter/components/user-center.js +8 -3
- package/lib/UserCenter/components/user-info/clock.js +3 -2
- package/lib/UserCenter/components/user-info/metadata.js +15 -62
- package/lib/UserCenter/components/user-info/timezone-select.d.ts +8 -0
- package/lib/UserCenter/components/user-info/timezone-select.js +99 -0
- package/lib/UserCenter/components/user-info/user-basic-info.js +4 -14
- package/lib/UserCenter/components/user-info/user-status.d.ts +2 -1
- package/lib/UserCenter/components/user-info/user-status.js +49 -14
- package/lib/UserCenter/components/user-info/utils.d.ts +23 -0
- package/lib/UserCenter/components/user-info/utils.js +25 -1
- package/lib/UserCenter/libs/locales.d.ts +12 -0
- package/lib/UserCenter/libs/locales.js +18 -6
- package/package.json +4 -4
- package/src/@types/index.ts +1 -0
- package/src/UserCenter/components/editable-field.tsx +6 -0
- package/src/UserCenter/components/status-dialog/date-picker.tsx +67 -0
- package/src/UserCenter/components/status-dialog/index.tsx +280 -0
- package/src/UserCenter/components/status-selector/menu-item.tsx +2 -0
- package/src/UserCenter/components/user-center.tsx +7 -2
- package/src/UserCenter/components/user-info/clock.tsx +3 -2
- package/src/UserCenter/components/user-info/metadata.tsx +17 -70
- package/src/UserCenter/components/user-info/timezone-select.tsx +114 -0
- package/src/UserCenter/components/user-info/user-basic-info.tsx +4 -11
- package/src/UserCenter/components/user-info/user-status.tsx +57 -13
- package/src/UserCenter/components/user-info/utils.ts +29 -1
- package/src/UserCenter/libs/locales.ts +14 -2
package/lib/@types/index.d.ts
CHANGED
package/lib/@types/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export var DurationEnum = /* @__PURE__ */ ((DurationEnum2) => {
|
|
|
4
4
|
DurationEnum2["FourHours"] = "4_hours";
|
|
5
5
|
DurationEnum2["Today"] = "today";
|
|
6
6
|
DurationEnum2["ThisWeek"] = "this_week";
|
|
7
|
+
DurationEnum2["Custom"] = "custom";
|
|
7
8
|
return DurationEnum2;
|
|
8
9
|
})(DurationEnum || {});
|
|
9
10
|
export var StatusEnum = /* @__PURE__ */ ((StatusEnum2) => {
|
|
@@ -17,6 +17,7 @@ interface EditableFieldProps {
|
|
|
17
17
|
style?: React.CSSProperties;
|
|
18
18
|
verified?: boolean;
|
|
19
19
|
errorMsg?: string;
|
|
20
|
+
canEdit?: boolean;
|
|
20
21
|
}
|
|
21
|
-
declare function EditableField({ value, onChange, onValueValidate, errorMsg, editable, component, placeholder, rows, maxLength, icon, label, children, tooltip, inline, style, verified, }: EditableFieldProps): JSX.Element | null;
|
|
22
|
+
declare function EditableField({ value, onChange, onValueValidate, errorMsg, editable, component, placeholder, rows, maxLength, icon, label, children, tooltip, inline, style, verified, canEdit, }: EditableFieldProps): JSX.Element | null;
|
|
22
23
|
export default EditableField;
|
|
@@ -42,7 +42,8 @@ function EditableField({
|
|
|
42
42
|
tooltip,
|
|
43
43
|
inline = true,
|
|
44
44
|
style = {},
|
|
45
|
-
verified = false
|
|
45
|
+
verified = false,
|
|
46
|
+
canEdit = true
|
|
46
47
|
}) {
|
|
47
48
|
const { locale } = useLocaleContext();
|
|
48
49
|
const t = useMemoizedFn((key, data = {}) => {
|
|
@@ -116,6 +117,9 @@ function EditableField({
|
|
|
116
117
|
)
|
|
117
118
|
] });
|
|
118
119
|
}, [value, handleChange, component, placeholder, rows, children]);
|
|
120
|
+
if (!canEdit && editable) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
119
123
|
if (!editable) {
|
|
120
124
|
return value ? /* @__PURE__ */ jsx(Tooltip, { title: tooltip, placement: "top", children: /* @__PURE__ */ jsxs(
|
|
121
125
|
Typography,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface DateTimeInputProps {
|
|
2
|
+
value: Date;
|
|
3
|
+
onChange: (value: Date) => void;
|
|
4
|
+
error?: boolean;
|
|
5
|
+
helperText?: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
timezone?: string;
|
|
8
|
+
}
|
|
9
|
+
export default function DateTimeInput({ value, onChange, error, helperText, label, timezone }: DateTimeInputProps): JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef } from "react";
|
|
3
|
+
import { TextField, FormControl, FormHelperText } from "@mui/material";
|
|
4
|
+
import dayjs from "dayjs";
|
|
5
|
+
import { useCreation } from "ahooks";
|
|
6
|
+
export default function DateTimeInput({ value, onChange, error, helperText, label, timezone }) {
|
|
7
|
+
const inputRef = useRef(null);
|
|
8
|
+
const minDateTime = dayjs().format("YYYY-MM-DDTHH:mm");
|
|
9
|
+
const formatValue = useCreation(() => {
|
|
10
|
+
let date = dayjs();
|
|
11
|
+
if (value) {
|
|
12
|
+
date = timezone ? dayjs(value).tz(timezone) : dayjs(value);
|
|
13
|
+
}
|
|
14
|
+
return date.format("YYYY-MM-DD HH:mm");
|
|
15
|
+
}, [value, timezone]);
|
|
16
|
+
const handleChange = (e) => {
|
|
17
|
+
onChange(new Date(e.target.value));
|
|
18
|
+
};
|
|
19
|
+
const handleClick = () => {
|
|
20
|
+
inputRef.current?.showPicker();
|
|
21
|
+
};
|
|
22
|
+
return /* @__PURE__ */ jsxs(FormControl, { fullWidth: true, error, children: [
|
|
23
|
+
/* @__PURE__ */ jsx(
|
|
24
|
+
TextField,
|
|
25
|
+
{
|
|
26
|
+
inputRef,
|
|
27
|
+
type: "datetime-local",
|
|
28
|
+
value: formatValue,
|
|
29
|
+
onChange: handleChange,
|
|
30
|
+
inputProps: {
|
|
31
|
+
min: minDateTime,
|
|
32
|
+
style: { cursor: "pointer" }
|
|
33
|
+
},
|
|
34
|
+
label,
|
|
35
|
+
error,
|
|
36
|
+
onClick: handleClick,
|
|
37
|
+
InputLabelProps: {
|
|
38
|
+
shrink: true
|
|
39
|
+
},
|
|
40
|
+
sx: {
|
|
41
|
+
"& .MuiInputBase-root": {
|
|
42
|
+
cursor: "pointer"
|
|
43
|
+
},
|
|
44
|
+
"& .MuiOutlinedInput-notchedOutline": {
|
|
45
|
+
cursor: "pointer"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
),
|
|
50
|
+
helperText && /* @__PURE__ */ jsx(FormHelperText, { children: helperText })
|
|
51
|
+
] });
|
|
52
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { UserMetadata } from '../../../@types';
|
|
2
|
+
import { StatusItem } from '../status-selector/menu-item';
|
|
3
|
+
interface StatusDialogProps {
|
|
4
|
+
open: boolean;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
data: Array<StatusItem>;
|
|
7
|
+
selected: UserMetadata['status'];
|
|
8
|
+
onSelect: (v: UserMetadata['status']) => void;
|
|
9
|
+
timezone?: string;
|
|
10
|
+
}
|
|
11
|
+
export default function StatusDialog({ open, onClose, data, selected, onSelect, timezone }: StatusDialogProps): import("react").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState, createElement, Suspense, useMemo } from "react";
|
|
3
|
+
import {
|
|
4
|
+
Dialog,
|
|
5
|
+
DialogTitle,
|
|
6
|
+
DialogContent,
|
|
7
|
+
DialogActions,
|
|
8
|
+
Button,
|
|
9
|
+
FormControl,
|
|
10
|
+
InputLabel,
|
|
11
|
+
Select,
|
|
12
|
+
MenuItem,
|
|
13
|
+
ListItemIcon,
|
|
14
|
+
Box,
|
|
15
|
+
Typography,
|
|
16
|
+
IconButton
|
|
17
|
+
} from "@mui/material";
|
|
18
|
+
import dayjs from "dayjs";
|
|
19
|
+
import CloseIcon from "@mui/icons-material/Close";
|
|
20
|
+
import { translate } from "@arcblock/ux/lib/Locale/util";
|
|
21
|
+
import { useMemoizedFn } from "ahooks";
|
|
22
|
+
import { temp as colors } from "@arcblock/ux/lib/Colors";
|
|
23
|
+
import ArrowDownwardIcon from "@arcblock/icons/lib/ArrowDown";
|
|
24
|
+
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
25
|
+
import { DurationEnum } from "../../../@types/index.js";
|
|
26
|
+
import { translations } from "../../libs/locales.js";
|
|
27
|
+
import DateTimeInput from "./date-picker.js";
|
|
28
|
+
import { defaultButtonStyle, primaryButtonStyle } from "../user-info/utils.js";
|
|
29
|
+
const selectStyle = {
|
|
30
|
+
padding: "8px 16px",
|
|
31
|
+
borderRadius: "8px",
|
|
32
|
+
"&:hover": {
|
|
33
|
+
"fieldset.MuiOutlinedInput-notchedOutline": {
|
|
34
|
+
borderColor: colors.dividerColor
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"fieldset.MuiOutlinedInput-notchedOutline": {
|
|
38
|
+
borderColor: colors.dividerColor,
|
|
39
|
+
borderRadius: "8px"
|
|
40
|
+
},
|
|
41
|
+
".MuiSelect-select": {
|
|
42
|
+
padding: "0 !important",
|
|
43
|
+
display: "flex",
|
|
44
|
+
alignItems: "center"
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export default function StatusDialog({ open, onClose, data, selected, onSelect, timezone }) {
|
|
48
|
+
const { locale } = useLocaleContext();
|
|
49
|
+
const t = useMemoizedFn((key, _data = {}) => {
|
|
50
|
+
return translate(translations, key, locale, "en", _data);
|
|
51
|
+
});
|
|
52
|
+
const [status, setStatus] = useState(selected?.value || "");
|
|
53
|
+
const [duration, setDuration] = useState(selected?.duration || "");
|
|
54
|
+
const [customDate, setCustomDate] = useState(selected?.dateRange?.[1] || "");
|
|
55
|
+
const [changed, setChanged] = useState(false);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
setStatus(selected?.value || "");
|
|
58
|
+
setDuration(selected?.duration || "");
|
|
59
|
+
setCustomDate(selected?.dateRange?.[1] || "");
|
|
60
|
+
}, [selected]);
|
|
61
|
+
const isValid = useMemo(() => {
|
|
62
|
+
return status && duration;
|
|
63
|
+
}, [status, duration]);
|
|
64
|
+
const selectedStatus = data.find((item) => item.id === status);
|
|
65
|
+
const handleQuickSettingClick = (item) => {
|
|
66
|
+
onSelect({
|
|
67
|
+
value: item.id,
|
|
68
|
+
...item.duration ? { duration: item.duration } : {}
|
|
69
|
+
});
|
|
70
|
+
onClose();
|
|
71
|
+
};
|
|
72
|
+
const handleSubmit = (clean = false) => {
|
|
73
|
+
if (clean) {
|
|
74
|
+
onSelect(void 0);
|
|
75
|
+
} else {
|
|
76
|
+
const current = dayjs();
|
|
77
|
+
onSelect({
|
|
78
|
+
value: status,
|
|
79
|
+
...duration ? { duration } : {},
|
|
80
|
+
...duration === DurationEnum.Custom && customDate ? { dateRange: [current.toDate(), dayjs(customDate).toDate()] } : {}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
onClose();
|
|
84
|
+
};
|
|
85
|
+
return /* @__PURE__ */ jsxs(
|
|
86
|
+
Dialog,
|
|
87
|
+
{
|
|
88
|
+
open,
|
|
89
|
+
onClose,
|
|
90
|
+
maxWidth: "xs",
|
|
91
|
+
fullWidth: true,
|
|
92
|
+
PaperProps: {
|
|
93
|
+
sx: {
|
|
94
|
+
borderRadius: "8px"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
children: [
|
|
98
|
+
/* @__PURE__ */ jsxs(DialogTitle, { sx: { borderBottom: `1px solid ${colors.dividerColor}` }, children: [
|
|
99
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", sx: { fontSize: "16px !important", mb: 0 }, children: t("profile.setStatus") }),
|
|
100
|
+
/* @__PURE__ */ jsx(
|
|
101
|
+
IconButton,
|
|
102
|
+
{
|
|
103
|
+
"aria-label": "close",
|
|
104
|
+
onClick: onClose,
|
|
105
|
+
sx: {
|
|
106
|
+
position: "absolute",
|
|
107
|
+
right: 8,
|
|
108
|
+
top: 8,
|
|
109
|
+
color: (theme) => theme.palette.grey[500]
|
|
110
|
+
},
|
|
111
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
] }),
|
|
115
|
+
/* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
116
|
+
/* @__PURE__ */ jsxs(FormControl, { fullWidth: true, sx: { mt: 2 }, children: [
|
|
117
|
+
/* @__PURE__ */ jsx(InputLabel, { size: "small", sx: { fontSize: "14px" }, children: t("profile.setStatus") }),
|
|
118
|
+
/* @__PURE__ */ jsx(
|
|
119
|
+
Select,
|
|
120
|
+
{
|
|
121
|
+
value: status,
|
|
122
|
+
label: t("profile.setStatus"),
|
|
123
|
+
onChange: (e) => {
|
|
124
|
+
setStatus(e.target.value);
|
|
125
|
+
setChanged(true);
|
|
126
|
+
},
|
|
127
|
+
variant: "outlined",
|
|
128
|
+
IconComponent: (props) => /* @__PURE__ */ jsx(ArrowDownwardIcon, { ...props, width: 20, height: 20 }),
|
|
129
|
+
sx: selectStyle,
|
|
130
|
+
children: data.map((item) => /* @__PURE__ */ jsx(MenuItem, { value: item.id, children: /* @__PURE__ */ jsxs(Suspense, { fallback: null, children: [
|
|
131
|
+
item.icon && /* @__PURE__ */ jsx(ListItemIcon, { style: { minWidth: "24px" }, children: createElement(item.icon, {
|
|
132
|
+
style: { fontSize: "16px", width: "16px", height: "16px" }
|
|
133
|
+
}) }),
|
|
134
|
+
item.name
|
|
135
|
+
] }) }, item.id))
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
] }),
|
|
139
|
+
!status ? /* @__PURE__ */ jsxs(Box, { sx: { mt: "24px" }, children: [
|
|
140
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: t("profile.quickSettings") }),
|
|
141
|
+
data.map((item) => /* @__PURE__ */ jsx(
|
|
142
|
+
Box,
|
|
143
|
+
{
|
|
144
|
+
onClick: () => handleQuickSettingClick(item),
|
|
145
|
+
sx: {
|
|
146
|
+
display: "flex",
|
|
147
|
+
alignItems: "center",
|
|
148
|
+
gap: "4px",
|
|
149
|
+
p: "4px",
|
|
150
|
+
cursor: "pointer",
|
|
151
|
+
borderRadius: "8px",
|
|
152
|
+
"&:hover": {
|
|
153
|
+
bgcolor: "action.hover"
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
children: /* @__PURE__ */ jsxs(Suspense, { fallback: null, children: [
|
|
157
|
+
item.icon && /* @__PURE__ */ jsx(ListItemIcon, { style: { minWidth: "24px" }, children: createElement(item.icon, {
|
|
158
|
+
style: { fontSize: "16px", width: "16px", height: "16px" }
|
|
159
|
+
}) }),
|
|
160
|
+
item.name,
|
|
161
|
+
item.durationName && /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "text.secondary", children: [
|
|
162
|
+
"- ",
|
|
163
|
+
item.durationName
|
|
164
|
+
] })
|
|
165
|
+
] })
|
|
166
|
+
},
|
|
167
|
+
item.id
|
|
168
|
+
))
|
|
169
|
+
] }) : /* @__PURE__ */ jsxs(FormControl, { fullWidth: true, sx: { mt: "24px" }, children: [
|
|
170
|
+
/* @__PURE__ */ jsx(InputLabel, { size: "small", sx: { fontSize: "14px" }, children: t("profile.removeStatusAfter") }),
|
|
171
|
+
/* @__PURE__ */ jsx(
|
|
172
|
+
Select,
|
|
173
|
+
{
|
|
174
|
+
value: duration,
|
|
175
|
+
label: t("profile.removeStatusAfter"),
|
|
176
|
+
onChange: (e) => {
|
|
177
|
+
const { value } = e.target;
|
|
178
|
+
setDuration(value);
|
|
179
|
+
if (value === DurationEnum.Custom) {
|
|
180
|
+
setCustomDate(dayjs().toDate());
|
|
181
|
+
}
|
|
182
|
+
setChanged(true);
|
|
183
|
+
},
|
|
184
|
+
variant: "outlined",
|
|
185
|
+
IconComponent: (props) => /* @__PURE__ */ jsx(ArrowDownwardIcon, { ...props, width: 20, height: 20 }),
|
|
186
|
+
sx: selectStyle,
|
|
187
|
+
children: selectedStatus?.children?.map((item) => /* @__PURE__ */ jsx(MenuItem, { value: item.id, children: item.name }, item.id))
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
] }),
|
|
191
|
+
duration === DurationEnum.Custom && /* @__PURE__ */ jsx(Box, { sx: { mt: "24px" }, children: /* @__PURE__ */ jsx(
|
|
192
|
+
DateTimeInput,
|
|
193
|
+
{
|
|
194
|
+
value: customDate,
|
|
195
|
+
onChange: (e) => {
|
|
196
|
+
setCustomDate(e);
|
|
197
|
+
setChanged(true);
|
|
198
|
+
},
|
|
199
|
+
label: t("profile.selectEndTime"),
|
|
200
|
+
timezone,
|
|
201
|
+
error: duration === DurationEnum.Custom && !customDate,
|
|
202
|
+
helperText: duration === DurationEnum.Custom && !customDate ? t("profile.pleaseSelectTime") : void 0
|
|
203
|
+
}
|
|
204
|
+
) })
|
|
205
|
+
] }),
|
|
206
|
+
/* @__PURE__ */ jsx(DialogActions, { sx: { p: "16px 24px", borderTop: `1px solid ${colors.dividerColor}` }, children: selected?.value && !changed ? /* @__PURE__ */ jsx(
|
|
207
|
+
Button,
|
|
208
|
+
{
|
|
209
|
+
sx: { ...defaultButtonStyle, minWidth: "54px" },
|
|
210
|
+
size: "small",
|
|
211
|
+
variant: "outlined",
|
|
212
|
+
onClick: () => handleSubmit(true),
|
|
213
|
+
children: t("profile.cleanStatus")
|
|
214
|
+
}
|
|
215
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
216
|
+
/* @__PURE__ */ jsx(Button, { size: "small", variant: "outlined", sx: { ...defaultButtonStyle, minWidth: "54px" }, onClick: onClose, children: t("common.cancel") }),
|
|
217
|
+
/* @__PURE__ */ jsx(
|
|
218
|
+
Button,
|
|
219
|
+
{
|
|
220
|
+
sx: {
|
|
221
|
+
...primaryButtonStyle,
|
|
222
|
+
minWidth: "54px",
|
|
223
|
+
"&.Mui-disabled": {
|
|
224
|
+
backgroundColor: "rgba(0, 0, 0, 0.12)"
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
disabled: !isValid,
|
|
228
|
+
onClick: () => handleSubmit(false),
|
|
229
|
+
size: "small",
|
|
230
|
+
variant: "outlined",
|
|
231
|
+
children: t("common.confirm")
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
] }) })
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
}
|
|
@@ -52,6 +52,11 @@ const ContentWrapper = styled(Box)(({ theme }) => ({
|
|
|
52
52
|
flex: "revert",
|
|
53
53
|
[theme.breakpoints.up("md")]: {
|
|
54
54
|
flex: 1
|
|
55
|
+
},
|
|
56
|
+
"@media (min-width: 850px) and (max-width: 1050px)": {
|
|
57
|
+
"& .user-center-tabs": {
|
|
58
|
+
maxWidth: "500px"
|
|
59
|
+
}
|
|
55
60
|
}
|
|
56
61
|
}));
|
|
57
62
|
export default function UserCenter({
|
|
@@ -193,8 +198,8 @@ export default function UserCenter({
|
|
|
193
198
|
label: x.title || x.label,
|
|
194
199
|
url: x.link || x.url,
|
|
195
200
|
protected: privacyState?.data?.[value] ?? false,
|
|
196
|
-
isPrivate: x.isPrivate || x.
|
|
197
|
-
// FIXME: HACK: 隐藏
|
|
201
|
+
isPrivate: x.isPrivate || x.private || x._rawLink.includes("/customer")
|
|
202
|
+
// FIXME: HACK: 隐藏 /customer 菜单, 需要一个通用的解决方案,在嵌入的时候就决定是否是私有的
|
|
198
203
|
// icon: x.icon,
|
|
199
204
|
};
|
|
200
205
|
}).filter((x) => isMyself || !x.isPrivate);
|
|
@@ -382,7 +387,7 @@ export default function UserCenter({
|
|
|
382
387
|
] });
|
|
383
388
|
}
|
|
384
389
|
return /* @__PURE__ */ jsxs(ContentWrapper, { display: "flex", flexDirection: isMobile ? "column" : "row", children: [
|
|
385
|
-
/* @__PURE__ */ jsxs(Box, { flex: "1", order: isMobile ? 2 : "unset", children: [
|
|
390
|
+
/* @__PURE__ */ jsxs(Box, { flex: "1", className: "user-center-tabs", order: isMobile ? 2 : "unset", children: [
|
|
386
391
|
userCenterTabs.length > 0 && currentTab ? /* @__PURE__ */ jsxs(
|
|
387
392
|
Box,
|
|
388
393
|
{
|
|
@@ -5,10 +5,11 @@ import utc from "dayjs/plugin/utc";
|
|
|
5
5
|
import timezonePlugin from "dayjs/plugin/timezone";
|
|
6
6
|
import { formatToDatetime } from "@arcblock/ux/lib/Util";
|
|
7
7
|
import { useCreation } from "ahooks";
|
|
8
|
+
import { currentTimezone } from "./utils.js";
|
|
8
9
|
dayjs.extend(utc);
|
|
9
10
|
dayjs.extend(timezonePlugin);
|
|
10
|
-
export default function Clock({ timezone =
|
|
11
|
-
const [time, setTime] = useState(dayjs().tz(timezone));
|
|
11
|
+
export default function Clock({ timezone = currentTimezone, locale = "zh" }) {
|
|
12
|
+
const [time, setTime] = useState(dayjs().tz(timezone || currentTimezone));
|
|
12
13
|
useEffect(() => {
|
|
13
14
|
const timerId = setInterval(() => {
|
|
14
15
|
setTime(dayjs().tz(timezone));
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { createElement } from "react";
|
|
3
3
|
import Box from "@mui/material/Box";
|
|
4
|
-
import MenuItem from "@mui/material/MenuItem";
|
|
5
|
-
import Select from "@mui/material/Select";
|
|
6
4
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
7
5
|
import SwipeableDrawer from "@mui/material/SwipeableDrawer";
|
|
8
6
|
import Backdrop from "@mui/material/Backdrop";
|
|
@@ -10,47 +8,23 @@ import styled from "@emotion/styled";
|
|
|
10
8
|
import { joinURL } from "ufo";
|
|
11
9
|
import Button from "@arcblock/ux/lib/Button";
|
|
12
10
|
import cloneDeep from "lodash/cloneDeep";
|
|
13
|
-
import { temp as colors } from "@arcblock/ux/lib/Colors";
|
|
14
11
|
import { useCreation, useMemoizedFn, useReactive } from "ahooks";
|
|
15
12
|
import { useMemo, useRef, useState, memo, forwardRef, useEffect, lazy } from "react";
|
|
16
13
|
import { translate } from "@arcblock/ux/lib/Locale/util";
|
|
17
14
|
import isEmail from "validator/lib/isEmail";
|
|
18
15
|
import isMobilePhone from "validator/lib/isMobilePhone";
|
|
19
16
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
20
|
-
import ArrowDownwardIcon from "@arcblock/icons/lib/ArrowDown";
|
|
21
17
|
import { useBrowser } from "@arcblock/react-hooks";
|
|
22
18
|
import { translations } from "../../libs/locales.js";
|
|
23
19
|
import EditableField from "../editable-field.js";
|
|
24
20
|
import { LinkPreviewInput } from "./link-preview-input.js";
|
|
25
|
-
import {
|
|
21
|
+
import { currentTimezone, defaultButtonStyle, primaryButtonStyle } from "./utils.js";
|
|
26
22
|
import Clock from "./clock.js";
|
|
27
|
-
|
|
23
|
+
import { TimezoneSelect } from "./timezone-select.js";
|
|
28
24
|
const LocationIcon = lazy(() => import("@arcblock/icons/lib/Location"));
|
|
29
25
|
const TimezoneIcon = lazy(() => import("@arcblock/icons/lib/Timezone"));
|
|
30
26
|
const EmailIcon = lazy(() => import("@arcblock/icons/lib/Email"));
|
|
31
27
|
const PhoneIcon = lazy(() => import("@arcblock/icons/lib/Phone"));
|
|
32
|
-
const defaultButtonStyle = {
|
|
33
|
-
color: colors.foregroundsFgBase,
|
|
34
|
-
borderColor: colors.strokeBorderBase,
|
|
35
|
-
backgroundColor: colors.buttonsButtonNeutral,
|
|
36
|
-
"&:hover": {
|
|
37
|
-
borderColor: colors.strokeBorderBase,
|
|
38
|
-
backgroundColor: colors.buttonsButtonNeutralHover
|
|
39
|
-
},
|
|
40
|
-
py: 0.5,
|
|
41
|
-
borderRadius: 2
|
|
42
|
-
};
|
|
43
|
-
const primaryButtonStyle = {
|
|
44
|
-
color: colors.buttonsButtonNeutral,
|
|
45
|
-
borderColor: colors.foregroundsFgInteractive,
|
|
46
|
-
backgroundColor: colors.foregroundsFgInteractive,
|
|
47
|
-
"&:hover": {
|
|
48
|
-
borderColor: colors.foregroundsFgInteractive,
|
|
49
|
-
backgroundColor: colors.foregroundsFgInteractive
|
|
50
|
-
},
|
|
51
|
-
py: 0.5,
|
|
52
|
-
borderRadius: 2
|
|
53
|
-
};
|
|
54
28
|
const iconSize = {
|
|
55
29
|
width: 20,
|
|
56
30
|
height: 20
|
|
@@ -172,6 +146,9 @@ export default function UserMetadataComponent({
|
|
|
172
146
|
if (k === "bio") {
|
|
173
147
|
metadata[k] = metadata[k]?.slice(0, bioMaxLength);
|
|
174
148
|
}
|
|
149
|
+
if (k === "timezone") {
|
|
150
|
+
metadata[k] = value || currentTimezone;
|
|
151
|
+
}
|
|
175
152
|
});
|
|
176
153
|
onSave(metadata);
|
|
177
154
|
setEditable(false);
|
|
@@ -189,6 +166,7 @@ export default function UserMetadataComponent({
|
|
|
189
166
|
component: "textarea",
|
|
190
167
|
inline: false,
|
|
191
168
|
rows: 3,
|
|
169
|
+
label: t("profile.bio"),
|
|
192
170
|
maxLength: bioMaxLength,
|
|
193
171
|
style: {
|
|
194
172
|
...editing ? { marginBottom: 8 } : {}
|
|
@@ -225,7 +203,7 @@ export default function UserMetadataComponent({
|
|
|
225
203
|
/* @__PURE__ */ jsx(
|
|
226
204
|
EditableField,
|
|
227
205
|
{
|
|
228
|
-
value: metadata.timezone
|
|
206
|
+
value: metadata.timezone || currentTimezone,
|
|
229
207
|
onChange: (value) => onChange(value, "timezone"),
|
|
230
208
|
editable: editing,
|
|
231
209
|
placeholder: "timezone",
|
|
@@ -236,38 +214,11 @@ export default function UserMetadataComponent({
|
|
|
236
214
|
/* @__PURE__ */ jsx(Clock, { timezone: metadata.timezone, locale })
|
|
237
215
|
] }),
|
|
238
216
|
children: /* @__PURE__ */ jsx(
|
|
239
|
-
|
|
217
|
+
TimezoneSelect,
|
|
240
218
|
{
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
disabled: !editing,
|
|
245
|
-
displayEmpty: true,
|
|
246
|
-
variant: "outlined",
|
|
247
|
-
placeholder: "Timezone",
|
|
248
|
-
IconComponent: (props) => /* @__PURE__ */ jsx(ArrowDownwardIcon, { ...props, width: 20, height: 20 }),
|
|
249
|
-
MenuProps: {
|
|
250
|
-
PaperProps: {
|
|
251
|
-
style: {
|
|
252
|
-
maxHeight: "400px"
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
style: {
|
|
256
|
-
zIndex: mode === "drawer" ? 9999 : 1300
|
|
257
|
-
}
|
|
258
|
-
},
|
|
259
|
-
sx: {
|
|
260
|
-
width: "100%",
|
|
261
|
-
"&:hover": {
|
|
262
|
-
"fieldset.MuiOutlinedInput-notchedOutline": {
|
|
263
|
-
borderColor: colors.dividerColor
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
fieldset: {
|
|
267
|
-
borderColor: colors.dividerColor
|
|
268
|
-
}
|
|
269
|
-
},
|
|
270
|
-
children: timezones.map((tz) => /* @__PURE__ */ jsx(MenuItem, { value: tz.value, children: tz.label }, tz.value))
|
|
219
|
+
value: metadata.timezone || currentTimezone,
|
|
220
|
+
onChange: (value) => onChange(value, "timezone"),
|
|
221
|
+
disabled: !editing
|
|
271
222
|
}
|
|
272
223
|
)
|
|
273
224
|
}
|
|
@@ -276,7 +227,8 @@ export default function UserMetadataComponent({
|
|
|
276
227
|
EditableField,
|
|
277
228
|
{
|
|
278
229
|
value: metadata.email ?? user?.email ?? "",
|
|
279
|
-
editable: editing
|
|
230
|
+
editable: editing,
|
|
231
|
+
canEdit: !emailVerified,
|
|
280
232
|
verified: emailVerified,
|
|
281
233
|
placeholder: "Email",
|
|
282
234
|
icon: /* @__PURE__ */ jsx(EmailIcon, { ...iconSize }),
|
|
@@ -296,7 +248,8 @@ export default function UserMetadataComponent({
|
|
|
296
248
|
EditableField,
|
|
297
249
|
{
|
|
298
250
|
value: metadata.phone ?? user?.phone ?? "",
|
|
299
|
-
editable: editing
|
|
251
|
+
editable: editing,
|
|
252
|
+
canEdit: !phoneVerified,
|
|
300
253
|
verified: phoneVerified,
|
|
301
254
|
placeholder: "Phone",
|
|
302
255
|
icon: /* @__PURE__ */ jsx(PhoneIcon, { ...iconSize }),
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface TimezoneSelectProps {
|
|
2
|
+
value: string;
|
|
3
|
+
onChange: (value: string) => void;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
mode?: 'drawer' | 'self';
|
|
6
|
+
}
|
|
7
|
+
export declare function TimezoneSelect({ value, onChange, disabled, mode }: TimezoneSelectProps): import("react").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useMemo } from "react";
|
|
3
|
+
import MenuItem from "@mui/material/MenuItem";
|
|
4
|
+
import Select from "@mui/material/Select";
|
|
5
|
+
import ListSubheader from "@mui/material/ListSubheader";
|
|
6
|
+
import TextField from "@mui/material/TextField";
|
|
7
|
+
import debounce from "lodash/debounce";
|
|
8
|
+
import { temp as colors } from "@arcblock/ux/lib/Colors";
|
|
9
|
+
import ArrowDownwardIcon from "@arcblock/icons/lib/ArrowDown";
|
|
10
|
+
import { getTimezones } from "./utils.js";
|
|
11
|
+
const timezones = getTimezones();
|
|
12
|
+
export function TimezoneSelect({ value, onChange, disabled = false, mode = "self" }) {
|
|
13
|
+
const [timezoneData, setTimezoneData] = useState(timezones);
|
|
14
|
+
const [searchText, setSearchText] = useState("");
|
|
15
|
+
const timezoneDebounce = useMemo(
|
|
16
|
+
() => debounce((v) => {
|
|
17
|
+
return v ? timezones.filter((tz) => tz.value.toLowerCase().includes(v.toLowerCase())) : timezones;
|
|
18
|
+
}, 300),
|
|
19
|
+
[]
|
|
20
|
+
);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const data = timezoneDebounce(searchText);
|
|
23
|
+
setTimezoneData(data ?? timezones);
|
|
24
|
+
}, [searchText, timezoneDebounce]);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
return () => {
|
|
27
|
+
timezoneDebounce.cancel();
|
|
28
|
+
};
|
|
29
|
+
}, [timezoneDebounce]);
|
|
30
|
+
return /* @__PURE__ */ jsxs(
|
|
31
|
+
Select,
|
|
32
|
+
{
|
|
33
|
+
className: `timezone-select ${disabled ? "disabled" : ""}`,
|
|
34
|
+
value,
|
|
35
|
+
onChange: (e) => onChange(e.target.value),
|
|
36
|
+
disabled,
|
|
37
|
+
displayEmpty: true,
|
|
38
|
+
variant: "outlined",
|
|
39
|
+
placeholder: "Timezone",
|
|
40
|
+
IconComponent: (props) => /* @__PURE__ */ jsx(ArrowDownwardIcon, { ...props, width: 20, height: 20 }),
|
|
41
|
+
MenuProps: {
|
|
42
|
+
PaperProps: {
|
|
43
|
+
style: {
|
|
44
|
+
maxHeight: "400px"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
style: {
|
|
48
|
+
zIndex: mode === "drawer" ? 9999 : 1300
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
sx: {
|
|
52
|
+
width: "100%",
|
|
53
|
+
"&:hover": {
|
|
54
|
+
"fieldset.MuiOutlinedInput-notchedOutline": {
|
|
55
|
+
borderColor: colors.dividerColor
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"fieldset.MuiOutlinedInput-notchedOutline": {
|
|
59
|
+
borderColor: colors.dividerColor
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
children: [
|
|
63
|
+
/* @__PURE__ */ jsx(ListSubheader, { children: /* @__PURE__ */ jsx(
|
|
64
|
+
TextField,
|
|
65
|
+
{
|
|
66
|
+
autoFocus: true,
|
|
67
|
+
value: searchText,
|
|
68
|
+
placeholder: "Timezone",
|
|
69
|
+
variant: "outlined",
|
|
70
|
+
size: "small",
|
|
71
|
+
fullWidth: true,
|
|
72
|
+
onChange: (event) => setSearchText(event.target.value),
|
|
73
|
+
onKeyDown: (e) => {
|
|
74
|
+
if (e.key !== "Escape") {
|
|
75
|
+
e.stopPropagation();
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
sx: {
|
|
79
|
+
marginTop: "8px",
|
|
80
|
+
"& .MuiOutlinedInput-root": {
|
|
81
|
+
"& fieldset": {
|
|
82
|
+
borderColor: colors.dividerColor,
|
|
83
|
+
borderWidth: "1px"
|
|
84
|
+
},
|
|
85
|
+
"&:hover fieldset": {
|
|
86
|
+
borderColor: colors.dividerColor
|
|
87
|
+
},
|
|
88
|
+
"&.Mui-focused fieldset": {
|
|
89
|
+
borderColor: colors.dividerColor
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
) }),
|
|
95
|
+
timezoneData.map((tz) => /* @__PURE__ */ jsx(MenuItem, { value: tz.value, children: tz.label }, tz.value))
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
}
|