frontAnswerer/lib/components/ViewPublicationPage/questions/File.tsx
2024-02-17 03:14:28 +03:00

289 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
Box,
ButtonBase,
IconButton,
Modal,
Skeleton,
Typography,
useTheme
} from "@mui/material";
import { updateAnswer, useQuizViewStore } from "@stores/quizView";
import CloseBold from "@icons/CloseBold";
import UploadIcon from "@icons/UploadIcon";
import { sendAnswer, sendFile } from "@api/quizRelase";
import { useQuizData } from "@contexts/QuizDataContext";
import Info from "@icons/Info";
import { enqueueSnackbar } from "notistack";
import { useState } from "react";
import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext";
import type { QuizQuestionFile } from "../../../model/questionTypes/file";
import { ACCEPT_SEND_FILE_TYPES_MAP, MAX_FILE_SIZE, UPLOAD_FILE_DESCRIPTIONS_MAP } from "../tools/fileUpload";
type ModalWarningType = "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | null;
type FileProps = {
currentQuestion: QuizQuestionFile;
};
export const File = ({ currentQuestion }: FileProps) => {
const theme = useTheme();
const { answers } = useQuizViewStore();
const { quizId } = useQuizData();
const [modalWarningType, setModalWarningType] = useState<ModalWarningType>(null);
const [isSending, setIsSending] = useState<boolean>(false);
const [isDropzoneHighlighted, setIsDropzoneHighlighted] = useState<boolean>(false);
const isMobile = useRootContainerSize() < 500;
const answer = answers.find(
({ questionId }) => questionId === currentQuestion.id
)?.answer as string;
const uploadFile = async (file: File | undefined) => {
if (isSending) return;
if (!file) return;
console.log(file.size)
console.log(MAX_FILE_SIZE)
if (file.size > MAX_FILE_SIZE) return setModalWarningType("errorSize");
const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].some(
fileType => file.name.toLowerCase().endsWith(fileType)
);
if (!isFileTypeAccepted) return setModalWarningType("errorType");
setIsSending(true);
try {
const data = await sendFile({
questionId: currentQuestion.id,
body: {
file: file,
name: file.name
},
qid: quizId,
});
console.log(data);
await sendAnswer({
questionId: currentQuestion.id,
body: `https://storage.yandexcloud.net/squizanswer/${quizId}/${currentQuestion.id}/${data.data.fileIDMap[currentQuestion.id]}`,
qid: quizId,
});
updateAnswer(currentQuestion.id, `${file.name}|${URL.createObjectURL(file)}`, 0);
} catch (e) {
console.log(e);
enqueueSnackbar("ответ не был засчитан");
}
setIsSending(false);
};
const onDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
setIsDropzoneHighlighted(false);
const file = event.dataTransfer.files[0];
uploadFile(file);
};
return (
<Box>
<Typography
variant="h5"
color={theme.palette.text.primary}
sx={{ wordBreak: "break-word" }}
>{currentQuestion.title}</Typography>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
marginTop: "20px",
maxWidth: answer?.split("|")[0] ? "640px" : "600px",
}}
>
{answer?.split("|")[0] ? (
<Box sx={{ display: "flex", alignItems: "center", gap: "15px" }}>
<Typography color={theme.palette.text.primary}>Вы загрузили:</Typography>
<Box
sx={{
padding: "5px 5px 5px 16px",
backgroundColor: theme.palette.primary.main,
borderRadius: "8px",
color: "#FFFFFF",
display: "flex",
alignItems: "center",
overflow: "hidden",
gap: "15px",
}}
>
<Typography
sx={{
whiteSpace: "nowrap",
textOverflow: "ellipsis",
overflow: "hidden",
}}
>
{answer?.split("|")[0]}</Typography>
<IconButton
sx={{ p: 0 }}
onClick={async () => {
if (answer.length > 0) {
setIsSending(true);
await sendAnswer({
questionId: currentQuestion.id,
body: "",
qid: quizId,
});
}
console.log(answer);
updateAnswer(currentQuestion.id, "", 0);
setIsSending(false);
}}
>
<CloseBold />
</IconButton>
</Box>
</Box>
) : (
<Box
sx={{
display: "flex",
alignItems: "center"
}}
>
{isSending ?
<Skeleton
variant="rounded"
sx={{
width: "100%",
height: "120px",
maxWidth: "560px",
}}
/>
:
<ButtonBase
component="label"
sx={{ justifyContent: "flex-start", width: "100%" }}
>
<input
onChange={e => uploadFile(e.target.files?.[0])}
hidden
accept={ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].join(",")}
multiple
type="file"
/>
<Box
onDragEnter={() => !answer?.split("|")[0] && setIsDropzoneHighlighted(true)}
onDragLeave={() => setIsDropzoneHighlighted(false)}
onDragOver={(e) => e.preventDefault()}
onDrop={onDrop}
sx={{
width: "100%",
height: isMobile ? undefined : "120px",
display: "flex",
gap: "50px",
justifyContent: "flex-start",
alignItems: "center",
padding: "33px 44px 33px 55px",
backgroundColor: theme.palette.background.default,
border: `1px solid ${isDropzoneHighlighted ? "red" : "#9A9AAF"}`,
borderRadius: "8px",
}}
>
<UploadIcon />
<Box>
<Typography
sx={{
color: "#9A9AAF",
// color: theme.palette.grey2.main,
fontWeight: 500,
}}
>
{UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type].title}
</Typography>
<Typography
sx={{
color: "#9A9AAF",
// color: theme.palette.grey2.main,
fontSize: "16px",
lineHeight: "19px",
}}
>
{UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type].description}
</Typography>
</Box>
</Box>
</ButtonBase>
}
<Info
sx={{ width: "40px", height: "40px" }}
color={theme.palette.primary.main}
onClick={() => setModalWarningType(currentQuestion.content.type)}
/>
</Box>
)}
{answer && currentQuestion.content.type === "picture" && (
<img
src={answer.split("|")[1]}
alt=""
style={{
marginTop: "15px",
maxWidth: "300px",
maxHeight: "300px",
}}
/>
)}
{answer && currentQuestion.content.type === "video" && (
<video
src={answer.split("|")[1]}
style={{
marginTop: "15px",
maxWidth: "300px",
maxHeight: "300px",
objectFit: "cover",
}}
/>
)}
</Box>
<Modal
open={modalWarningType !== null}
onClose={() => setModalWarningType(null)}
>
<Box sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: isMobile ? 300 : 400,
bgcolor: 'background.paper',
borderRadius: 3,
boxShadow: 24,
p: 4,
}}>
<CurrentModal status={modalWarningType} />
</Box>
</Modal>
</Box>
);
};
const CurrentModal = ({ status }: { status: ModalWarningType; }) => {
switch (status) {
case null: return null;
case 'errorType': return <Typography>Выбран некорректный тип файла</Typography>;
case 'errorSize': return <Typography>Файл слишком большой. Максимальный размер 50 МБ</Typography>;
default: return (
<>
<Typography>Допустимые расширения файлов:</Typography>
<Typography>{
ACCEPT_SEND_FILE_TYPES_MAP[status].join(" ")}</Typography>
</>
);
}
};