@blocklet/ui-react 2.12.15 → 2.12.17
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 +5 -1
- package/lib/UserCenter/components/editable-field.d.ts +2 -1
- package/lib/UserCenter/components/editable-field.js +3 -2
- package/lib/UserCenter/components/user-center.d.ts +2 -1
- package/lib/UserCenter/components/user-center.js +25 -3
- package/lib/UserCenter/components/user-info/metadata.d.ts +2 -1
- package/lib/UserCenter/components/user-info/metadata.js +240 -152
- package/lib/UserCenter/components/user-info/timezone-select.js +2 -5
- package/lib/UserCenter/components/user-info/user-basic-info.d.ts +2 -1
- package/lib/UserCenter/components/user-info/user-basic-info.js +42 -5
- package/package.json +5 -5
- package/src/@types/index.ts +6 -1
- package/src/UserCenter/components/editable-field.tsx +3 -1
- package/src/UserCenter/components/user-center.tsx +26 -2
- package/src/UserCenter/components/user-info/metadata.tsx +106 -22
- package/src/UserCenter/components/user-info/timezone-select.tsx +2 -5
- package/src/UserCenter/components/user-info/user-basic-info.tsx +45 -7
package/lib/@types/index.d.ts
CHANGED
|
@@ -57,6 +57,10 @@ export declare enum StatusEnum {
|
|
|
57
57
|
OffSick = "off_sick",
|
|
58
58
|
WorkingRemotely = "working_remotely"
|
|
59
59
|
}
|
|
60
|
+
export type UserPhoneProps = {
|
|
61
|
+
country: string;
|
|
62
|
+
phoneNumber: string;
|
|
63
|
+
};
|
|
60
64
|
export type UserMetadata = {
|
|
61
65
|
bio?: string;
|
|
62
66
|
location?: string;
|
|
@@ -70,7 +74,7 @@ export type UserMetadata = {
|
|
|
70
74
|
links?: UserMetadataLink[];
|
|
71
75
|
cover?: string;
|
|
72
76
|
email?: string;
|
|
73
|
-
phone?:
|
|
77
|
+
phone?: UserPhoneProps;
|
|
74
78
|
};
|
|
75
79
|
export type User = UserPublicInfo & {
|
|
76
80
|
role: string;
|
|
@@ -18,6 +18,7 @@ interface EditableFieldProps {
|
|
|
18
18
|
verified?: boolean;
|
|
19
19
|
errorMsg?: string;
|
|
20
20
|
canEdit?: boolean;
|
|
21
|
+
renderValue?: (value: string) => React.ReactNode;
|
|
21
22
|
}
|
|
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;
|
|
23
|
+
declare function EditableField({ value, onChange, onValueValidate, errorMsg, editable, component, placeholder, rows, maxLength, icon, label, children, tooltip, inline, style, verified, canEdit, renderValue, }: EditableFieldProps): JSX.Element | null;
|
|
23
24
|
export default EditableField;
|
|
@@ -43,7 +43,8 @@ function EditableField({
|
|
|
43
43
|
inline = true,
|
|
44
44
|
style = {},
|
|
45
45
|
verified = false,
|
|
46
|
-
canEdit = true
|
|
46
|
+
canEdit = true,
|
|
47
|
+
renderValue
|
|
47
48
|
}) {
|
|
48
49
|
const { locale } = useLocaleContext();
|
|
49
50
|
const t = useMemoizedFn((key, data = {}) => {
|
|
@@ -146,7 +147,7 @@ function EditableField({
|
|
|
146
147
|
whiteSpace: "pre-wrap",
|
|
147
148
|
...inline ? { whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } : {}
|
|
148
149
|
},
|
|
149
|
-
children: value
|
|
150
|
+
children: renderValue ? renderValue(value) : value
|
|
150
151
|
}
|
|
151
152
|
),
|
|
152
153
|
verified && /* @__PURE__ */ jsx(VerifiedIcon, { color: "success", style: { fontSize: 16, width: 16, marginLeft: 4, flexShrink: 0 } })
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BoxProps } from '@mui/material';
|
|
2
|
-
export default function UserCenter({ children, notLoginContent, currentTab, contentProps, disableAutoRedirect, hideFooter, headerProps, footerProps, userDid, stickySidebar, embed, }: {
|
|
2
|
+
export default function UserCenter({ children, notLoginContent, currentTab, contentProps, disableAutoRedirect, hideFooter, headerProps, footerProps, userDid, stickySidebar, embed, onlyProfile, }: {
|
|
3
3
|
readonly children: any;
|
|
4
4
|
readonly notLoginContent: any;
|
|
5
5
|
readonly currentTab: string;
|
|
@@ -12,4 +12,5 @@ export default function UserCenter({ children, notLoginContent, currentTab, cont
|
|
|
12
12
|
readonly userDid?: string;
|
|
13
13
|
readonly stickySidebar?: boolean;
|
|
14
14
|
readonly embed?: boolean;
|
|
15
|
+
readonly onlyProfile?: boolean;
|
|
15
16
|
}): import("react").JSX.Element | null;
|
|
@@ -70,7 +70,9 @@ export default function UserCenter({
|
|
|
70
70
|
footerProps = {},
|
|
71
71
|
userDid = void 0,
|
|
72
72
|
stickySidebar = false,
|
|
73
|
-
embed = false
|
|
73
|
+
embed = false,
|
|
74
|
+
onlyProfile = false
|
|
75
|
+
// 只显示 profile 页面,用于 ArcSphere 只需要显示 Profile 的内容
|
|
74
76
|
}) {
|
|
75
77
|
const { locale } = useLocaleContext();
|
|
76
78
|
const isMobile = useMobile({ key: "md" });
|
|
@@ -386,6 +388,26 @@ export default function UserCenter({
|
|
|
386
388
|
userCenterTabs.length === 0 && emptyContent
|
|
387
389
|
] });
|
|
388
390
|
}
|
|
391
|
+
if (onlyProfile) {
|
|
392
|
+
return /* @__PURE__ */ jsx(ContentWrapper, { display: "flex", flexDirection: isMobile ? "column" : "row", children: /* @__PURE__ */ jsx(
|
|
393
|
+
UserBasicInfo,
|
|
394
|
+
{
|
|
395
|
+
isMobile,
|
|
396
|
+
order: isMobile ? 1 : "unset",
|
|
397
|
+
isMyself,
|
|
398
|
+
switchPassport: handleSwitchPassport,
|
|
399
|
+
switchProfile: session.switchProfile,
|
|
400
|
+
user: userState.data,
|
|
401
|
+
showFullDid: false,
|
|
402
|
+
onlyProfile,
|
|
403
|
+
sx: {
|
|
404
|
+
padding: !isMobile ? "40px 24px 24px 40px" : "16px 0 0 0",
|
|
405
|
+
...!isMobile ? { width: 320, maxWidth: 320, flexShrink: 0 } : {},
|
|
406
|
+
boxSizing: "content-box"
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
) });
|
|
410
|
+
}
|
|
389
411
|
return /* @__PURE__ */ jsxs(ContentWrapper, { display: "flex", flexDirection: isMobile ? "column" : "row", children: [
|
|
390
412
|
/* @__PURE__ */ jsxs(Box, { flex: "1", className: "user-center-tabs", order: isMobile ? 2 : "unset", children: [
|
|
391
413
|
userCenterTabs.length > 0 && currentTab ? /* @__PURE__ */ jsxs(
|
|
@@ -412,7 +434,7 @@ export default function UserCenter({
|
|
|
412
434
|
".MuiTabs-flexContainer": {
|
|
413
435
|
gap: 3,
|
|
414
436
|
".MuiButtonBase-root": {
|
|
415
|
-
padding: "40px 4px 32px 4px",
|
|
437
|
+
padding: isMobile ? "16px 4px" : "40px 4px 32px 4px",
|
|
416
438
|
fontSize: 16
|
|
417
439
|
},
|
|
418
440
|
".MuiTab-root": {
|
|
@@ -453,7 +475,7 @@ export default function UserCenter({
|
|
|
453
475
|
user: userState.data,
|
|
454
476
|
showFullDid: false,
|
|
455
477
|
sx: {
|
|
456
|
-
padding: !isMobile ? "40px 24px 24px 40px" : "
|
|
478
|
+
padding: !isMobile ? "40px 24px 24px 40px" : "16px 0 0 0",
|
|
457
479
|
...!isMobile ? { width: 320, maxWidth: 320, flexShrink: 0 } : {},
|
|
458
480
|
boxSizing: "content-box"
|
|
459
481
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { User, UserMetadata } from '../../../@types';
|
|
2
|
-
export default function UserMetadataComponent({ isMyself, user, onSave, }: {
|
|
2
|
+
export default function UserMetadataComponent({ isMyself, user, onSave, isMobile, }: {
|
|
3
|
+
isMobile: boolean;
|
|
3
4
|
isMyself: boolean;
|
|
4
5
|
user: User;
|
|
5
6
|
onSave: (v: UserMetadata) => void;
|
|
@@ -7,12 +7,13 @@ import Backdrop from "@mui/material/Backdrop";
|
|
|
7
7
|
import styled from "@emotion/styled";
|
|
8
8
|
import { joinURL } from "ufo";
|
|
9
9
|
import Button from "@arcblock/ux/lib/Button";
|
|
10
|
+
import PhoneInput, { validatePhoneNumber } from "@arcblock/ux/lib/PhoneInput";
|
|
10
11
|
import cloneDeep from "lodash/cloneDeep";
|
|
11
12
|
import { useCreation, useMemoizedFn, useReactive } from "ahooks";
|
|
12
13
|
import { useMemo, useRef, useState, memo, forwardRef, useEffect, lazy } from "react";
|
|
13
14
|
import { translate } from "@arcblock/ux/lib/Locale/util";
|
|
14
15
|
import isEmail from "validator/lib/isEmail";
|
|
15
|
-
import
|
|
16
|
+
import { temp as colors } from "@arcblock/ux/lib/Colors";
|
|
16
17
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
17
18
|
import { useBrowser } from "@arcblock/react-hooks";
|
|
18
19
|
import { translations } from "../../libs/locales.js";
|
|
@@ -52,7 +53,8 @@ BackdropWrap.displayName = "BackdropWrap";
|
|
|
52
53
|
export default function UserMetadataComponent({
|
|
53
54
|
isMyself,
|
|
54
55
|
user,
|
|
55
|
-
onSave
|
|
56
|
+
onSave,
|
|
57
|
+
isMobile
|
|
56
58
|
}) {
|
|
57
59
|
const [editable, setEditable] = useState(false);
|
|
58
60
|
const [visible, setVisible] = useState(false);
|
|
@@ -82,9 +84,34 @@ export default function UserMetadataComponent({
|
|
|
82
84
|
user?.metadata ? cloneDeep(user.metadata) : {
|
|
83
85
|
joinedAt: user?.createdAt,
|
|
84
86
|
email: user?.email,
|
|
85
|
-
phone:
|
|
87
|
+
phone: {
|
|
88
|
+
country: "cn",
|
|
89
|
+
phoneNumber: user?.phone ?? ""
|
|
90
|
+
}
|
|
86
91
|
}
|
|
87
92
|
);
|
|
93
|
+
const phoneValue = useCreation(() => {
|
|
94
|
+
const phone = metadata.phone ?? user?.phone ?? {
|
|
95
|
+
country: "cn",
|
|
96
|
+
phone: ""
|
|
97
|
+
};
|
|
98
|
+
if (typeof phone === "string") {
|
|
99
|
+
return {
|
|
100
|
+
country: "cn",
|
|
101
|
+
phone
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (typeof phone === "object") {
|
|
105
|
+
return {
|
|
106
|
+
country: phone.country,
|
|
107
|
+
phone: phone.phoneNumber
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
country: "cn",
|
|
112
|
+
phone: ""
|
|
113
|
+
};
|
|
114
|
+
}, [metadata.phone, user?.phone]);
|
|
88
115
|
const onChange = (v, field) => {
|
|
89
116
|
metadata[field] = v;
|
|
90
117
|
};
|
|
@@ -155,159 +182,223 @@ export default function UserMetadataComponent({
|
|
|
155
182
|
setVisible(false);
|
|
156
183
|
};
|
|
157
184
|
const renderEdit = (editing, mode = "self") => {
|
|
158
|
-
return /* @__PURE__ */ jsxs(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
/* @__PURE__ */ jsx(Clock, { timezone: metadata.timezone, locale })
|
|
215
|
-
] }),
|
|
216
|
-
children: /* @__PURE__ */ jsx(
|
|
217
|
-
TimezoneSelect,
|
|
185
|
+
return /* @__PURE__ */ jsxs(
|
|
186
|
+
MetadataInfo,
|
|
187
|
+
{
|
|
188
|
+
pt: 2,
|
|
189
|
+
display: "flex",
|
|
190
|
+
flexDirection: "column",
|
|
191
|
+
justifyContent: "space-between",
|
|
192
|
+
alignItems: "flex-start",
|
|
193
|
+
gap: !isMobile ? "16px" : "4px",
|
|
194
|
+
children: [
|
|
195
|
+
/* @__PURE__ */ jsx(
|
|
196
|
+
EditableField,
|
|
197
|
+
{
|
|
198
|
+
value: metadata.bio ?? "",
|
|
199
|
+
onChange: (value) => onChange(value, "bio"),
|
|
200
|
+
editable: editing,
|
|
201
|
+
placeholder: "Bio",
|
|
202
|
+
component: "textarea",
|
|
203
|
+
inline: false,
|
|
204
|
+
rows: 3,
|
|
205
|
+
label: t("profile.bio"),
|
|
206
|
+
maxLength: bioMaxLength,
|
|
207
|
+
style: {
|
|
208
|
+
...editing ? { marginBottom: 8 } : {}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
),
|
|
212
|
+
!editing && isMyself ? /* @__PURE__ */ jsx(
|
|
213
|
+
Button,
|
|
214
|
+
{
|
|
215
|
+
size: isMobile ? "small" : "large",
|
|
216
|
+
variant: "outlined",
|
|
217
|
+
sx: {
|
|
218
|
+
...defaultButtonStyle,
|
|
219
|
+
mb: !isMobile ? 2 : "4px",
|
|
220
|
+
mt: !isMobile ? 2 : "4px",
|
|
221
|
+
height: !isMobile ? "40px" : "32px"
|
|
222
|
+
},
|
|
223
|
+
onClick: onEdit,
|
|
224
|
+
fullWidth: true,
|
|
225
|
+
children: t("profile.editProfile")
|
|
226
|
+
}
|
|
227
|
+
) : null,
|
|
228
|
+
/* @__PURE__ */ jsx(
|
|
229
|
+
EditableField,
|
|
230
|
+
{
|
|
231
|
+
value: metadata.location ?? "",
|
|
232
|
+
onChange: (value) => onChange(value, "location"),
|
|
233
|
+
editable: editing,
|
|
234
|
+
placeholder: "Location",
|
|
235
|
+
label: t("profile.location"),
|
|
236
|
+
icon: /* @__PURE__ */ jsx(LocationIcon, { ...iconSize })
|
|
237
|
+
}
|
|
238
|
+
),
|
|
239
|
+
/* @__PURE__ */ jsx(
|
|
240
|
+
EditableField,
|
|
218
241
|
{
|
|
219
242
|
value: metadata.timezone || currentTimezone,
|
|
220
243
|
onChange: (value) => onChange(value, "timezone"),
|
|
221
|
-
|
|
244
|
+
editable: editing,
|
|
245
|
+
placeholder: "timezone",
|
|
246
|
+
icon: /* @__PURE__ */ jsx(TimezoneIcon, { ...iconSize }),
|
|
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
|
+
] }),
|
|
252
|
+
children: /* @__PURE__ */ jsx(
|
|
253
|
+
TimezoneSelect,
|
|
254
|
+
{
|
|
255
|
+
value: metadata.timezone || currentTimezone,
|
|
256
|
+
onChange: (value) => onChange(value, "timezone"),
|
|
257
|
+
disabled: !editing
|
|
258
|
+
}
|
|
259
|
+
)
|
|
222
260
|
}
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
261
|
+
),
|
|
262
|
+
/* @__PURE__ */ jsx(
|
|
263
|
+
EditableField,
|
|
264
|
+
{
|
|
265
|
+
value: metadata.email ?? user?.email ?? "",
|
|
266
|
+
editable: editing,
|
|
267
|
+
canEdit: !emailVerified,
|
|
268
|
+
verified: emailVerified,
|
|
269
|
+
placeholder: "Email",
|
|
270
|
+
icon: /* @__PURE__ */ jsx(EmailIcon, { ...iconSize }),
|
|
271
|
+
label: t("profile.email"),
|
|
272
|
+
onChange: (value) => onChange(value, "email"),
|
|
273
|
+
errorMsg: validateMsg.email,
|
|
274
|
+
renderValue: (value) => /* @__PURE__ */ jsx(
|
|
275
|
+
"a",
|
|
276
|
+
{
|
|
277
|
+
href: `mailto:${value}`,
|
|
278
|
+
style: {
|
|
279
|
+
color: "inherit",
|
|
280
|
+
textDecoration: "none"
|
|
281
|
+
},
|
|
282
|
+
children: value
|
|
283
|
+
}
|
|
284
|
+
),
|
|
285
|
+
onValueValidate: (value) => {
|
|
286
|
+
let msg = "";
|
|
287
|
+
if (!!value && !isEmail(value)) {
|
|
288
|
+
msg = t("profile.emailInvalid");
|
|
289
|
+
}
|
|
290
|
+
validateMsg.email = msg;
|
|
291
|
+
}
|
|
242
292
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
293
|
+
),
|
|
294
|
+
/* @__PURE__ */ jsx(
|
|
295
|
+
EditableField,
|
|
296
|
+
{
|
|
297
|
+
value: phoneValue.phone,
|
|
298
|
+
editable: editing,
|
|
299
|
+
canEdit: !phoneVerified,
|
|
300
|
+
verified: phoneVerified,
|
|
301
|
+
placeholder: "Phone",
|
|
302
|
+
icon: /* @__PURE__ */ jsx(PhoneIcon, { ...iconSize }),
|
|
303
|
+
onChange: (value) => onChange(value, "phone"),
|
|
304
|
+
label: t("profile.phone"),
|
|
305
|
+
renderValue: () => {
|
|
306
|
+
return /* @__PURE__ */ jsx(PhoneInput, { value: phoneValue, preview: true });
|
|
307
|
+
},
|
|
308
|
+
children: /* @__PURE__ */ jsx(
|
|
309
|
+
PhoneInput,
|
|
310
|
+
{
|
|
311
|
+
variant: "outlined",
|
|
312
|
+
className: "editable-field",
|
|
313
|
+
InputProps: {
|
|
314
|
+
sx: { backgroundColor: "transparent" },
|
|
315
|
+
placeholder: "Phone"
|
|
316
|
+
},
|
|
317
|
+
value: phoneValue,
|
|
318
|
+
error: !!validateMsg.phone,
|
|
319
|
+
helperText: validateMsg.phone,
|
|
320
|
+
sx: {
|
|
321
|
+
width: "100%",
|
|
322
|
+
".MuiOutlinedInput-root": {
|
|
323
|
+
"&:hover": {
|
|
324
|
+
fieldset: {
|
|
325
|
+
borderColor: colors.dividerColor
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
"&.Mui-focused": {
|
|
329
|
+
fieldset: {
|
|
330
|
+
borderColor: colors.dividerColor
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
fieldset: {
|
|
335
|
+
borderColor: colors.dividerColor
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
onChange: (value) => {
|
|
339
|
+
const isValid = validatePhoneNumber(value.phone);
|
|
340
|
+
if (!isValid) {
|
|
341
|
+
validateMsg.phone = t("profile.phoneInvalid");
|
|
342
|
+
} else {
|
|
343
|
+
validateMsg.phone = "";
|
|
344
|
+
}
|
|
345
|
+
onChange(
|
|
346
|
+
{
|
|
347
|
+
country: value.country,
|
|
348
|
+
phoneNumber: value.phone
|
|
349
|
+
},
|
|
350
|
+
"phone"
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
)
|
|
263
355
|
}
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
sx: { ...defaultButtonStyle, minWidth: "54px" },
|
|
285
|
-
onClick: onCancel,
|
|
286
|
-
children: t("common.cancel")
|
|
287
|
-
}
|
|
288
|
-
),
|
|
289
|
-
/* @__PURE__ */ jsx(
|
|
290
|
-
Button,
|
|
291
|
-
{
|
|
292
|
-
fullWidth: mode === "drawer",
|
|
293
|
-
size: "small",
|
|
294
|
-
disabled: !!validateMsg.email || !!validateMsg.phone,
|
|
295
|
-
variant: "outlined",
|
|
296
|
-
sx: {
|
|
297
|
-
...primaryButtonStyle,
|
|
298
|
-
minWidth: "54px",
|
|
299
|
-
"&.Mui-disabled": {
|
|
300
|
-
backgroundColor: "rgba(0, 0, 0, 0.12)"
|
|
356
|
+
),
|
|
357
|
+
/* @__PURE__ */ jsx(LinkPreviewInput, { editable: editing, links, onChange: handleLinksChange }),
|
|
358
|
+
editing && isMyself ? /* @__PURE__ */ jsxs(
|
|
359
|
+
Box,
|
|
360
|
+
{
|
|
361
|
+
display: "flex",
|
|
362
|
+
gap: 1,
|
|
363
|
+
style: { width: "100%" },
|
|
364
|
+
justifyContent: "flex-end",
|
|
365
|
+
flexDirection: mode === "drawer" ? "column" : "row",
|
|
366
|
+
children: [
|
|
367
|
+
/* @__PURE__ */ jsx(
|
|
368
|
+
Button,
|
|
369
|
+
{
|
|
370
|
+
fullWidth: mode === "drawer",
|
|
371
|
+
size: "small",
|
|
372
|
+
variant: "outlined",
|
|
373
|
+
sx: { ...defaultButtonStyle, minWidth: "54px" },
|
|
374
|
+
onClick: onCancel,
|
|
375
|
+
children: t("common.cancel")
|
|
301
376
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
377
|
+
),
|
|
378
|
+
/* @__PURE__ */ jsx(
|
|
379
|
+
Button,
|
|
380
|
+
{
|
|
381
|
+
fullWidth: mode === "drawer",
|
|
382
|
+
size: "small",
|
|
383
|
+
disabled: !!validateMsg.email || !!validateMsg.phone,
|
|
384
|
+
variant: "outlined",
|
|
385
|
+
sx: {
|
|
386
|
+
...primaryButtonStyle,
|
|
387
|
+
minWidth: "54px",
|
|
388
|
+
"&.Mui-disabled": {
|
|
389
|
+
backgroundColor: "rgba(0, 0, 0, 0.12)"
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
onClick: handleSave,
|
|
393
|
+
children: t("common.save")
|
|
394
|
+
}
|
|
395
|
+
)
|
|
396
|
+
]
|
|
397
|
+
}
|
|
398
|
+
) : null
|
|
399
|
+
]
|
|
400
|
+
}
|
|
401
|
+
);
|
|
311
402
|
};
|
|
312
403
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
313
404
|
renderEdit(editable),
|
|
@@ -374,9 +465,6 @@ export default function UserMetadataComponent({
|
|
|
374
465
|
] });
|
|
375
466
|
}
|
|
376
467
|
const MetadataInfo = styled(Box)`
|
|
377
|
-
gap: 16px;
|
|
378
|
-
justify-content: space-between;
|
|
379
|
-
align-items: flex-start;
|
|
380
468
|
width: 100%;
|
|
381
469
|
|
|
382
470
|
.MuiOutlinedInput-root {
|
|
@@ -70,11 +70,8 @@ export function TimezoneSelect({ value, onChange, disabled = false, mode = "self
|
|
|
70
70
|
size: "small",
|
|
71
71
|
fullWidth: true,
|
|
72
72
|
onChange: (event) => setSearchText(event.target.value),
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
e.stopPropagation();
|
|
76
|
-
}
|
|
77
|
-
},
|
|
73
|
+
onClick: (e) => e.stopPropagation(),
|
|
74
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
78
75
|
sx: {
|
|
79
76
|
marginTop: "8px",
|
|
80
77
|
"& .MuiOutlinedInput-root": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BoxProps } from '@mui/material';
|
|
2
2
|
import type { User } from '../../../@types';
|
|
3
|
-
export default function UserBasicInfo({ user, isMyself, showFullDid, switchPassport, switchProfile, isMobile, ...rest }: {
|
|
3
|
+
export default function UserBasicInfo({ user, isMyself, showFullDid, switchPassport, switchProfile, isMobile, onlyProfile, ...rest }: {
|
|
4
4
|
user: User;
|
|
5
5
|
isMyself?: boolean;
|
|
6
6
|
showFullDid?: boolean;
|
|
@@ -8,4 +8,5 @@ export default function UserBasicInfo({ user, isMyself, showFullDid, switchPassp
|
|
|
8
8
|
switchProfile: () => void;
|
|
9
9
|
size?: number;
|
|
10
10
|
isMobile?: boolean;
|
|
11
|
+
onlyProfile?: boolean;
|
|
11
12
|
} & BoxProps): import("react").JSX.Element | null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Divider, Typography } from "@mui/material";
|
|
2
|
+
import { Box, Divider, Typography, IconButton, Collapse } from "@mui/material";
|
|
3
3
|
import Avatar from "@arcblock/ux/lib/Avatar";
|
|
4
4
|
import DID from "@arcblock/ux/lib/DID";
|
|
5
5
|
import { useMemoizedFn } from "ahooks";
|
|
@@ -10,6 +10,8 @@ import { useEffect, useState } from "react";
|
|
|
10
10
|
import Toast from "@arcblock/ux/lib/Toast";
|
|
11
11
|
import { temp as colors } from "@arcblock/ux/lib/Colors";
|
|
12
12
|
import { parseURL, joinURL } from "ufo";
|
|
13
|
+
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
|
14
|
+
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
|
13
15
|
import { translations } from "../../libs/locales.js";
|
|
14
16
|
import { formatAxiosError } from "../../libs/utils.js";
|
|
15
17
|
import { currentTimezone, getStatusDuration, isValidUrl } from "./utils.js";
|
|
@@ -25,6 +27,7 @@ export default function UserBasicInfo({
|
|
|
25
27
|
switchPassport,
|
|
26
28
|
switchProfile,
|
|
27
29
|
isMobile = false,
|
|
30
|
+
onlyProfile = false,
|
|
28
31
|
...rest
|
|
29
32
|
}) {
|
|
30
33
|
const { locale } = useLocaleContext();
|
|
@@ -32,9 +35,13 @@ export default function UserBasicInfo({
|
|
|
32
35
|
const t = useMemoizedFn((key, data = {}) => {
|
|
33
36
|
return translate(translations, key, locale, "en", data);
|
|
34
37
|
});
|
|
38
|
+
const [expanded, setExpanded] = useState(!isMobile || onlyProfile);
|
|
35
39
|
useEffect(() => {
|
|
36
40
|
setUserStatus(user?.metadata?.status);
|
|
37
41
|
}, [user]);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
setExpanded(!isMobile || onlyProfile);
|
|
44
|
+
}, [isMobile, onlyProfile]);
|
|
38
45
|
const onUpdateUserStatus = async (v) => {
|
|
39
46
|
if (!isMyself) {
|
|
40
47
|
return;
|
|
@@ -57,6 +64,9 @@ export default function UserBasicInfo({
|
|
|
57
64
|
Toast.error(formatAxiosError(err));
|
|
58
65
|
}
|
|
59
66
|
};
|
|
67
|
+
const toggleExpand = () => {
|
|
68
|
+
setExpanded(!expanded);
|
|
69
|
+
};
|
|
60
70
|
if (!user) {
|
|
61
71
|
return null;
|
|
62
72
|
}
|
|
@@ -184,11 +194,38 @@ export default function UserBasicInfo({
|
|
|
184
194
|
}
|
|
185
195
|
)
|
|
186
196
|
] }),
|
|
187
|
-
/* @__PURE__ */ jsx(UserMetadataComponent, { isMyself, user, onSave }),
|
|
197
|
+
/* @__PURE__ */ jsx(UserMetadataComponent, { isMobile, isMyself, user, onSave }),
|
|
188
198
|
isMyself ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
189
|
-
/* @__PURE__ */ jsx(Divider, { sx: { my: 3, borderColor: colors.dividerColor } }),
|
|
190
|
-
/* @__PURE__ */ jsx(
|
|
191
|
-
|
|
199
|
+
/* @__PURE__ */ jsx(Divider, { sx: { my: isMobile ? 1 : 3, borderColor: colors.dividerColor } }),
|
|
200
|
+
isMobile && !onlyProfile ? /* @__PURE__ */ jsx(
|
|
201
|
+
Box,
|
|
202
|
+
{
|
|
203
|
+
sx: {
|
|
204
|
+
display: "flex",
|
|
205
|
+
justifyContent: "center",
|
|
206
|
+
mb: 0
|
|
207
|
+
},
|
|
208
|
+
children: /* @__PURE__ */ jsx(
|
|
209
|
+
IconButton,
|
|
210
|
+
{
|
|
211
|
+
size: "small",
|
|
212
|
+
onClick: toggleExpand,
|
|
213
|
+
sx: {
|
|
214
|
+
backgroundColor: colors.backgroundsBgField,
|
|
215
|
+
"&:hover": {
|
|
216
|
+
backgroundColor: colors.backgroundsBgField,
|
|
217
|
+
opacity: 0.8
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
children: expanded ? /* @__PURE__ */ jsx(KeyboardArrowUpIcon, {}) : /* @__PURE__ */ jsx(KeyboardArrowDownIcon, {})
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
) : null,
|
|
225
|
+
/* @__PURE__ */ jsxs(Collapse, { in: expanded, timeout: "auto", children: [
|
|
226
|
+
/* @__PURE__ */ jsx(Typography, { component: "p", color: "text.secondary", fontSize: "14px", mb: 2, children: t("profile.justForYou") }),
|
|
227
|
+
/* @__PURE__ */ jsx(UserInfo, { user, isMySelf: isMyself })
|
|
228
|
+
] })
|
|
192
229
|
] }) : null
|
|
193
230
|
]
|
|
194
231
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/ui-react",
|
|
3
|
-
"version": "2.12.
|
|
3
|
+
"version": "2.12.17",
|
|
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.
|
|
37
|
-
"@arcblock/react-hooks": "^2.12.
|
|
36
|
+
"@arcblock/bridge": "^2.12.17",
|
|
37
|
+
"@arcblock/react-hooks": "^2.12.17",
|
|
38
38
|
"@arcblock/ws": "^1.19.15",
|
|
39
|
-
"@blocklet/did-space-react": "^1.0.
|
|
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": "
|
|
90
|
+
"gitHead": "de1814f4c2eca44000845ce0db42e9d30d487e80"
|
|
91
91
|
}
|
package/src/@types/index.ts
CHANGED
|
@@ -66,6 +66,11 @@ export enum StatusEnum {
|
|
|
66
66
|
WorkingRemotely = 'working_remotely',
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
export type UserPhoneProps = {
|
|
70
|
+
country: string;
|
|
71
|
+
phoneNumber: string;
|
|
72
|
+
};
|
|
73
|
+
|
|
69
74
|
export type UserMetadata = {
|
|
70
75
|
bio?: string;
|
|
71
76
|
location?: string;
|
|
@@ -80,7 +85,7 @@ export type UserMetadata = {
|
|
|
80
85
|
cover?: string;
|
|
81
86
|
// 这两个字段是 User, 方便数据更新,在保存时同步
|
|
82
87
|
email?: string;
|
|
83
|
-
phone?:
|
|
88
|
+
phone?: UserPhoneProps;
|
|
84
89
|
};
|
|
85
90
|
|
|
86
91
|
export type User = UserPublicInfo & {
|
|
@@ -25,6 +25,7 @@ interface EditableFieldProps {
|
|
|
25
25
|
verified?: boolean;
|
|
26
26
|
errorMsg?: string;
|
|
27
27
|
canEdit?: boolean;
|
|
28
|
+
renderValue?: (value: string) => React.ReactNode;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
const inputFieldStyle = {
|
|
@@ -65,6 +66,7 @@ function EditableField({
|
|
|
65
66
|
style = {},
|
|
66
67
|
verified = false,
|
|
67
68
|
canEdit = true,
|
|
69
|
+
renderValue,
|
|
68
70
|
}: EditableFieldProps) {
|
|
69
71
|
const { locale } = useLocaleContext();
|
|
70
72
|
const t = useMemoizedFn((key, data = {}) => {
|
|
@@ -160,7 +162,7 @@ function EditableField({
|
|
|
160
162
|
whiteSpace: 'pre-wrap',
|
|
161
163
|
...(inline ? { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } : {}),
|
|
162
164
|
}}>
|
|
163
|
-
{value}
|
|
165
|
+
{renderValue ? renderValue(value) : value}
|
|
164
166
|
</Typography>
|
|
165
167
|
{verified && (
|
|
166
168
|
<VerifiedIcon color="success" style={{ fontSize: 16, width: 16, marginLeft: 4, flexShrink: 0 }} />
|
|
@@ -81,6 +81,7 @@ export default function UserCenter({
|
|
|
81
81
|
userDid = undefined,
|
|
82
82
|
stickySidebar = false,
|
|
83
83
|
embed = false,
|
|
84
|
+
onlyProfile = false, // 只显示 profile 页面,用于 ArcSphere 只需要显示 Profile 的内容
|
|
84
85
|
}: {
|
|
85
86
|
readonly children: any;
|
|
86
87
|
readonly notLoginContent: any;
|
|
@@ -95,6 +96,7 @@ export default function UserCenter({
|
|
|
95
96
|
readonly userDid?: string;
|
|
96
97
|
readonly stickySidebar?: boolean;
|
|
97
98
|
readonly embed?: boolean;
|
|
99
|
+
readonly onlyProfile?: boolean;
|
|
98
100
|
}) {
|
|
99
101
|
const { locale } = useLocaleContext();
|
|
100
102
|
const isMobile = useMobile({ key: 'md' });
|
|
@@ -456,6 +458,28 @@ export default function UserCenter({
|
|
|
456
458
|
);
|
|
457
459
|
}
|
|
458
460
|
|
|
461
|
+
if (onlyProfile) {
|
|
462
|
+
return (
|
|
463
|
+
<ContentWrapper display="flex" flexDirection={isMobile ? 'column' : 'row'}>
|
|
464
|
+
<UserBasicInfo
|
|
465
|
+
isMobile={isMobile}
|
|
466
|
+
order={isMobile ? 1 : 'unset'}
|
|
467
|
+
isMyself={isMyself}
|
|
468
|
+
switchPassport={handleSwitchPassport}
|
|
469
|
+
switchProfile={session.switchProfile}
|
|
470
|
+
user={userState.data as User}
|
|
471
|
+
showFullDid={false}
|
|
472
|
+
onlyProfile={onlyProfile}
|
|
473
|
+
sx={{
|
|
474
|
+
padding: !isMobile ? '40px 24px 24px 40px' : '16px 0 0 0',
|
|
475
|
+
...(!isMobile ? { width: 320, maxWidth: 320, flexShrink: 0 } : {}),
|
|
476
|
+
boxSizing: 'content-box',
|
|
477
|
+
}}
|
|
478
|
+
/>
|
|
479
|
+
</ContentWrapper>
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
459
483
|
return (
|
|
460
484
|
<ContentWrapper display="flex" flexDirection={isMobile ? 'column' : 'row'}>
|
|
461
485
|
<Box flex="1" className="user-center-tabs" order={isMobile ? 2 : 'unset'}>
|
|
@@ -479,7 +503,7 @@ export default function UserCenter({
|
|
|
479
503
|
'.MuiTabs-flexContainer': {
|
|
480
504
|
gap: 3,
|
|
481
505
|
'.MuiButtonBase-root': {
|
|
482
|
-
padding: '40px 4px 32px 4px',
|
|
506
|
+
padding: isMobile ? '16px 4px' : '40px 4px 32px 4px',
|
|
483
507
|
fontSize: 16,
|
|
484
508
|
},
|
|
485
509
|
'.MuiTab-root': {
|
|
@@ -516,7 +540,7 @@ export default function UserCenter({
|
|
|
516
540
|
user={userState.data as User}
|
|
517
541
|
showFullDid={false}
|
|
518
542
|
sx={{
|
|
519
|
-
padding: !isMobile ? '40px 24px 24px 40px' : '
|
|
543
|
+
padding: !isMobile ? '40px 24px 24px 40px' : '16px 0 0 0',
|
|
520
544
|
...(!isMobile ? { width: 320, maxWidth: 320, flexShrink: 0 } : {}),
|
|
521
545
|
boxSizing: 'content-box',
|
|
522
546
|
}}
|
|
@@ -9,18 +9,19 @@ import Backdrop, { BackdropProps } from '@mui/material/Backdrop';
|
|
|
9
9
|
import styled from '@emotion/styled';
|
|
10
10
|
import { joinURL } from 'ufo';
|
|
11
11
|
import Button from '@arcblock/ux/lib/Button';
|
|
12
|
+
import PhoneInput, { PhoneValue, validatePhoneNumber } from '@arcblock/ux/lib/PhoneInput';
|
|
12
13
|
import cloneDeep from 'lodash/cloneDeep';
|
|
13
14
|
|
|
14
15
|
import { useCreation, useMemoizedFn, useReactive } from 'ahooks';
|
|
15
16
|
import { useMemo, useRef, useState, memo, forwardRef, useEffect, lazy } from 'react';
|
|
16
17
|
import { translate } from '@arcblock/ux/lib/Locale/util';
|
|
17
18
|
import isEmail from 'validator/lib/isEmail';
|
|
18
|
-
import
|
|
19
|
+
import { temp as colors } from '@arcblock/ux/lib/Colors';
|
|
19
20
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
20
21
|
|
|
21
22
|
import { useBrowser } from '@arcblock/react-hooks';
|
|
22
23
|
import { translations } from '../../libs/locales';
|
|
23
|
-
import type { User, UserMetadata } from '../../../@types';
|
|
24
|
+
import type { User, UserMetadata, UserPhoneProps } from '../../../@types';
|
|
24
25
|
import EditableField from '../editable-field';
|
|
25
26
|
import { LinkPreviewInput } from './link-preview-input';
|
|
26
27
|
import { currentTimezone, defaultButtonStyle, primaryButtonStyle } from './utils';
|
|
@@ -64,7 +65,9 @@ export default function UserMetadataComponent({
|
|
|
64
65
|
isMyself,
|
|
65
66
|
user,
|
|
66
67
|
onSave,
|
|
68
|
+
isMobile,
|
|
67
69
|
}: {
|
|
70
|
+
isMobile: boolean;
|
|
68
71
|
isMyself: boolean;
|
|
69
72
|
user: User;
|
|
70
73
|
onSave: (v: UserMetadata) => void;
|
|
@@ -104,11 +107,40 @@ export default function UserMetadataComponent({
|
|
|
104
107
|
: {
|
|
105
108
|
joinedAt: user?.createdAt,
|
|
106
109
|
email: user?.email,
|
|
107
|
-
phone:
|
|
110
|
+
phone: {
|
|
111
|
+
country: 'cn',
|
|
112
|
+
phoneNumber: user?.phone ?? '',
|
|
113
|
+
},
|
|
108
114
|
}
|
|
109
115
|
);
|
|
110
116
|
|
|
111
|
-
const
|
|
117
|
+
const phoneValue = useCreation((): PhoneValue => {
|
|
118
|
+
const phone = metadata.phone ??
|
|
119
|
+
user?.phone ?? {
|
|
120
|
+
country: 'cn',
|
|
121
|
+
phone: '',
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (typeof phone === 'string') {
|
|
125
|
+
return {
|
|
126
|
+
country: 'cn',
|
|
127
|
+
phone,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (typeof phone === 'object') {
|
|
131
|
+
return {
|
|
132
|
+
country: phone.country,
|
|
133
|
+
phone: (phone as UserPhoneProps).phoneNumber,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
country: 'cn',
|
|
139
|
+
phone: '',
|
|
140
|
+
};
|
|
141
|
+
}, [metadata.phone, user?.phone]);
|
|
142
|
+
|
|
143
|
+
const onChange = (v: any, field: keyof UserMetadata | 'email' | 'phone') => {
|
|
112
144
|
metadata[field] = v;
|
|
113
145
|
};
|
|
114
146
|
|
|
@@ -186,7 +218,13 @@ export default function UserMetadataComponent({
|
|
|
186
218
|
|
|
187
219
|
const renderEdit = (editing: boolean, mode: 'drawer' | 'self' = 'self') => {
|
|
188
220
|
return (
|
|
189
|
-
<MetadataInfo
|
|
221
|
+
<MetadataInfo
|
|
222
|
+
pt={2}
|
|
223
|
+
display="flex"
|
|
224
|
+
flexDirection="column"
|
|
225
|
+
justifyContent="space-between"
|
|
226
|
+
alignItems="flex-start"
|
|
227
|
+
gap={!isMobile ? '16px' : '4px'}>
|
|
190
228
|
<EditableField
|
|
191
229
|
value={metadata.bio ?? ''}
|
|
192
230
|
onChange={(value) => onChange(value, 'bio')}
|
|
@@ -203,13 +241,13 @@ export default function UserMetadataComponent({
|
|
|
203
241
|
/>
|
|
204
242
|
{!editing && isMyself ? (
|
|
205
243
|
<Button
|
|
206
|
-
size=
|
|
244
|
+
size={isMobile ? 'small' : 'large'}
|
|
207
245
|
variant="outlined"
|
|
208
246
|
sx={{
|
|
209
247
|
...defaultButtonStyle,
|
|
210
|
-
mb: 2,
|
|
211
|
-
mt: 2,
|
|
212
|
-
height: '40px',
|
|
248
|
+
mb: !isMobile ? 2 : '4px',
|
|
249
|
+
mt: !isMobile ? 2 : '4px',
|
|
250
|
+
height: !isMobile ? '40px' : '32px',
|
|
213
251
|
}}
|
|
214
252
|
onClick={onEdit}
|
|
215
253
|
fullWidth>
|
|
@@ -255,6 +293,16 @@ export default function UserMetadataComponent({
|
|
|
255
293
|
label={t('profile.email')}
|
|
256
294
|
onChange={(value) => onChange(value, 'email')}
|
|
257
295
|
errorMsg={validateMsg.email}
|
|
296
|
+
renderValue={(value) => (
|
|
297
|
+
<a
|
|
298
|
+
href={`mailto:${value}`}
|
|
299
|
+
style={{
|
|
300
|
+
color: 'inherit',
|
|
301
|
+
textDecoration: 'none',
|
|
302
|
+
}}>
|
|
303
|
+
{value}
|
|
304
|
+
</a>
|
|
305
|
+
)}
|
|
258
306
|
onValueValidate={(value) => {
|
|
259
307
|
let msg = '';
|
|
260
308
|
if (!!value && !isEmail(value)) {
|
|
@@ -265,7 +313,7 @@ export default function UserMetadataComponent({
|
|
|
265
313
|
/>
|
|
266
314
|
|
|
267
315
|
<EditableField
|
|
268
|
-
value={
|
|
316
|
+
value={phoneValue.phone}
|
|
269
317
|
editable={editing}
|
|
270
318
|
canEdit={!phoneVerified}
|
|
271
319
|
verified={phoneVerified}
|
|
@@ -273,15 +321,54 @@ export default function UserMetadataComponent({
|
|
|
273
321
|
icon={<PhoneIcon {...iconSize} />}
|
|
274
322
|
onChange={(value) => onChange(value, 'phone')}
|
|
275
323
|
label={t('profile.phone')}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
324
|
+
renderValue={() => {
|
|
325
|
+
return <PhoneInput value={phoneValue} preview />;
|
|
326
|
+
}}>
|
|
327
|
+
<PhoneInput
|
|
328
|
+
variant="outlined"
|
|
329
|
+
className="editable-field"
|
|
330
|
+
InputProps={{
|
|
331
|
+
sx: { backgroundColor: 'transparent' },
|
|
332
|
+
placeholder: 'Phone',
|
|
333
|
+
}}
|
|
334
|
+
value={phoneValue}
|
|
335
|
+
error={!!validateMsg.phone}
|
|
336
|
+
helperText={validateMsg.phone}
|
|
337
|
+
sx={{
|
|
338
|
+
width: '100%',
|
|
339
|
+
'.MuiOutlinedInput-root': {
|
|
340
|
+
'&:hover': {
|
|
341
|
+
fieldset: {
|
|
342
|
+
borderColor: colors.dividerColor,
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
'&.Mui-focused': {
|
|
346
|
+
fieldset: {
|
|
347
|
+
borderColor: colors.dividerColor,
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
fieldset: {
|
|
352
|
+
borderColor: colors.dividerColor,
|
|
353
|
+
},
|
|
354
|
+
}}
|
|
355
|
+
onChange={(value: any) => {
|
|
356
|
+
const isValid = validatePhoneNumber(value.phone);
|
|
357
|
+
if (!isValid) {
|
|
358
|
+
validateMsg.phone = t('profile.phoneInvalid');
|
|
359
|
+
} else {
|
|
360
|
+
validateMsg.phone = '';
|
|
361
|
+
}
|
|
362
|
+
onChange(
|
|
363
|
+
{
|
|
364
|
+
country: value.country,
|
|
365
|
+
phoneNumber: value.phone,
|
|
366
|
+
},
|
|
367
|
+
'phone'
|
|
368
|
+
);
|
|
369
|
+
}}
|
|
370
|
+
/>
|
|
371
|
+
</EditableField>
|
|
285
372
|
|
|
286
373
|
<LinkPreviewInput editable={editing} links={links} onChange={handleLinksChange} />
|
|
287
374
|
{editing && isMyself ? (
|
|
@@ -374,9 +461,6 @@ export default function UserMetadataComponent({
|
|
|
374
461
|
);
|
|
375
462
|
}
|
|
376
463
|
const MetadataInfo = styled(Box)`
|
|
377
|
-
gap: 16px;
|
|
378
|
-
justify-content: space-between;
|
|
379
|
-
align-items: flex-start;
|
|
380
464
|
width: 100%;
|
|
381
465
|
|
|
382
466
|
.MuiOutlinedInput-root {
|
|
@@ -82,11 +82,8 @@ export function TimezoneSelect({ value, onChange, disabled = false, mode = 'self
|
|
|
82
82
|
size="small"
|
|
83
83
|
fullWidth
|
|
84
84
|
onChange={(event) => setSearchText(event.target.value)}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
e.stopPropagation();
|
|
88
|
-
}
|
|
89
|
-
}}
|
|
85
|
+
onClick={(e) => e.stopPropagation()}
|
|
86
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
90
87
|
sx={{
|
|
91
88
|
marginTop: '8px',
|
|
92
89
|
'& .MuiOutlinedInput-root': {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box, Divider, Typography } from '@mui/material';
|
|
1
|
+
import { Box, Divider, Typography, IconButton, Collapse } from '@mui/material';
|
|
2
2
|
import type { BoxProps } from '@mui/material';
|
|
3
3
|
import Avatar from '@arcblock/ux/lib/Avatar';
|
|
4
4
|
import DID from '@arcblock/ux/lib/DID';
|
|
@@ -11,6 +11,8 @@ import Toast from '@arcblock/ux/lib/Toast';
|
|
|
11
11
|
import { temp as colors } from '@arcblock/ux/lib/Colors';
|
|
12
12
|
import type { AxiosError } from 'axios';
|
|
13
13
|
import { parseURL, joinURL } from 'ufo';
|
|
14
|
+
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
|
15
|
+
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
|
14
16
|
|
|
15
17
|
import { translations } from '../../libs/locales';
|
|
16
18
|
import type { User, UserMetadata } from '../../../@types';
|
|
@@ -29,6 +31,7 @@ export default function UserBasicInfo({
|
|
|
29
31
|
switchPassport,
|
|
30
32
|
switchProfile,
|
|
31
33
|
isMobile = false,
|
|
34
|
+
onlyProfile = false,
|
|
32
35
|
...rest
|
|
33
36
|
}: {
|
|
34
37
|
user: User;
|
|
@@ -38,6 +41,7 @@ export default function UserBasicInfo({
|
|
|
38
41
|
switchProfile: () => void;
|
|
39
42
|
size?: number;
|
|
40
43
|
isMobile?: boolean;
|
|
44
|
+
onlyProfile?: boolean;
|
|
41
45
|
} & BoxProps) {
|
|
42
46
|
const { locale } = useLocaleContext();
|
|
43
47
|
const [userStatus, setUserStatus] = useState<UserMetadata['status']>(undefined);
|
|
@@ -45,10 +49,17 @@ export default function UserBasicInfo({
|
|
|
45
49
|
return translate(translations, key, locale, 'en', data);
|
|
46
50
|
});
|
|
47
51
|
|
|
52
|
+
const [expanded, setExpanded] = useState(!isMobile || onlyProfile);
|
|
53
|
+
|
|
48
54
|
useEffect(() => {
|
|
49
55
|
setUserStatus(user?.metadata?.status);
|
|
50
56
|
}, [user]);
|
|
51
57
|
|
|
58
|
+
// Add effect to handle mobile state changes
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
setExpanded(!isMobile || onlyProfile);
|
|
61
|
+
}, [isMobile, onlyProfile]);
|
|
62
|
+
|
|
52
63
|
const onUpdateUserStatus = async (v: UserMetadata['status']) => {
|
|
53
64
|
if (!isMyself) {
|
|
54
65
|
return;
|
|
@@ -72,6 +83,10 @@ export default function UserBasicInfo({
|
|
|
72
83
|
}
|
|
73
84
|
};
|
|
74
85
|
|
|
86
|
+
const toggleExpand = () => {
|
|
87
|
+
setExpanded(!expanded);
|
|
88
|
+
};
|
|
89
|
+
|
|
75
90
|
if (!user) {
|
|
76
91
|
return null;
|
|
77
92
|
}
|
|
@@ -187,14 +202,37 @@ export default function UserBasicInfo({
|
|
|
187
202
|
<DID did={user.did} showQrcode copyable compact={!showFullDid} responsive={!showFullDid} locale={locale} />
|
|
188
203
|
</Box>
|
|
189
204
|
</Box>
|
|
190
|
-
<UserMetadataComponent isMyself={isMyself} user={user} onSave={onSave} />
|
|
205
|
+
<UserMetadataComponent isMobile={isMobile} isMyself={isMyself} user={user} onSave={onSave} />
|
|
191
206
|
{isMyself ? (
|
|
192
207
|
<>
|
|
193
|
-
<Divider sx={{ my: 3, borderColor: colors.dividerColor }} />
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
208
|
+
<Divider sx={{ my: isMobile ? 1 : 3, borderColor: colors.dividerColor }} />
|
|
209
|
+
{isMobile && !onlyProfile ? (
|
|
210
|
+
<Box
|
|
211
|
+
sx={{
|
|
212
|
+
display: 'flex',
|
|
213
|
+
justifyContent: 'center',
|
|
214
|
+
mb: 0,
|
|
215
|
+
}}>
|
|
216
|
+
<IconButton
|
|
217
|
+
size="small"
|
|
218
|
+
onClick={toggleExpand}
|
|
219
|
+
sx={{
|
|
220
|
+
backgroundColor: colors.backgroundsBgField,
|
|
221
|
+
'&:hover': {
|
|
222
|
+
backgroundColor: colors.backgroundsBgField,
|
|
223
|
+
opacity: 0.8,
|
|
224
|
+
},
|
|
225
|
+
}}>
|
|
226
|
+
{expanded ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
|
|
227
|
+
</IconButton>
|
|
228
|
+
</Box>
|
|
229
|
+
) : null}
|
|
230
|
+
<Collapse in={expanded} timeout="auto">
|
|
231
|
+
<Typography component="p" color="text.secondary" fontSize="14px" mb={2}>
|
|
232
|
+
{t('profile.justForYou')}
|
|
233
|
+
</Typography>
|
|
234
|
+
<UserInfo user={user} isMySelf={isMyself} />
|
|
235
|
+
</Collapse>
|
|
198
236
|
</>
|
|
199
237
|
) : null}
|
|
200
238
|
</Box>
|