From 47587812b809fee2a95c76266d9d0e42fc4ac1ca Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 15 Jun 2021 14:14:28 +0800 Subject: ... --- .../src/app/views/settings/ChangeAvatarDialog.tsx | 305 --------------------- .../app/views/settings/ChangeNicknameDialog.tsx | 32 --- .../app/views/settings/ChangePasswordDialog.tsx | 68 ----- FrontEnd/src/app/views/settings/index.tsx | 138 ---------- FrontEnd/src/app/views/settings/settings.sass | 14 - 5 files changed, 557 deletions(-) delete mode 100644 FrontEnd/src/app/views/settings/ChangeAvatarDialog.tsx delete mode 100644 FrontEnd/src/app/views/settings/ChangeNicknameDialog.tsx delete mode 100644 FrontEnd/src/app/views/settings/ChangePasswordDialog.tsx delete mode 100644 FrontEnd/src/app/views/settings/index.tsx delete mode 100644 FrontEnd/src/app/views/settings/settings.sass (limited to 'FrontEnd/src/app/views/settings') diff --git a/FrontEnd/src/app/views/settings/ChangeAvatarDialog.tsx b/FrontEnd/src/app/views/settings/ChangeAvatarDialog.tsx deleted file mode 100644 index c4f6f492..00000000 --- a/FrontEnd/src/app/views/settings/ChangeAvatarDialog.tsx +++ /dev/null @@ -1,305 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { useTranslation } from "react-i18next"; -import { AxiosError } from "axios"; -import { Modal, Row, Button } from "react-bootstrap"; - -import { UiLogicError } from "@/common"; - -import { useUserLoggedIn } from "@/services/user"; - -import { getHttpUserClient } from "@/http/user"; - -import ImageCropper, { Clip, applyClipToImage } from "../common/ImageCropper"; - -export interface ChangeAvatarDialogProps { - open: boolean; - close: () => void; -} - -const ChangeAvatarDialog: React.FC = (props) => { - const { t } = useTranslation(); - - const user = useUserLoggedIn(); - - const [file, setFile] = React.useState(null); - const [fileUrl, setFileUrl] = React.useState(null); - const [clip, setClip] = React.useState(null); - const [cropImgElement, setCropImgElement] = - React.useState(null); - const [resultBlob, setResultBlob] = React.useState(null); - const [resultUrl, setResultUrl] = React.useState(null); - - const [state, setState] = React.useState< - | "select" - | "crop" - | "processcrop" - | "preview" - | "uploading" - | "success" - | "error" - >("select"); - - const [message, setMessage] = useState< - string | { type: "custom"; text: string } | null - >("settings.dialogChangeAvatar.prompt.select"); - - const trueMessage = - message == null - ? null - : typeof message === "string" - ? t(message) - : message.text; - - const closeDialog = props.close; - - const close = React.useCallback((): void => { - if (!(state === "uploading")) { - closeDialog(); - } - }, [state, closeDialog]); - - useEffect(() => { - if (file != null) { - const url = URL.createObjectURL(file); - setClip(null); - setFileUrl(url); - setState("crop"); - return () => { - URL.revokeObjectURL(url); - }; - } else { - setFileUrl(null); - setState("select"); - } - }, [file]); - - React.useEffect(() => { - if (resultBlob != null) { - const url = URL.createObjectURL(resultBlob); - setResultUrl(url); - setState("preview"); - return () => { - URL.revokeObjectURL(url); - }; - } else { - setResultUrl(null); - } - }, [resultBlob]); - - const onSelectFile = React.useCallback( - (e: React.ChangeEvent): void => { - const files = e.target.files; - if (files == null || files.length === 0) { - setFile(null); - } else { - setFile(files[0]); - } - }, - [] - ); - - const onCropNext = React.useCallback(() => { - if ( - cropImgElement == null || - clip == null || - clip.width === 0 || - file == null - ) { - throw new UiLogicError(); - } - - setState("processcrop"); - void applyClipToImage(cropImgElement, clip, file.type).then((b) => { - setResultBlob(b); - }); - }, [cropImgElement, clip, file]); - - const onCropPrevious = React.useCallback(() => { - setFile(null); - setState("select"); - }, []); - - const onPreviewPrevious = React.useCallback(() => { - setResultBlob(null); - setState("crop"); - }, []); - - const upload = React.useCallback(() => { - if (resultBlob == null) { - throw new UiLogicError(); - } - - setState("uploading"); - getHttpUserClient() - .putAvatar(user.username, resultBlob) - .then( - () => { - setState("success"); - }, - (e: unknown) => { - setState("error"); - setMessage({ type: "custom", text: (e as AxiosError).message }); - } - ); - }, [user.username, resultBlob]); - - const createPreviewRow = (): React.ReactElement => { - if (resultUrl == null) { - throw new UiLogicError(); - } - return ( - - {t("settings.dialogChangeAvatar.previewImgAlt")} - - ); - }; - - return ( - - - {t("settings.dialogChangeAvatar.title")} - - {(() => { - if (state === "select") { - return ( - <> - - {t("settings.dialogChangeAvatar.prompt.select")} - - - - - - - - - ); - } else if (state === "crop") { - if (fileUrl == null) { - throw new UiLogicError(); - } - return ( - <> - - - - - {t("settings.dialogChangeAvatar.prompt.crop")} - - - - - - - - ); - } else if (state === "processcrop") { - return ( - <> - - - {t("settings.dialogChangeAvatar.prompt.processingCrop")} - - - - - - - - ); - } else if (state === "preview") { - return ( - <> - - {createPreviewRow()} - {t("settings.dialogChangeAvatar.prompt.preview")} - - - - - - - - ); - } else if (state === "uploading") { - return ( - <> - - {createPreviewRow()} - {t("settings.dialogChangeAvatar.prompt.uploading")} - - - - ); - } else if (state === "success") { - return ( - <> - - - {t("operationDialog.success")} - - - - - - - ); - } else { - return ( - <> - - {createPreviewRow()} - {trueMessage} - - - - - - - ); - } - })()} - - ); -}; - -export default ChangeAvatarDialog; diff --git a/FrontEnd/src/app/views/settings/ChangeNicknameDialog.tsx b/FrontEnd/src/app/views/settings/ChangeNicknameDialog.tsx deleted file mode 100644 index 4b44cdd6..00000000 --- a/FrontEnd/src/app/views/settings/ChangeNicknameDialog.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { getHttpUserClient } from "@/http/user"; -import { useUserLoggedIn } from "@/services/user"; -import React from "react"; - -import OperationDialog from "../common/OperationDialog"; - -export interface ChangeNicknameDialogProps { - open: boolean; - close: () => void; -} - -const ChangeNicknameDialog: React.FC = (props) => { - const user = useUserLoggedIn(); - - return ( - { - return getHttpUserClient().patch(user.username, { - nickname: newNickname, - }); - }} - close={props.close} - /> - ); -}; - -export default ChangeNicknameDialog; diff --git a/FrontEnd/src/app/views/settings/ChangePasswordDialog.tsx b/FrontEnd/src/app/views/settings/ChangePasswordDialog.tsx deleted file mode 100644 index 21eeeb09..00000000 --- a/FrontEnd/src/app/views/settings/ChangePasswordDialog.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useState } from "react"; -import { useHistory } from "react-router"; - -import { userService } from "@/services/user"; - -import OperationDialog from "../common/OperationDialog"; - -export interface ChangePasswordDialogProps { - open: boolean; - close: () => void; -} - -const ChangePasswordDialog: React.FC = (props) => { - const history = useHistory(); - - const [redirect, setRedirect] = useState(false); - - return ( - { - const result: Record = {}; - if (oldPassword === "") { - result[0] = "settings.dialogChangePassword.errorEmptyOldPassword"; - } - if (newPassword === "") { - result[1] = "settings.dialogChangePassword.errorEmptyNewPassword"; - } - if (retypedNewPassword !== newPassword) { - result[2] = "settings.dialogChangePassword.errorRetypeNotMatch"; - } - return result; - }} - onProcess={async ([oldPassword, newPassword]) => { - await userService.changePassword(oldPassword, newPassword); - setRedirect(true); - }} - close={() => { - props.close(); - if (redirect) { - history.push("/login"); - } - }} - /> - ); -}; - -export default ChangePasswordDialog; diff --git a/FrontEnd/src/app/views/settings/index.tsx b/FrontEnd/src/app/views/settings/index.tsx deleted file mode 100644 index 04a2777a..00000000 --- a/FrontEnd/src/app/views/settings/index.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import React, { useState } from "react"; -import { useHistory } from "react-router"; -import { useTranslation } from "react-i18next"; -import { Container, Form, Row, Col, Button, Modal } from "react-bootstrap"; - -import { useUser, userService } from "@/services/user"; - -import ChangePasswordDialog from "./ChangePasswordDialog"; -import ChangeAvatarDialog from "./ChangeAvatarDialog"; -import ChangeNicknameDialog from "./ChangeNicknameDialog"; - -const ConfirmLogoutDialog: React.FC<{ - onClose: () => void; - onConfirm: () => void; -}> = ({ onClose, onConfirm }) => { - const { t } = useTranslation(); - - return ( - - - - {t("settings.dialogConfirmLogout.title")} - - - {t("settings.dialogConfirmLogout.prompt")} - - - - - - ); -}; - -const SettingsPage: React.FC = (_) => { - const { i18n, t } = useTranslation(); - const user = useUser(); - const history = useHistory(); - - const [dialog, setDialog] = useState< - null | "changepassword" | "changeavatar" | "changenickname" | "logout" - >(null); - - const language = i18n.language.slice(0, 2); - - return ( - <> - - {user ? ( -
-

- {t("settings.subheaders.account")} -

-
setDialog("changeavatar")} - > - {t("settings.changeAvatar")} -
-
setDialog("changenickname")} - > - {t("settings.changeNickname")} -
-
setDialog("changepassword")} - > - {t("settings.changePassword")} -
-
{ - setDialog("logout"); - }} - > - {t("settings.logout")} -
-
- ) : null} -
-

- {t("settings.subheaders.customization")} -

- - -
{t("settings.languagePrimary")}
- - {t("settings.languageSecondary")} - - - - { - void i18n.changeLanguage(e.target.value); - }} - > - - - - -
-
-
- {(() => { - switch (dialog) { - case "changepassword": - return setDialog(null)} />; - case "logout": - return ( - setDialog(null)} - onConfirm={() => { - void userService.logout().then(() => { - history.push("/"); - }); - }} - /> - ); - case "changeavatar": - return setDialog(null)} />; - case "changenickname": - return setDialog(null)} />; - default: - return null; - } - })()} - - ); -}; - -export default SettingsPage; diff --git a/FrontEnd/src/app/views/settings/settings.sass b/FrontEnd/src/app/views/settings/settings.sass deleted file mode 100644 index 8c6d24b8..00000000 --- a/FrontEnd/src/app/views/settings/settings.sass +++ /dev/null @@ -1,14 +0,0 @@ -.settings-item - padding: 0.5em 1em - transition: background 0.3s - border-bottom: 1px solid $gray-200 - - &.first - border-top: 1px solid $gray-200 - - &.clickable - cursor: pointer - - &:hover - background: $gray-300 - -- cgit v1.2.3