import { useState, useEffect } from "react"; import * as React from "react"; import { useTranslation } from "react-i18next"; import { AxiosError } from "axios"; import { convertI18nText, I18nText, UiLogicError } from "@/common"; import { useUser } from "@/services/user"; import { getHttpUserClient } from "@/http/user"; import ImageCropper, { Clip, applyClipToImage } from "../common/ImageCropper"; import Button from "../common/button/Button"; import Dialog from "../common/dailog/Dialog"; export interface ChangeAvatarDialogProps { open: boolean; close: () => void; } const ChangeAvatarDialog: React.FC = (props) => { const { t } = useTranslation(); const user = useUser(); 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( "settings.dialogChangeAvatar.prompt.select" ); const trueMessage = convertI18nText(message, t); 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(); } if (user == null) { throw new UiLogicError(); } setState("uploading"); getHttpUserClient() .putAvatar(user.username, resultBlob) .then( () => { setState("success"); }, (e: unknown) => { setState("error"); setMessage({ type: "custom", value: (e as AxiosError).message }); } ); }, [user, 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;