186 lines
5.5 KiB
TypeScript
186 lines
5.5 KiB
TypeScript
import { Box, ButtonBase, IconButton, Typography, useTheme } from "@mui/material";
|
|
import { useState, useRef } from "react";
|
|
import CloseIcon from "@mui/icons-material/Close";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useQuizStore } from "@/stores/useQuizStore";
|
|
import { useQuizViewStore } from "@/stores/quizView";
|
|
import { useSnackbar } from "notistack";
|
|
import { Skeleton } from "@mui/material";
|
|
import UploadIcon from "@/assets/icons/UploadIcon";
|
|
import { sendFile } from "@/api/quizRelase";
|
|
import { ACCEPT_SEND_FILE_TYPES_MAP, MAX_FILE_SIZE } from "../../tools/fileUpload";
|
|
|
|
// Пропсы компонента
|
|
export type OwnImageProps = {
|
|
imageUrl?: string;
|
|
questionId: string;
|
|
variantId: string;
|
|
onValidationError: (error: "size" | "type") => void;
|
|
};
|
|
|
|
export const OwnImage = ({ imageUrl, questionId, variantId, onValidationError }: OwnImageProps) => {
|
|
const theme = useTheme();
|
|
const { t } = useTranslation();
|
|
const { quizId, preview } = useQuizStore();
|
|
const { ownVariants, updateOwnVariant } = useQuizViewStore((state) => state);
|
|
const { enqueueSnackbar } = useSnackbar();
|
|
|
|
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
|
const [isUploading, setIsUploading] = useState(false);
|
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
// Получаем ownVariant для этого варианта
|
|
const ownVariantData = ownVariants.find((v) => v.id === variantId);
|
|
|
|
// Загрузка файла
|
|
const uploadImage = async (file: File) => {
|
|
if (isUploading) return;
|
|
if (!file) return;
|
|
if (file.size > MAX_FILE_SIZE) {
|
|
onValidationError("size");
|
|
return;
|
|
}
|
|
const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP.picture.some((fileType) =>
|
|
file.name.toLowerCase().endsWith(fileType)
|
|
);
|
|
if (!isFileTypeAccepted) {
|
|
onValidationError("type");
|
|
return;
|
|
}
|
|
setIsUploading(true);
|
|
try {
|
|
const data = await sendFile({
|
|
questionId,
|
|
body: { file, name: file.name, preview },
|
|
qid: quizId,
|
|
});
|
|
const fileId = data?.data.fileIDMap[questionId];
|
|
const localImageUrl = URL.createObjectURL(file);
|
|
updateOwnVariant(variantId, "", "", fileId, localImageUrl);
|
|
setSelectedFile(file);
|
|
} catch (error) {
|
|
console.error("Error uploading image:", error);
|
|
enqueueSnackbar(t("The answer was not counted"));
|
|
} finally {
|
|
setIsUploading(false);
|
|
}
|
|
};
|
|
|
|
// Обработчик выбора файла
|
|
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
const file = event.target.files?.[0];
|
|
if (file) {
|
|
uploadImage(file);
|
|
}
|
|
};
|
|
|
|
// Открытие диалога выбора файла
|
|
const handleClick = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
fileInputRef.current?.click();
|
|
};
|
|
|
|
// Удаление изображения
|
|
const handleRemoveImage = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
setSelectedFile(null);
|
|
updateOwnVariant(variantId, "", "", "", "");
|
|
};
|
|
|
|
// Определяем, что показывать
|
|
let imageToDisplay: string | null = null;
|
|
if (selectedFile) {
|
|
imageToDisplay = URL.createObjectURL(selectedFile);
|
|
} else if (ownVariantData?.variant.localImageUrl) {
|
|
imageToDisplay = ownVariantData.variant.localImageUrl;
|
|
} else if (imageUrl) {
|
|
imageToDisplay = imageUrl;
|
|
}
|
|
|
|
if (isUploading) {
|
|
return (
|
|
<Skeleton
|
|
variant="rounded"
|
|
sx={{ width: "100%", height: "100%", borderRadius: "12px" }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ButtonBase
|
|
component="div"
|
|
onClick={handleClick}
|
|
disabled={isUploading}
|
|
sx={{
|
|
width: "100%",
|
|
height: "100%",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
borderRadius: "12px",
|
|
transition: "border-color 0.3s, background-color 0.3s",
|
|
overflow: "hidden",
|
|
position: "relative",
|
|
opacity: isUploading ? 0.7 : 1,
|
|
}}
|
|
>
|
|
<input
|
|
type="file"
|
|
ref={fileInputRef}
|
|
onChange={handleFileChange}
|
|
accept={ACCEPT_SEND_FILE_TYPES_MAP.picture.join(",")}
|
|
hidden
|
|
/>
|
|
{imageToDisplay ? (
|
|
<>
|
|
<Box sx={{ width: "100%", height: "100%", position: "relative" }}>
|
|
<img
|
|
src={imageToDisplay}
|
|
alt="Preview"
|
|
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
|
/>
|
|
</Box>
|
|
<IconButton
|
|
onClick={handleRemoveImage}
|
|
sx={{
|
|
position: "absolute",
|
|
top: 8,
|
|
right: 8,
|
|
zIndex: 1,
|
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
color: "white",
|
|
height: "25px",
|
|
width: "25px",
|
|
"&:hover": {
|
|
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
|
},
|
|
}}
|
|
>
|
|
<CloseIcon />
|
|
</IconButton>
|
|
</>
|
|
) : (
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
opacity: 0.5,
|
|
}}
|
|
>
|
|
<UploadIcon />
|
|
<Typography
|
|
variant="body2"
|
|
color="text.secondary"
|
|
sx={{ p: 2, textAlign: "center" }}
|
|
>
|
|
{t("Add your image")}
|
|
</Typography>
|
|
</Box>
|
|
)}
|
|
</ButtonBase>
|
|
);
|
|
};
|