332 lines
9.4 KiB
JavaScript
332 lines
9.4 KiB
JavaScript
import {
|
|
Button,
|
|
Card,
|
|
Col,
|
|
Form,
|
|
Input,
|
|
Row,
|
|
Select,
|
|
Space,
|
|
Table,
|
|
Upload,
|
|
notification,
|
|
} from "antd";
|
|
import { useContext, useState } from "react";
|
|
import {
|
|
Constants,
|
|
EncodeStringToBase64,
|
|
FormatDatetime,
|
|
MyAvatar,
|
|
SentMessagesCommands,
|
|
WebSocketContext,
|
|
getConnectionStatusItem,
|
|
getUserSessionFromLocalStorage,
|
|
handleUnauthorizedStatus,
|
|
isEmailValid,
|
|
} from "../../utils";
|
|
import { Link } from "react-router-dom";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
export default function UserProfile() {
|
|
const webSocketContext = useContext(WebSocketContext);
|
|
const [notificationApi, notificationContextHolder] =
|
|
notification.useNotification();
|
|
const { t, i18n } = useTranslation();
|
|
|
|
const [oldPassword, setOldPassword] = useState("");
|
|
const [newPassword, setNewPassword] = useState("");
|
|
const [repeatedNewPassword, setRepeatedNewPassword] = useState("");
|
|
|
|
const getTableColumns = () => {
|
|
return [
|
|
{
|
|
title: t("userProfile.column.userAgent"),
|
|
dataIndex: "userAgent",
|
|
key: "userAgent",
|
|
},
|
|
{
|
|
title: t("userProfile.column.connectionStatus"),
|
|
dataIndex: "connectionStatus",
|
|
key: "connectionStatus",
|
|
},
|
|
{
|
|
title: t("userProfile.column.lastUsed"),
|
|
dataIndex: "lastUsed",
|
|
key: "lastUsed",
|
|
},
|
|
{
|
|
title: t("userProfile.column.expiresAt"),
|
|
dataIndex: "expiresAt",
|
|
key: "expiresAt",
|
|
},
|
|
{
|
|
title: t("userProfile.column.action"),
|
|
dataIndex: "action",
|
|
key: "action",
|
|
render: (_, record) => {
|
|
return (
|
|
<Space size="middle">
|
|
<Link
|
|
href="#"
|
|
onClick={() => {
|
|
fetch(`${Constants.API_ADDRESS}/user/session/${record.key}`, {
|
|
method: "DELETE",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"X-Authorization": getUserSessionFromLocalStorage(),
|
|
},
|
|
})
|
|
.then((res) => handleUnauthorizedStatus(res.status))
|
|
.catch((err) => {
|
|
console.error(err);
|
|
});
|
|
}}
|
|
>
|
|
{t("userProfile.column.action.signOut")}
|
|
</Link>
|
|
</Space>
|
|
);
|
|
},
|
|
},
|
|
];
|
|
};
|
|
|
|
const getTableItems = () => {
|
|
let items = [];
|
|
|
|
webSocketContext.User.Sessions.sort(
|
|
(a, b) => b.ConnectionStatus - a.ConnectionStatus
|
|
);
|
|
|
|
webSocketContext.User.Sessions.forEach((session) => {
|
|
items.push({
|
|
key: session.IdForDeletion,
|
|
userAgent: session.UserAgent,
|
|
connectionStatus: getConnectionStatusItem(session.ConnectionStatus),
|
|
lastUsed: FormatDatetime(session.LastUsed),
|
|
expiresAt: FormatDatetime(session.ExpiresAt),
|
|
});
|
|
});
|
|
|
|
return items;
|
|
};
|
|
|
|
const beforeUpload = (file) => {
|
|
if (file.size > Constants.MAX_AVATAR_SIZE) {
|
|
notificationApi["error"]({
|
|
message: t("userProfile.changeAvatarError.notification.message"),
|
|
description: t(
|
|
"userProfile.changeAvatarError.notification.description",
|
|
{ MAX_AVATAR_SIZE: Constants.MAX_AVATAR_SIZE / 1024 / 1024 }
|
|
),
|
|
});
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
const isButtonDisabled = () => {
|
|
if (
|
|
!isEmailValid(webSocketContext.UserProfileStateEmail) ||
|
|
webSocketContext.UserProfileStateUsername.length <
|
|
Constants.GLOBALS.MIN_USERNAME_LENGTH
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
if (
|
|
webSocketContext.UserProfileStateUsername !==
|
|
webSocketContext.User.Username ||
|
|
webSocketContext.UserProfileStateEmail !== webSocketContext.User.Email ||
|
|
(oldPassword !== "" &&
|
|
newPassword !== "" &&
|
|
newPassword === repeatedNewPassword)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
const handleOnSubmit = () => {
|
|
const changes = {};
|
|
|
|
if (
|
|
webSocketContext.User.Username !==
|
|
webSocketContext.UserProfileStateUsername
|
|
) {
|
|
changes.username = webSocketContext.UserProfileStateUsername;
|
|
}
|
|
|
|
if (
|
|
webSocketContext.User.Email !== webSocketContext.UserProfileStateEmail
|
|
) {
|
|
changes.email = webSocketContext.UserProfileStateEmail;
|
|
}
|
|
|
|
if (
|
|
oldPassword !== "" &&
|
|
newPassword !== "" &&
|
|
newPassword === repeatedNewPassword
|
|
) {
|
|
changes.oldPassword = EncodeStringToBase64(oldPassword);
|
|
changes.newPassword = EncodeStringToBase64(newPassword);
|
|
}
|
|
|
|
webSocketContext.SendSocketMessage(SentMessagesCommands.UpdateUserProfile, {
|
|
changes: changes,
|
|
});
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{notificationContextHolder}
|
|
|
|
<h1 style={{ fontWeight: "bold" }}>
|
|
{t("userProfile.header.yourProfile")}
|
|
</h1>
|
|
|
|
<Card>
|
|
<Form layout="vertical">
|
|
<Row>
|
|
<Col span={4}>
|
|
<Upload
|
|
accept={Constants.ACCEPTED_FILE_TYPES.join(",")}
|
|
action={Constants.API_ADDRESS + "/user/avatar"}
|
|
maxCount={1}
|
|
showUploadList={false}
|
|
beforeUpload={beforeUpload}
|
|
headers={{
|
|
"X-Authorization": getUserSessionFromLocalStorage(),
|
|
}}
|
|
>
|
|
<MyAvatar
|
|
avatarWidth={200}
|
|
allUsers={webSocketContext.AllUsers}
|
|
userId={webSocketContext.User.Id}
|
|
/>
|
|
</Upload>
|
|
</Col>
|
|
|
|
<Col span={4} offset={16}>
|
|
<Form.Item label={t("userProfile.form.language")}>
|
|
<Select
|
|
style={{ width: "100%" }}
|
|
defaultValue={i18n.language}
|
|
options={[
|
|
{
|
|
value: "en",
|
|
label: "English",
|
|
},
|
|
{
|
|
value: "de",
|
|
label: "Deutsch",
|
|
},
|
|
]}
|
|
onChange={(e) => i18n.changeLanguage(e)}
|
|
/>
|
|
</Form.Item>
|
|
</Col>
|
|
</Row>
|
|
|
|
<Form.Item
|
|
label={t("userProfile.form.username")}
|
|
hasFeedback
|
|
validateStatus={
|
|
webSocketContext.UserProfileStateUsername.length <
|
|
Constants.GLOBALS.MIN_USERNAME_LENGTH && "error"
|
|
}
|
|
>
|
|
<Input
|
|
value={webSocketContext.UserProfileStateUsername}
|
|
onChange={(e) =>
|
|
webSocketContext.setUserProfileStateUsername(e.target.value)
|
|
}
|
|
minLength={Constants.GLOBALS.MIN_USERNAME_LENGTH}
|
|
maxLength={Constants.GLOBALS.MAX_USERNAME_LENGTH}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("userProfile.form.email")}
|
|
hasFeedback
|
|
validateStatus={
|
|
!isEmailValid(webSocketContext.UserProfileStateEmail) && "error"
|
|
}
|
|
>
|
|
<Input
|
|
type="email"
|
|
value={webSocketContext.UserProfileStateEmail}
|
|
onChange={(e) =>
|
|
webSocketContext.setUserProfileStateEmail(e.target.value)
|
|
}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("userProfile.form.oldPassword")}
|
|
hasFeedback
|
|
validateStatus={
|
|
oldPassword !== "" &&
|
|
oldPassword.length < Constants.GLOBALS.MIN_PASSWORD_LENGTH &&
|
|
"error"
|
|
}
|
|
>
|
|
<Input.Password
|
|
value={oldPassword}
|
|
onChange={(e) => setOldPassword(e.target.value)}
|
|
minLength={Constants.GLOBALS.MIN_PASSWORD_LENGTH}
|
|
maxLength={Constants.GLOBALS.MAX_PASSWORD_LENGTH}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("userProfile.form.newPassword")}
|
|
hasFeedback
|
|
validateStatus={
|
|
newPassword !== "" &&
|
|
newPassword.length < Constants.GLOBALS.MIN_PASSWORD_LENGTH &&
|
|
"error"
|
|
}
|
|
>
|
|
<Input.Password
|
|
value={newPassword}
|
|
onChange={(e) => setNewPassword(e.target.value)}
|
|
minLength={Constants.GLOBALS.MIN_PASSWORD_LENGTH}
|
|
maxLength={Constants.GLOBALS.MAX_PASSWORD_LENGTH}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("userProfile.form.repeatNewPassword")}
|
|
hasFeedback
|
|
validateStatus={newPassword !== repeatedNewPassword && "error"}
|
|
>
|
|
<Input.Password
|
|
value={repeatedNewPassword}
|
|
onChange={(e) => setRepeatedNewPassword(e.target.value)}
|
|
minLength={Constants.GLOBALS.MIN_PASSWORD_LENGTH}
|
|
maxLength={Constants.GLOBALS.MAX_PASSWORD_LENGTH}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item style={{ margin: 0 }}>
|
|
<Button
|
|
type="primary"
|
|
htmlType="submit"
|
|
onClick={() => handleOnSubmit()}
|
|
disabled={isButtonDisabled()}
|
|
>
|
|
{t("buttonSave")}
|
|
</Button>
|
|
</Form.Item>
|
|
</Form>
|
|
</Card>
|
|
|
|
<br />
|
|
|
|
<h1 style={{ fontWeight: "bold" }}>
|
|
{t("userProfile.header.yourSessions")} (
|
|
{webSocketContext.User.Sessions.length})
|
|
</h1>
|
|
|
|
<Table columns={getTableColumns()} dataSource={getTableItems()} />
|
|
</>
|
|
);
|
|
}
|