import { useState, useEffect, ChangeEvent, ComponentPropsWithoutRef, } from "react"; import { AxiosError } from "axios"; import { useC, Text, UiLogicError } from "@/common"; import { useUser } from "@/services/user"; import { getHttpUserClient } from "@/http/user"; import ImageCropper, { Clip, applyClipToImage, } from "@/views/common/ImageCropper"; import Button from "@/views/common/button/Button"; import ButtonRow from "@/views/common/button/ButtonRow"; import Dialog from "@/views/common/dialog/Dialog"; import DialogContainer from "@/views/common/dialog/DialogContainer"; interface ChangeAvatarDialogProps { open: boolean; onClose: () => void; } export default function ChangeAvatarDialog({ open, onClose, }: ChangeAvatarDialogProps) { const c = useC(); const user = useUser(); const [file, setFile] = useState(null); const [fileUrl, setFileUrl] = useState(null); const [clip, setClip] = useState(null); const [cropImgElement, setCropImgElement] = useState( null, ); const [resultBlob, setResultBlob] = useState(null); const [resultUrl, setResultUrl] = useState(null); type State = | "select" | "crop" | "process-crop" | "preview" | "uploading" | "success" | "error"; const [state, setState] = useState("select"); const [message, setMessage] = useState( "settings.dialogChangeAvatar.prompt.select", ); const trueMessage = c(message); const close = (): void => { if (!(state === "uploading")) { onClose(); } }; 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]); useEffect(() => { if (resultBlob != null) { const url = URL.createObjectURL(resultBlob); setResultUrl(url); setState("preview"); return () => { URL.revokeObjectURL(url); }; } else { setResultUrl(null); } }, [resultBlob]); const onSelectFile = (e: ChangeEvent): void => { const files = e.target.files; if (files == null || files.length === 0) { setFile(null); } else { setFile(files[0]); } }; const onCropNext = () => { if ( cropImgElement == null || clip == null || clip.width === 0 || file == null ) { throw new UiLogicError(); } setState("process-crop"); void applyClipToImage(cropImgElement, clip, file.type).then((b) => { setResultBlob(b); }); }; const onCropPrevious = () => { setFile(null); setState("select"); }; const onPreviewPrevious = () => { setResultBlob(null); setState("crop"); }; const upload = () => { 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 }); }, ); }; const createPreviewRow = (): React.ReactElement => { if (resultUrl == null) { throw new UiLogicError(); } return (
{c("settings.dialogChangeAvatar.previewImgAlt")
); }; const buttonsMap: Record< State, ComponentPropsWithoutRef["buttons"] > = { select: [ { key: "cancel", type: "normal", props: { outline: true, color: "secondary", text: "operationDialog.cancel", onClick: close, }, }, ], crop: [ { key: "cancel", type: "normal", props: { outline: true, color: "secondary", text: "operationDialog.cancel", onClick: close, }, }, { key: "previous", type: "normal", props: { outline: true, color: "secondary", text: "operationDialog.previousStep", onClick: onCropPrevious, }, }, { key: "next", type: "normal", props: { color: "primary", text: "operationDialog.nextStep", onClick: onCropNext, disabled: cropImgElement == null || clip == null || clip.width === 0, }, }, ], }; return ( {(() => { if (state === "select") { return (
{c("settings.dialogChangeAvatar.prompt.select")}
); } else if (state === "crop") { if (fileUrl == null) { throw new UiLogicError(); } return (
{c("settings.dialogChangeAvatar.prompt.crop")}
); } else if (state === "process-crop") { return ( <>
{c("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}

); } })()}
); }