frontAnswerer/lib/components/ViewPublicationPage/questions/File.tsx

287 lines
12 KiB
TypeScript
Raw Normal View History

2023-12-16 14:55:56 +00:00
import {
2024-02-02 14:35:02 +00:00
Box,
ButtonBase,
IconButton,
Modal,
Skeleton,
Typography,
useTheme
2023-12-16 14:55:56 +00:00
} from "@mui/material";
import { updateAnswer, useQuizViewStore } from "@stores/quizView";
2023-12-16 14:55:56 +00:00
import CloseBold from "@icons/CloseBold";
import UploadIcon from "@icons/UploadIcon";
2023-12-16 14:55:56 +00:00
2024-01-23 18:00:06 +00:00
import { sendAnswer, sendFile } from "@api/quizRelase";
2024-02-16 11:19:14 +00:00
import { useQuizData } from "@contexts/QuizDataContext";
import Info from "@icons/Info";
import { enqueueSnackbar } from "notistack";
2024-02-16 11:19:14 +00:00
import { useState } from "react";
import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext";
import type { QuizQuestionFile } from "../../../model/questionTypes/file";
2024-02-16 11:19:14 +00:00
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;
2023-12-16 14:55:56 +00:00
type FileProps = {
2024-02-02 14:35:02 +00:00
currentQuestion: QuizQuestionFile;
2023-12-16 14:55:56 +00:00
};
export const File = ({ currentQuestion }: FileProps) => {
2024-02-02 14:35:02 +00:00
const theme = useTheme();
const { answers } = useQuizViewStore();
const { quizId } = useQuizData();
2024-02-16 11:19:14 +00:00
const [modalWarningType, setModalWarningType] = useState<ModalWarningType>(null);
const [isSending, setIsSending] = useState<boolean>(false);
const [isDropzoneHighlighted, setIsDropzoneHighlighted] = useState<boolean>(false);
const isMobile = useRootContainerSize() < 500;
2024-02-02 14:35:02 +00:00
const answer = answers.find(
({ questionId }) => questionId === currentQuestion.id
)?.answer as string;
2024-02-16 11:19:14 +00:00
const uploadFile = async (file: File | undefined) => {
if (isSending) return;
if (!file) return;
if (file.size > MAX_FILE_SIZE) return setModalWarningType("errorSize");
2024-02-16 11:19:14 +00:00
const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].some(
fileType => file.name.toLowerCase().endsWith(fileType)
);
2024-02-16 11:19:14 +00:00
if (!isFileTypeAccepted) return setModalWarningType("errorType");
2024-02-16 11:19:14 +00:00
setIsSending(true);
try {
const data = await sendFile({
questionId: currentQuestion.id,
body: {
file: file,
name: file.name
},
qid: quizId,
});
console.log(data);
2024-02-16 11:19:14 +00:00
await sendAnswer({
questionId: currentQuestion.id,
body: `https://storage.yandexcloud.net/squizanswer/${quizId}/${currentQuestion.id}/${data.data.fileIDMap[currentQuestion.id]}`,
qid: quizId,
});
2024-02-16 11:19:14 +00:00
updateAnswer(currentQuestion.id, `${file.name}|${URL.createObjectURL(file)}`, 0);
} catch (e) {
console.log(e);
enqueueSnackbar("ответ не был засчитан");
}
2024-02-16 11:19:14 +00:00
setIsSending(false);
};
2024-02-16 11:19:14 +00:00
const onDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
setIsDropzoneHighlighted(false);
2024-02-16 11:19:14 +00:00
const file = event.dataTransfer.files[0];
uploadFile(file);
2024-02-02 14:35:02 +00:00
};
2024-02-02 14:35:02 +00:00
return (
2024-02-16 11:19:14 +00:00
<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
2024-02-02 14:35:02 +00:00
sx={{
2024-02-16 11:19:14 +00:00
whiteSpace: "nowrap",
textOverflow: "ellipsis",
2024-02-02 14:35:02 +00:00
overflow: "hidden",
}}
>
2024-02-16 11:19:14 +00:00
{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>
2024-02-02 14:35:02 +00:00
</Box>
2024-02-16 11:19:14 +00:00
</Box>
) : (
<Box
sx={{
2024-02-02 14:35:02 +00:00
display: "flex",
alignItems: "center"
2024-02-16 11:19:14 +00:00
}}
>
{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
2024-02-02 14:35:02 +00:00
sx={{
2024-02-16 11:19:14 +00:00
color: "#9A9AAF",
// color: theme.palette.grey2.main,
fontWeight: 500,
2024-02-02 14:35:02 +00:00
}}
>
2024-02-16 11:19:14 +00:00
{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)}
2024-02-02 14:35:02 +00:00
/>
2024-02-16 11:19:14 +00:00
</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",
}}
/>
)}
2023-12-16 14:55:56 +00:00
</Box>
2024-02-02 14:35:02 +00:00
<Modal
2024-02-16 11:19:14 +00:00
open={modalWarningType !== null}
onClose={() => setModalWarningType(null)}
2024-02-02 14:35:02 +00:00
>
<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,
}}>
2024-02-16 11:19:14 +00:00
<CurrentModal status={modalWarningType} />
2024-02-02 14:35:02 +00:00
</Box>
</Modal>
2024-02-16 11:19:14 +00:00
</Box>
2024-02-02 14:35:02 +00:00
);
2024-02-16 11:19:14 +00:00
};
const CurrentModal = ({ status }: { status: ModalWarningType; }) => {
2023-12-29 00:58:19 +00:00
2024-02-16 11:19:14 +00:00
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>
</>
);
}
2023-12-16 14:55:56 +00:00
};