From 8e0d06697062a854ec34ad47d2b29719bcd7e438 Mon Sep 17 00:00:00 2001 From: Nastya Date: Sat, 31 May 2025 20:32:13 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BD=D0=B5=20=D1=81=D1=82=D0=B0=D0=B1=D0=B8?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D0=BE,=20=D0=B2=D0=B5=D1=80=D0=BD=D1=83?= =?UTF-8?q?=D0=BB=D0=B0=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=20100=20?= =?UTF-8?q?=D0=B2=D0=BE=D0=BF=D1=80=D0=BE=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/api/quizRelase.ts | 48 ++++++- .../questions/Images/ImageCard.tsx | 122 ++++++++++++++++++ .../questions/Images/ImageVariant.tsx | 78 +++-------- 3 files changed, 185 insertions(+), 63 deletions(-) create mode 100644 lib/components/ViewPublicationPage/questions/Images/ImageCard.tsx diff --git a/lib/api/quizRelase.ts b/lib/api/quizRelase.ts index f72ef9c..ac569fb 100644 --- a/lib/api/quizRelase.ts +++ b/lib/api/quizRelase.ts @@ -76,7 +76,52 @@ export const publicationMakeRequest = ({ url, body }: PublicationMakeRequestPara let globalStatus: string | null = null; let isFirstRequest = true; -export async function getData({ quizId, page }: { quizId: string; page?: number }): Promise<{ +export async function getData({ quizId }: { quizId: string }): Promise<{ + data: GetQuizDataResponse | null; + isRecentlyCompleted: boolean; + error?: AxiosError; +}> { + try { + const { data, headers } = await axios( + domain + `/answer/v1.0.0/settings${window.location.search}`, + { + method: "POST", + headers: { + "X-Sessionkey": SESSIONS, + "Content-Type": "application/json", + DeviceType: DeviceType, + Device: Device, + OS: OSDevice, + Browser: userAgent, + }, + data: { + quiz_id: quizId, + limit: 100, + page: 0, + need_config: true, + }, + } + ); + const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); + + //Тут ещё проверка на антифрод без парса конфига. Нам не интересно время если не нужно запрещать проходить чаще чем в сутки + if (typeof sessions[quizId] === "number" && data.settings.cfg.includes('antifraud":true')) { + // unix время. Если меньше суток прошло - выводить ошибку, иначе пустить дальше + if (Date.now() - sessions[quizId] < 86400000) { + return { data, isRecentlyCompleted: true }; + } + } + + SESSIONS = headers["x-sessionkey"] ? headers["x-sessionkey"] : SESSIONS; + + return { data, isRecentlyCompleted: false }; + } catch (nativeError) { + const error = nativeError as AxiosError; + + return { data: null, isRecentlyCompleted: false, error: error }; + } +} +export async function getDataSingle({ quizId, page }: { quizId: string; page?: number }): Promise<{ data: GetQuizDataResponse | null; isRecentlyCompleted: boolean; error?: AxiosError; @@ -179,6 +224,7 @@ export async function getData({ quizId, page }: { quizId: string; page?: number }; } } + export async function getQuizData({ quizId, status = "" }: { quizId: string; status?: string }) { if (!quizId) throw new Error("No quiz id"); diff --git a/lib/components/ViewPublicationPage/questions/Images/ImageCard.tsx b/lib/components/ViewPublicationPage/questions/Images/ImageCard.tsx new file mode 100644 index 0000000..dec273d --- /dev/null +++ b/lib/components/ViewPublicationPage/questions/Images/ImageCard.tsx @@ -0,0 +1,122 @@ +import { Box, ButtonBase, Typography, useTheme } from "@mui/material"; +import { useMemo, useState } from "react"; +import { useRootContainerSize } from "@contexts/RootContainerWidthContext"; +import { useTranslation } from "react-i18next"; +import { enqueueSnackbar } from "notistack"; +import { ACCEPT_SEND_FILE_TYPES_MAP } from "@/components/ViewPublicationPage/tools/fileUpload"; +import UploadIcon from "@icons/UploadIcon"; +import { uploadFile } from "@/utils/fileUpload"; +import { useQuizStore } from "@/stores/useQuizStore"; + +interface ImageCardProps { + questionId: string; + imageUrl: string; + isOwn?: boolean; + onImageUpload?: (fileUrl: string) => void; +} + +const useFileUpload = (questionId: string, onImageUpload?: (fileUrl: string) => void) => { + const { t } = useTranslation(); + const [isSending, setIsSending] = useState(false); + const [currentImageUrl, setCurrentImageUrl] = useState(null); + const { quizId, preview } = useQuizStore(); + + const handleFileUpload = async (file: File | undefined) => { + if (isSending || !file) return; + + const result = await uploadFile({ + file, + questionId, + quizId, + fileType: "picture", + preview, + onSuccess: (fileUrl) => { + setCurrentImageUrl(URL.createObjectURL(file)); + onImageUpload?.(fileUrl); + }, + onError: (error) => { + console.error(error); + enqueueSnackbar(t(error.message)); + }, + onProgress: () => { + setIsSending(true); + }, + }); + + setIsSending(false); + }; + + return { + isSending, + currentImageUrl, + handleFileUpload, + }; +}; + +export const ImageCard = ({ questionId, imageUrl, isOwn, onImageUpload }: ImageCardProps) => { + const theme = useTheme(); + const { t } = useTranslation(); + const isMobile = useRootContainerSize() < 450; + const isTablet = useRootContainerSize() < 850; + const [isDropzoneHighlighted, setIsDropzoneHighlighted] = useState(false); + const { currentImageUrl, handleFileUpload } = useFileUpload(questionId, onImageUpload); + + const onDrop = (event: React.DragEvent) => { + event.preventDefault(); + setIsDropzoneHighlighted(false); + + const file = event.dataTransfer.files[0]; + handleFileUpload(file); + }; + + return ( + + + {isOwn && ( + setIsDropzoneHighlighted(true)} + onDragLeave={() => setIsDropzoneHighlighted(false)} + onDragOver={(event) => event.preventDefault()} + onDrop={onDrop} + > + handleFileUpload(target.files?.[0])} + hidden + accept={ACCEPT_SEND_FILE_TYPES_MAP.picture.join(",")} + type="file" + /> + + + )} + + ); +}; diff --git a/lib/components/ViewPublicationPage/questions/Images/ImageVariant.tsx b/lib/components/ViewPublicationPage/questions/Images/ImageVariant.tsx index 2be97ba..ad17c2d 100644 --- a/lib/components/ViewPublicationPage/questions/Images/ImageVariant.tsx +++ b/lib/components/ViewPublicationPage/questions/Images/ImageVariant.tsx @@ -24,6 +24,7 @@ import { enqueueSnackbar } from "notistack"; import { ACCEPT_SEND_FILE_TYPES_MAP, MAX_FILE_SIZE } from "@/components/ViewPublicationPage/tools/fileUpload"; import UploadIcon from "@icons/UploadIcon"; import { uploadFile } from "@/utils/fileUpload"; +import { ImageCard } from "./ImageCard"; type ImagesProps = { questionId: string; @@ -37,12 +38,11 @@ type ImagesProps = { }; interface OwnInputProps { - questionId: string; variant: QuestionVariant; largeCheck: boolean; ownPlaceholder: string; } -const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputProps) => { +const OwnInput = ({ variant, largeCheck, ownPlaceholder }: OwnInputProps) => { const theme = useTheme(); const ownVariants = useQuizViewStore((state) => state.ownVariants); const { updateOwnVariant } = useQuizViewStore((state) => state); @@ -116,16 +116,16 @@ export const ImageVariant = ({ const isMobile = useRootContainerSize() < 450; const isTablet = useRootContainerSize() < 850; const [isSending, setIsSending] = useState(false); - const [isDropzoneHighlighted, setIsDropzoneHighlighted] = useState(false); + const { quizId, preview } = useQuizStore(); - const [imageUrl, setImageUrl] = useState(null); const canvasRef = useRef(null); - const containerCanvasRef = useRef(null); const onVariantClick = async (event: MouseEvent) => { event.preventDefault(); + if (own) return; + const variantId = variant.id; if (isMulti) { const currentAnswer = typeof answer !== "string" ? answer || [] : []; @@ -170,21 +170,13 @@ export const ImageVariant = ({ setIsSending(false); }; - const onDrop = (event: React.DragEvent) => { - event.preventDefault(); - setIsDropzoneHighlighted(false); - - const file = event.dataTransfer.files[0]; - handleFileUpload(file); - }; - const choiceImgUrl = useMemo(() => { if (variant.editedUrlImagesList !== undefined && variant.editedUrlImagesList !== null) { return variant.editedUrlImagesList[isMobile ? "mobile" : isTablet ? "tablet" : "desktop"]; } else { return variant.extendedText; } - }, []); + }, [variant.editedUrlImagesList, isMobile, isTablet, variant.extendedText]); useEffect(() => { if (canvasRef.current !== null) { @@ -207,11 +199,11 @@ export const ImageVariant = ({ - - {variant.extendedText && ( - <> - - {own && ( - - handleFileUpload(target.files?.[0])} - hidden - accept={ACCEPT_SEND_FILE_TYPES_MAP.picture.join(",")} - type="file" - /> - - - )} - - )} - + {variant.extendedText && ( + + )} {own && (