From de1d582bf2ed7062fd400459f30d463d47ef9982 Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 24 Aug 2020 22:59:45 +0800 Subject: ... --- .../ClientApp/src/app/user/ChangeAvatarDialog.tsx | 612 ++++++++++----------- 1 file changed, 306 insertions(+), 306 deletions(-) (limited to 'Timeline/ClientApp/src/app/user/ChangeAvatarDialog.tsx') diff --git a/Timeline/ClientApp/src/app/user/ChangeAvatarDialog.tsx b/Timeline/ClientApp/src/app/user/ChangeAvatarDialog.tsx index f7b25252..7d9f9514 100644 --- a/Timeline/ClientApp/src/app/user/ChangeAvatarDialog.tsx +++ b/Timeline/ClientApp/src/app/user/ChangeAvatarDialog.tsx @@ -1,306 +1,306 @@ -import React, { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { - Modal, - ModalHeader, - Row, - Button, - ModalBody, - ModalFooter, -} from 'reactstrap'; -import { AxiosError } from 'axios'; - -import ImageCropper, { Clip, applyClipToImage } from '../common/ImageCropper'; -import { UiLogicError } from '../common'; - -export interface ChangeAvatarDialogProps { - open: boolean; - close: () => void; - process: (blob: Blob) => Promise; -} - -const ChangeAvatarDialog: React.FC = (props) => { - const { t } = useTranslation(); - - 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 - >('userPage.dialogChangeAvatar.prompt.select'); - - const trueMessage = - message == null - ? null - : typeof message === 'string' - ? t(message) - : message.text; - - const closeDialog = props.close; - - const toggle = 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 process = props.process; - - const upload = React.useCallback(() => { - if (resultBlob == null) { - throw new UiLogicError(); - } - - setState('uploading'); - process(resultBlob).then( - () => { - setState('success'); - }, - (e: unknown) => { - setState('error'); - setMessage({ type: 'custom', text: (e as AxiosError).message }); - } - ); - }, [resultBlob, process]); - - const createPreviewRow = (): React.ReactElement => { - if (resultUrl == null) { - throw new UiLogicError(); - } - return ( - - {t('userPage.dialogChangeAvatar.previewImgAlt')} - - ); - }; - - return ( - - {t('userPage.dialogChangeAvatar.title')} - {(() => { - if (state === 'select') { - return ( - <> - - {t('userPage.dialogChangeAvatar.prompt.select')} - - - - - - - - - ); - } else if (state === 'crop') { - if (fileUrl == null) { - throw new UiLogicError(); - } - return ( - <> - - - - - {t('userPage.dialogChangeAvatar.prompt.crop')} - - - - - - - - ); - } else if (state === 'processcrop') { - return ( - <> - - - {t('userPage.dialogChangeAvatar.prompt.processingCrop')} - - - - - - - - ); - } else if (state === 'preview') { - return ( - <> - - {createPreviewRow()} - {t('userPage.dialogChangeAvatar.prompt.preview')} - - - - - - - - ); - } else if (state === 'uploading') { - return ( - <> - - {createPreviewRow()} - {t('userPage.dialogChangeAvatar.prompt.uploading')} - - - - ); - } else if (state === 'success') { - return ( - <> - - - {t('operationDialog.success')} - - - - - - - ); - } else { - return ( - <> - - {createPreviewRow()} - {trueMessage} - - - - - - - ); - } - })()} - - ); -}; - -export default ChangeAvatarDialog; +import React, { useState, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { + Modal, + ModalHeader, + Row, + Button, + ModalBody, + ModalFooter, +} from "reactstrap"; +import { AxiosError } from "axios"; + +import ImageCropper, { Clip, applyClipToImage } from "../common/ImageCropper"; +import { UiLogicError } from "../common"; + +export interface ChangeAvatarDialogProps { + open: boolean; + close: () => void; + process: (blob: Blob) => Promise; +} + +const ChangeAvatarDialog: React.FC = (props) => { + const { t } = useTranslation(); + + 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 + >("userPage.dialogChangeAvatar.prompt.select"); + + const trueMessage = + message == null + ? null + : typeof message === "string" + ? t(message) + : message.text; + + const closeDialog = props.close; + + const toggle = 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 process = props.process; + + const upload = React.useCallback(() => { + if (resultBlob == null) { + throw new UiLogicError(); + } + + setState("uploading"); + process(resultBlob).then( + () => { + setState("success"); + }, + (e: unknown) => { + setState("error"); + setMessage({ type: "custom", text: (e as AxiosError).message }); + } + ); + }, [resultBlob, process]); + + const createPreviewRow = (): React.ReactElement => { + if (resultUrl == null) { + throw new UiLogicError(); + } + return ( + + {t("userPage.dialogChangeAvatar.previewImgAlt")} + + ); + }; + + return ( + + {t("userPage.dialogChangeAvatar.title")} + {(() => { + if (state === "select") { + return ( + <> + + {t("userPage.dialogChangeAvatar.prompt.select")} + + + + + + + + + ); + } else if (state === "crop") { + if (fileUrl == null) { + throw new UiLogicError(); + } + return ( + <> + + + + + {t("userPage.dialogChangeAvatar.prompt.crop")} + + + + + + + + ); + } else if (state === "processcrop") { + return ( + <> + + + {t("userPage.dialogChangeAvatar.prompt.processingCrop")} + + + + + + + + ); + } else if (state === "preview") { + return ( + <> + + {createPreviewRow()} + {t("userPage.dialogChangeAvatar.prompt.preview")} + + + + + + + + ); + } else if (state === "uploading") { + return ( + <> + + {createPreviewRow()} + {t("userPage.dialogChangeAvatar.prompt.uploading")} + + + + ); + } else if (state === "success") { + return ( + <> + + + {t("operationDialog.success")} + + + + + + + ); + } else { + return ( + <> + + {createPreviewRow()} + {trueMessage} + + + + + + + ); + } + })()} + + ); +}; + +export default ChangeAvatarDialog; -- cgit v1.2.3