diff --git a/src/pages/Questions/AnswerDraggableList/AnswerItem.tsx b/src/pages/Questions/AnswerDraggableList/AnswerItem.tsx index 3b1bfd92..67dcba5a 100644 --- a/src/pages/Questions/AnswerDraggableList/AnswerItem.tsx +++ b/src/pages/Questions/AnswerDraggableList/AnswerItem.tsx @@ -1,29 +1,24 @@ -import { MessageIcon } from "@icons/messagIcon"; import { PointsIcon } from "@icons/questionsPage/PointsIcon"; import { DeleteIcon } from "@icons/questionsPage/deleteIcon"; -import { TextareaAutosize } from "@mui/base/TextareaAutosize"; import { Box, FormControl, IconButton, InputAdornment, - Popover, TextField as MuiTextField, + TextFieldProps, useMediaQuery, useTheme, - TextFieldProps, } from "@mui/material"; import { addQuestionVariant, deleteQuestionVariant, setQuestionVariantField, } from "@root/questions/actions"; +import { enqueueSnackbar } from "notistack"; import type { ChangeEvent, FC, KeyboardEvent, ReactNode } from "react"; -import { useState } from "react"; import { Draggable } from "react-beautiful-dnd"; import type { QuestionVariant } from "../../../model/questionTypes/shared"; -import { useDebouncedCallback } from "use-debounce"; -import { enqueueSnackbar } from "notistack"; const TextField = MuiTextField as unknown as FC; @@ -48,21 +43,6 @@ export const AnswerItem = ({ }: AnswerItemProps) => { const theme = useTheme(); const isTablet = useMediaQuery(theme.breakpoints.down(790)); - const [isOpen, setIsOpen] = useState(false); - const [anchorEl, setAnchorEl] = useState(null); - const [inputValue, setInputValue] = useState(variant.answer); - const setQuestionVariantAnswer = useDebouncedCallback((value) => { - setQuestionVariantField(questionId, variant.id, "answer", value); - }, 200); - - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - setIsOpen(true); - }; - - const handleClose = () => { - setIsOpen(false); - }; return ( @@ -80,16 +60,20 @@ export const AnswerItem = ({ }} > ) => { if (target.value.length <= 1000) { - setInputValue(target.value); + setQuestionVariantField( + questionId, + variant.id, + "answer", + target.value || " ", + ); } - setQuestionVariantAnswer(target.value || " "); }} onKeyDown={(event: KeyboardEvent) => { if (disableKeyDown) { diff --git a/src/pages/Questions/ButtonsOptions.tsx b/src/pages/Questions/ButtonsOptions.tsx index dd91ec0d..615e4be1 100644 --- a/src/pages/Questions/ButtonsOptions.tsx +++ b/src/pages/Questions/ButtonsOptions.tsx @@ -1,6 +1,3 @@ -import { DoubleArrowRight } from "@icons/questionsPage/DoubleArrowRight"; -import { DoubleTick } from "@icons/questionsPage/DoubleTick"; -import { VectorQuestions } from "@icons/questionsPage/VectorQuestions"; import { DeleteIcon } from "@icons/questionsPage/deleteIcon"; import type { SxProps } from "@mui/material"; import { @@ -8,7 +5,6 @@ import { Button, IconButton, Modal, - Tooltip, Typography, useMediaQuery, useTheme, @@ -17,27 +13,17 @@ import { copyQuestion, deleteQuestion, deleteQuestionWithTimeout, - clearRuleForAll, - updateQuestion, - getQuestionByContentId, } from "@root/questions/actions"; +import { useQuestionsStore } from "@root/questions/store"; +import { useCurrentQuiz } from "@root/quizes/hooks"; import { updateDesireToOpenABranchingModal } from "@root/uiTools/actions"; import MiniButtonSetting from "@ui_kit/MiniButtonSetting"; +import { DeleteFunction } from "@utils/deleteFunc"; +import { useState } from "react"; import { CopyIcon } from "../../assets/icons/questionsPage/CopyIcon"; import Branching from "../../assets/icons/questionsPage/branching"; -import Clue from "../../assets/icons/questionsPage/clue"; -import { HideIcon } from "../../assets/icons/questionsPage/hideIcon"; import SettingIcon from "../../assets/icons/questionsPage/settingIcon"; import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared"; -import { useCurrentQuiz } from "@root/quizes/hooks"; -import { enqueueSnackbar } from "notistack"; -import { useQuestionsStore } from "@root/questions/store"; -import { updateOpenedModalSettingsId } from "@root/uiTools/actions"; -import { updateRootContentId } from "@root/quizes/actions"; -import { useUiTools } from "@root/uiTools/store"; -import { useState } from "react"; -import { updateSomeWorkBackend } from "@root/uiTools/actions"; -import { DeleteFunction } from "@utils/deleteFunc"; interface Props { switchState: string; @@ -306,7 +292,7 @@ export default function ButtonsOptions({ setOpenDelete(true); } else { deleteQuestionWithTimeout(question.id, () => - DeleteFunction(questions, question, quiz), + DeleteFunction(question), ); } }} @@ -351,7 +337,7 @@ export default function ButtonsOptions({ sx={{ minWidth: "150px" }} onClick={() => { deleteQuestionWithTimeout(question.id, () => - DeleteFunction(questions, question, quiz), + DeleteFunction(question), ); }} > diff --git a/src/pages/Questions/ButtonsOptions/ButtonsOptions.tsx b/src/pages/Questions/ButtonsOptions/ButtonsOptions.tsx index 88501fdc..4d3c7fec 100644 --- a/src/pages/Questions/ButtonsOptions/ButtonsOptions.tsx +++ b/src/pages/Questions/ButtonsOptions/ButtonsOptions.tsx @@ -1,6 +1,3 @@ -import { DoubleArrowRight } from "@icons/questionsPage/DoubleArrowRight"; -import { DoubleTick } from "@icons/questionsPage/DoubleTick"; -import { VectorQuestions } from "@icons/questionsPage/VectorQuestions"; import { DeleteIcon } from "@icons/questionsPage/deleteIcon"; import type { SxProps } from "@mui/material"; import { @@ -8,7 +5,6 @@ import { Button, IconButton, Modal, - Tooltip, Typography, useMediaQuery, useTheme, @@ -17,27 +13,17 @@ import { copyQuestion, deleteQuestion, deleteQuestionWithTimeout, - clearRuleForAll, - updateQuestion, - getQuestionByContentId, } from "@root/questions/actions"; +import { useQuestionsStore } from "@root/questions/store"; +import { useCurrentQuiz } from "@root/quizes/hooks"; import { updateDesireToOpenABranchingModal } from "@root/uiTools/actions"; import MiniButtonSetting from "@ui_kit/MiniButtonSetting"; -import { CopyIcon } from "../../assets/icons/questionsPage/CopyIcon"; -import Branching from "../../assets/icons/questionsPage/branching"; -import Clue from "../../assets/icons/questionsPage/clue"; -import { HideIcon } from "../../assets/icons/questionsPage/hideIcon"; -import SettingIcon from "../../assets/icons/questionsPage/settingIcon"; -import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared"; -import { useCurrentQuiz } from "@root/quizes/hooks"; -import { enqueueSnackbar } from "notistack"; -import { useQuestionsStore } from "@root/questions/store"; -import { updateOpenedModalSettingsId } from "@root/uiTools/actions"; -import { updateRootContentId } from "@root/quizes/actions"; -import { useUiTools } from "@root/uiTools/store"; -import { useState } from "react"; -import { updateSomeWorkBackend } from "@root/uiTools/actions"; import { DeleteFunction } from "@utils/deleteFunc"; +import { useState } from "react"; +import { CopyIcon } from "../../../assets/icons/questionsPage/CopyIcon"; +import Branching from "../../../assets/icons/questionsPage/branching"; +import SettingIcon from "../../../assets/icons/questionsPage/settingIcon"; +import type { AnyTypedQuizQuestion } from "../../../model/questionTypes/shared"; interface Props { switchState: string; @@ -213,7 +199,7 @@ export default function ButtonsOptions({ setOpenDelete(true); } else { deleteQuestionWithTimeout(question.id, () => - DeleteFunction(questions, question, quiz), + DeleteFunction(question), ); } }} @@ -258,7 +244,7 @@ export default function ButtonsOptions({ sx={{ minWidth: "150px" }} onClick={() => { deleteQuestionWithTimeout(question.id, () => - DeleteFunction(questions, question, quiz), + DeleteFunction(question), ); }} > diff --git a/src/pages/Questions/ButtonsOptionsAndPict.tsx b/src/pages/Questions/ButtonsOptionsAndPict.tsx index e0157b8d..566fcb0b 100644 --- a/src/pages/Questions/ButtonsOptionsAndPict.tsx +++ b/src/pages/Questions/ButtonsOptionsAndPict.tsx @@ -1,13 +1,10 @@ -import { DoubleArrowRight } from "@icons/questionsPage/DoubleArrowRight"; -import { DoubleTick } from "@icons/questionsPage/DoubleTick"; -import { VectorQuestions } from "@icons/questionsPage/VectorQuestions"; +import { QuizQuestionVariant } from "@model/questionTypes/variant"; import { QuizQuestionVarImg } from "@model/questionTypes/varimg"; import { Box, Button, IconButton, Modal, - Tooltip, Typography, useMediaQuery, useTheme, @@ -15,31 +12,20 @@ import { import { copyQuestion, deleteQuestion, - updateQuestion, - clearRuleForAll, - getQuestionByContentId, deleteQuestionWithTimeout, } from "@root/questions/actions"; +import { useQuestionsStore } from "@root/questions/store"; +import { useCurrentQuiz } from "@root/quizes/hooks"; +import { updateDesireToOpenABranchingModal } from "@root/uiTools/actions"; import MiniButtonSetting from "@ui_kit/MiniButtonSetting"; import { ReallyChangingModal } from "@ui_kit/Modal/ReallyChangingModal/ReallyChangingModal"; +import { DeleteFunction } from "@utils/deleteFunc"; import { useEffect, useState } from "react"; import { CopyIcon } from "../../assets/icons/questionsPage/CopyIcon"; import Branching from "../../assets/icons/questionsPage/branching"; -import Clue from "../../assets/icons/questionsPage/clue"; import { DeleteIcon } from "../../assets/icons/questionsPage/deleteIcon"; -import { HideIcon } from "../../assets/icons/questionsPage/hideIcon"; import ImgIcon from "../../assets/icons/questionsPage/imgIcon"; import SettingIcon from "../../assets/icons/questionsPage/settingIcon"; -import { QuizQuestionVariant } from "@model/questionTypes/variant"; -import { updateOpenedModalSettingsId } from "@root/questions/actions"; -import { updateDesireToOpenABranchingModal } from "@root/uiTools/actions"; -import { useQuestionsStore } from "@root/questions/store"; -import { enqueueSnackbar } from "notistack"; -import { useCurrentQuiz } from "@root/quizes/hooks"; -import { updateRootContentId } from "@root/quizes/actions"; -import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; -import { updateSomeWorkBackend } from "@root/uiTools/actions"; -import { DeleteFunction } from "@utils/deleteFunc"; interface Props { switchState: string; @@ -337,7 +323,7 @@ export default function ButtonsOptionsAndPict({ setOpenDelete(true); } else { deleteQuestionWithTimeout(question.id, () => - DeleteFunction(questions, question, quiz), + DeleteFunction(question), ); } }} @@ -382,7 +368,7 @@ export default function ButtonsOptionsAndPict({ sx={{ minWidth: "150px" }} onClick={() => { deleteQuestionWithTimeout(question.id, () => - DeleteFunction(questions, question, quiz), + DeleteFunction(question), ); }} > diff --git a/src/pages/Questions/DataOptions/settingData.tsx b/src/pages/Questions/DataOptions/settingData.tsx index 2ac9b07a..dfb7382f 100644 --- a/src/pages/Questions/DataOptions/settingData.tsx +++ b/src/pages/Questions/DataOptions/settingData.tsx @@ -1,15 +1,6 @@ -import { - Box, - Tooltip, - Typography, - useMediaQuery, - useTheme, -} from "@mui/material"; -import { setQuestionInnerName, updateQuestion } from "@root/questions/actions"; +import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { updateQuestion } from "@root/questions/actions"; import CustomCheckbox from "@ui_kit/CustomCheckbox"; -import CustomTextField from "@ui_kit/CustomTextField"; -import { useDebouncedCallback } from "use-debounce"; -import InfoIcon from "../../../assets/icons/InfoIcon"; import type { QuizQuestionDate } from "../../../model/questionTypes/date"; type SettingsDataProps = { @@ -22,10 +13,6 @@ export default function SettingsData({ question }: SettingsDataProps) { const isMobile = useMediaQuery(theme.breakpoints.down(790)); const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990)); - const setInnerName = useDebouncedCallback((value) => { - setQuestionInnerName(question.id, value); - }, 200); - return ( state.editSomeQuestion); useEffect(() => { let counter = 0; diff --git a/src/pages/Questions/DraggableList/QuestionPageCard.tsx b/src/pages/Questions/DraggableList/QuestionPageCard.tsx index 076f1592..ed12631f 100644 --- a/src/pages/Questions/DraggableList/QuestionPageCard.tsx +++ b/src/pages/Questions/DraggableList/QuestionPageCard.tsx @@ -13,6 +13,7 @@ import OptionsPict from "@icons/questionsPage/options_pict"; import Page from "@icons/questionsPage/page"; import RatingIcon from "@icons/questionsPage/rating"; import Slider from "@icons/questionsPage/slider"; +import { QuestionType } from "@model/question/question"; import ExpandLessIcon from "@mui/icons-material/ExpandLess"; import { Box, @@ -21,8 +22,9 @@ import { IconButton, InputAdornment, Modal, + TextField as MuiTextField, Paper, - TextField, + TextFieldProps, Typography, useMediaQuery, useTheme, @@ -31,26 +33,24 @@ import { copyQuestion, createUntypedQuestion, deleteQuestion, + deleteQuestionWithTimeout, toggleExpandQuestion, updateQuestion, updateUntypedQuestion, - deleteQuestionWithTimeout, } from "@root/questions/actions"; -import { useRef, useState } from "react"; +import { DeleteFunction } from "@utils/deleteFunc"; +import { FC, useRef, useState } from "react"; import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd"; -import { useDebouncedCallback } from "use-debounce"; import { ReactComponent as PlusIcon } from "../../../assets/icons/plus.svg"; import type { AnyTypedQuizQuestion, UntypedQuizQuestion, } from "../../../model/questionTypes/shared"; import SwitchQuestionsPage from "../SwitchQuestionsPage"; -import { ChooseAnswerModal } from "./ChooseAnswerModal"; import TypeQuestions from "../TypeQuestions"; -import { QuestionType } from "@model/question/question"; -import { useCurrentQuiz } from "@root/quizes/hooks"; -import { useQuestionsStore } from "@root/questions/store"; -import { DeleteFunction } from "@utils/deleteFunc"; +import { ChooseAnswerModal } from "./ChooseAnswerModal"; + +const TextField = MuiTextField as unknown as FC; interface Props { question: AnyTypedQuizQuestion | UntypedQuizQuestion; @@ -71,7 +71,6 @@ export default function QuestionsPageCard({ }: Props) { const maxLengthTextField = 225; - const { questions } = useQuestionsStore(); const [plusVisible, setPlusVisible] = useState(false); const [open, setOpen] = useState(false); @@ -82,16 +81,15 @@ export default function QuestionsPageCard({ const isTablet = useMediaQuery(theme.breakpoints.down(1000)); const isMobile = useMediaQuery(theme.breakpoints.down(790)); const anchorRef = useRef(null); - const quiz = useCurrentQuiz(); - const setTitle = useDebouncedCallback((title) => { + const setTitle = (title: string) => { const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion; updateQuestionFn(question.id, (question) => { question.title = title; }); - }, 200); + }; const handleInputFocus = () => { setIsTextFieldtActive(true); @@ -134,11 +132,9 @@ export default function QuestionsPageCard({ > - setTitle(target.value || " ") - } + onChange={({ target }) => setTitle(target.value || " ")} onFocus={handleInputFocus} onBlur={handleInputBlur} inputProps={{ @@ -303,7 +299,7 @@ export default function QuestionsPageCard({ setOpenDelete(true); } else { deleteQuestionWithTimeout(question.id, () => - DeleteFunction(questions, question, quiz), + DeleteFunction(question), ); } }} @@ -350,7 +346,7 @@ export default function QuestionsPageCard({ sx={{ minWidth: "150px" }} onClick={() => { deleteQuestionWithTimeout(question.id, () => - DeleteFunction(questions, question, quiz), + DeleteFunction(question), ); }} > diff --git a/src/pages/Questions/DropDown/settingDropDown.tsx b/src/pages/Questions/DropDown/settingDropDown.tsx index da78bcdd..ca5dd583 100644 --- a/src/pages/Questions/DropDown/settingDropDown.tsx +++ b/src/pages/Questions/DropDown/settingDropDown.tsx @@ -1,15 +1,7 @@ -import { - Box, - Tooltip, - Typography, - useMediaQuery, - useTheme, -} from "@mui/material"; -import { setQuestionInnerName, updateQuestion } from "@root/questions/actions"; +import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { updateQuestion } from "@root/questions/actions"; import CustomCheckbox from "@ui_kit/CustomCheckbox"; import CustomTextField from "@ui_kit/CustomTextField"; -import { useDebouncedCallback } from "use-debounce"; -import InfoIcon from "../../../assets/icons/InfoIcon"; import type { QuizQuestionSelect } from "../../../model/questionTypes/select"; type SettingDropDownProps = { @@ -21,17 +13,13 @@ export default function SettingDropDown({ question }: SettingDropDownProps) { const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990)); const isMobile = useMediaQuery(theme.breakpoints.down(790)); - const debounced = useDebouncedCallback((value) => { - setQuestionInnerName(question.id, value); - }, 200); - - const debounceAnswer = useDebouncedCallback((value) => { + const setContentDefault = (value: string) => { updateQuestion(question.id, (question) => { if (question.type !== "select") return; question.content.default = value; }); - }, 200); + }; return ( <> @@ -98,9 +86,9 @@ export default function SettingDropDown({ question }: SettingDropDownProps) { debounceAnswer(target.value)} + onChange={({ target }) => setContentDefault(target.value)} /> @@ -178,7 +166,7 @@ export default function SettingDropDown({ question }: SettingDropDownProps) { debounceAnswer(target.value)} + onChange={({ target }) => setContentDefault(target.value)} /> {/*{question.content.innerNameCheck && (*/} diff --git a/src/pages/Questions/Emoji/settingEmoji.tsx b/src/pages/Questions/Emoji/settingEmoji.tsx index 24de22c9..0fa8f138 100644 --- a/src/pages/Questions/Emoji/settingEmoji.tsx +++ b/src/pages/Questions/Emoji/settingEmoji.tsx @@ -1,15 +1,6 @@ -import { - Box, - Tooltip, - Typography, - useMediaQuery, - useTheme, -} from "@mui/material"; -import { setQuestionInnerName, updateQuestion } from "@root/questions/actions"; +import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { updateQuestion } from "@root/questions/actions"; import CustomCheckbox from "@ui_kit/CustomCheckbox"; -import CustomTextField from "@ui_kit/CustomTextField"; -import { useDebouncedCallback } from "use-debounce"; -import InfoIcon from "../../../assets/icons/InfoIcon"; import type { QuizQuestionEmoji } from "../../../model/questionTypes/emoji"; type SettingEmojiProps = { @@ -19,14 +10,9 @@ type SettingEmojiProps = { export default function SettingEmoji({ question }: SettingEmojiProps) { const theme = useTheme(); const isWrappColumn = useMediaQuery(theme.breakpoints.down(980)); - const isTablet = useMediaQuery(theme.breakpoints.down(985)); const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990)); const isMobile = useMediaQuery(theme.breakpoints.down(790)); - const setInnerName = useDebouncedCallback((value) => { - setQuestionInnerName(question.id, value); - }, 200); - return ( ; + interface Props { question: AnyTypedQuizQuestion | UntypedQuizQuestion; questionIndex: number; @@ -74,14 +77,14 @@ export default function QuestionsPageCard({ const isTablet = useMediaQuery(theme.breakpoints.down(1000)); const isMobile = useMediaQuery(theme.breakpoints.down(790)); - const setTitle = useDebouncedCallback((title) => { + const setTitle = (title: string) => { const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion; updateQuestionFn(question.id, (question) => { question.title = title; }); - }, 200); + }; const handleInputFocus = () => { setIsTextFieldtActive(true); @@ -134,10 +137,9 @@ export default function QuestionsPageCard({ > { - if ((target.value, toString().length <= 225)) - setTitle(target.value); + if (target.value.length <= 225) setTitle(target.value); }} onFocus={handleInputFocus} onBlur={handleInputBlur} diff --git a/src/pages/Questions/OptionsAndPicture/SettingOptionsAndPict.tsx b/src/pages/Questions/OptionsAndPicture/SettingOptionsAndPict.tsx index 31e77cb5..1b3e432d 100644 --- a/src/pages/Questions/OptionsAndPicture/SettingOptionsAndPict.tsx +++ b/src/pages/Questions/OptionsAndPicture/SettingOptionsAndPict.tsx @@ -1,15 +1,7 @@ -import { - Box, - Tooltip, - Typography, - useMediaQuery, - useTheme, -} from "@mui/material"; -import { setQuestionInnerName, updateQuestion } from "@root/questions/actions"; +import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { updateQuestion } from "@root/questions/actions"; import CustomCheckbox from "@ui_kit/CustomCheckbox"; import CustomTextField from "@ui_kit/CustomTextField"; -import { useDebouncedCallback } from "use-debounce"; -import InfoIcon from "../../../assets/icons/InfoIcon"; import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg"; type SettingOptionsAndPictProps = { @@ -24,17 +16,13 @@ export default function SettingOptionsAndPict({ const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990)); const isMobile = useMediaQuery(theme.breakpoints.down(680)); - const setReplText = useDebouncedCallback((replText) => { + const setReplText = (replText: string) => { updateQuestion(question.id, (question) => { if (question.type !== "varimg") return; question.content.replText = replText; }); - }, 200); - - const setDescription = useDebouncedCallback((value) => { - setQuestionInnerName(question.id, value); - }, 200); + }; return ( <> diff --git a/src/pages/Questions/OptionsPicture/settingOpytionsPict.tsx b/src/pages/Questions/OptionsPicture/settingOpytionsPict.tsx index 4a7c04b6..fb21f1c1 100644 --- a/src/pages/Questions/OptionsPicture/settingOpytionsPict.tsx +++ b/src/pages/Questions/OptionsPicture/settingOpytionsPict.tsx @@ -1,18 +1,12 @@ import { Box, Button, - Tooltip, Typography, useMediaQuery, useTheme, } from "@mui/material"; -import { setQuestionInnerName, updateQuestion } from "@root/questions/actions"; +import { updateQuestion } from "@root/questions/actions"; import CustomCheckbox from "@ui_kit/CustomCheckbox"; -import CustomTextField from "@ui_kit/CustomTextField"; -import { useDebouncedCallback } from "use-debounce"; -import InfoIcon from "../../../assets/icons/InfoIcon"; -import FormatIcon1 from "../../../assets/icons/questionsPage/FormatIcon1"; -import FormatIcon2 from "../../../assets/icons/questionsPage/FormatIcon2"; import ProportionsIcon11 from "../../../assets/icons/questionsPage/ProportionsIcon11"; import ProportionsIcon12 from "../../../assets/icons/questionsPage/ProportionsIcon12"; import ProportionsIcon21 from "../../../assets/icons/questionsPage/ProportionsIcon21"; @@ -43,18 +37,6 @@ export default function SettingOpytionsPict({ const isMobile = useMediaQuery(theme.breakpoints.down(790)); const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990)); - const debounced = useDebouncedCallback((value) => { - setQuestionInnerName(question.id, value); - }, 200); - - const updateProportions = (proportions: Proportion) => { - updateQuestion(question.id, (question) => { - if (question.type !== "images") return; - - question.content.xy = proportions; - }); - }; - return ( <> { + const setPlaceholder = (value: string) => { updateQuestion(question.id, (question) => { if (question.type !== "text") return; question.content.placeholder = value; }); - }, 200); + }; const SSHC = (data: string) => { setSwitchState(data); @@ -60,7 +58,7 @@ export default function OwnTextField({ > setPlaceholder(target.value)} sx={{ maxWidth: isFigmaTablte ? "549px" : "640px", diff --git a/src/pages/Questions/OwnTextField/settingTextField.tsx b/src/pages/Questions/OwnTextField/settingTextField.tsx index 9f4b5960..42686c6c 100644 --- a/src/pages/Questions/OwnTextField/settingTextField.tsx +++ b/src/pages/Questions/OwnTextField/settingTextField.tsx @@ -1,21 +1,6 @@ -import { - Box, - FormControl, - FormControlLabel, - Radio, - RadioGroup, - Tooltip, - Typography, - useMediaQuery, - useTheme, -} from "@mui/material"; -import { setQuestionInnerName, updateQuestion } from "@root/questions/actions"; +import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { updateQuestion } from "@root/questions/actions"; import CustomCheckbox from "@ui_kit/CustomCheckbox"; -import CustomTextField from "@ui_kit/CustomTextField"; -import CheckedIcon from "@ui_kit/RadioCheck"; -import CheckIcon from "@ui_kit/RadioIcon"; -import { useDebouncedCallback } from "use-debounce"; -import InfoIcon from "../../../assets/icons/InfoIcon"; import type { QuizQuestionText } from "../../../model/questionTypes/text"; type SettingTextFieldProps = { @@ -38,10 +23,6 @@ export default function SettingTextField({ question }: SettingTextFieldProps) { const isMobile = useMediaQuery(theme.breakpoints.down(790)); const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990)); - const debounced = useDebouncedCallback((value) => { - setQuestionInnerName(question.id, value); - }, 200); - return ( (false); - const setText = useDebouncedCallback((value) => { + const setText = (value: string) => { updateQuestion(question.id, (question) => { if (question.type !== "page") return; question.content.text = value; }); - }, 200); - - const quiz = useCurrentQuiz(); - const { questions } = useQuestionsStore.getState(); - const [openDelete, setOpenDelete] = useState(false); - - const openedModal = () => { - updateDesireToOpenABranchingModal(question.content.id); - }; - - const SSHC = (data: string) => { - setSwitchState(data); }; return ( @@ -82,7 +64,7 @@ export default function PageOptions({ disableInput, question }: Props) { setText(target.value)} maxLength={50} /> @@ -126,7 +108,7 @@ export default function PageOptions({ disableInput, question }: Props) { setOpenDelete(true); } else { deleteQuestionWithTimeout(question.id, () => - DeleteFunction(questions, question, quiz), + DeleteFunction(question), ); } }} @@ -171,7 +153,7 @@ export default function PageOptions({ disableInput, question }: Props) { sx={{ minWidth: "150px" }} onClick={() => { deleteQuestionWithTimeout(question.id, () => - DeleteFunction(questions, question, quiz), + DeleteFunction(question), ); }} > diff --git a/src/pages/Questions/PageOptions/SettingPageOptions.tsx b/src/pages/Questions/PageOptions/SettingPageOptions.tsx index 3b374395..a5cb8233 100644 --- a/src/pages/Questions/PageOptions/SettingPageOptions.tsx +++ b/src/pages/Questions/PageOptions/SettingPageOptions.tsx @@ -1,16 +1,5 @@ -import { - Box, - Tooltip, - Typography, - useMediaQuery, - useTheme, -} from "@mui/material"; -import CustomCheckbox from "@ui_kit/CustomCheckbox"; -import CustomTextField from "@ui_kit/CustomTextField"; -import { useDebouncedCallback } from "use-debounce"; -import InfoIcon from "../../../assets/icons/InfoIcon"; +import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; import type { QuizQuestionPage } from "../../../model/questionTypes/page"; -import { setQuestionInnerName, updateQuestion } from "@root/questions/actions"; type SettingPageOptionsProps = { question: QuizQuestionPage; @@ -22,10 +11,6 @@ export default function SettingPageOptions({ const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(790)); - const setInnerName = useDebouncedCallback((value) => { - setQuestionInnerName(question.id, value); - }, 200); - return ( { - const { questions } = useQuestionsStore.getState(); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(600)); - const quiz = useCurrentQuiz(); - const openBranchingPageHC = () => { + const openBranchingPageHC = useCallback(() => { if (!openBranchingPage) { - deleteTimeoutedQuestions(questions, quiz); + deleteTimeoutedQuestions(); } setOpenBranchingPage(!openBranchingPage); - }; + }, [openBranchingPage, setOpenBranchingPage]); return ( ; interface Props { question: QuizQuestionRating; @@ -37,43 +38,36 @@ export default function RatingOptions({ setOpenBranchingPage, }: Props) { const [switchState, setSwitchState] = useState("setting"); - const [negativeText, setNegativeText] = useState(""); - const [positiveText, setPositiveText] = useState(""); const [negativeTextWidth, setNegativeTextWidth] = useState(0); const [positiveTextWidth, setPositiveTextWidth] = useState(0); - const quizId = Number(useParams().quizId); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(790)); const negativeRef = useRef(null); const positiveRef = useRef(null); - const debounceNegativeDescription = useDebouncedCallback((value) => { + const setNegativeDescription = (value: string) => { updateQuestion(question.id, (question) => { if (question.type !== "rating") return; question.content.ratingNegativeDescription = value.substring(0, 15); }); - }, 200); - const debouncePositiveDescription = useDebouncedCallback((value) => { + }; + + const setPositiveDescription = (value: string) => { updateQuestion(question.id, (question) => { if (question.type !== "rating") return; question.content.ratingPositiveDescription = value.substring(0, 15); }); - }, 200); + }; - useEffect(() => { - setNegativeText(question.content.ratingNegativeDescription); - setPositiveText(question.content.ratingPositiveDescription); - }, []); - - useEffect(() => { + useLayoutEffect(() => { setNegativeTextWidth(negativeRef.current?.offsetWidth || 0); - }, [negativeText]); + }, [question.content.ratingNegativeDescription]); - useEffect(() => { + useLayoutEffect(() => { setPositiveTextWidth(positiveRef.current?.offsetWidth || 0); - }, [positiveText]); + }, [question.content.ratingPositiveDescription]); const buttonRatingForm: ButtonRatingFrom[] = [ { @@ -221,21 +215,17 @@ export default function RatingOptions({ fontSize: "16px", }} > - {negativeText} + {question.content.ratingNegativeDescription} { + onChange={({ target }) => { if (target.value.length <= 15) { - setNegativeText(target.value); - debounceNegativeDescription(target.value); + setNegativeDescription(target.value); } }} - onBlur={({ target }: { target: HTMLInputElement }) => - debounceNegativeDescription(target.value) - } + onBlur={({ target }) => setNegativeDescription(target.value)} sx={{ width: negativeTextWidth + 10 + "px", maxWidth: isMobile ? "140px" : "230px", @@ -279,20 +269,17 @@ export default function RatingOptions({ fontSize: "16px", }} > - {positiveText} + {question.content.ratingPositiveDescription} { + onChange={({ target }) => { if (target.value.length <= 15) { - setPositiveText(target.value); - debouncePositiveDescription(target.value); + setPositiveDescription(target.value); } }} - onBlur={({ target }: { target: HTMLInputElement }) => - debouncePositiveDescription(target.value) - } + onBlur={({ target }) => setPositiveDescription(target.value)} sx={{ width: positiveTextWidth + 10 + "px", maxWidth: isMobile ? "140px" : "230px", diff --git a/src/pages/Questions/RatingOptions/settingRating.tsx b/src/pages/Questions/RatingOptions/settingRating.tsx index cb37e5b8..966b9d21 100644 --- a/src/pages/Questions/RatingOptions/settingRating.tsx +++ b/src/pages/Questions/RatingOptions/settingRating.tsx @@ -3,16 +3,12 @@ import { Box, ButtonBase, Slider, - Tooltip, Typography, useMediaQuery, useTheme, } from "@mui/material"; -import { setQuestionInnerName, updateQuestion } from "@root/questions/actions"; +import { updateQuestion } from "@root/questions/actions"; import CustomCheckbox from "@ui_kit/CustomCheckbox"; -import CustomTextField from "@ui_kit/CustomTextField"; -import { useDebouncedCallback } from "use-debounce"; -import InfoIcon from "../../../assets/icons/InfoIcon"; import FlagIcon from "../../../assets/icons/questionsPage/FlagIcon"; import StarIconMini from "../../../assets/icons/questionsPage/StarIconMini"; import HashtagIcon from "../../../assets/icons/questionsPage/hashtagIcon"; @@ -32,10 +28,6 @@ export default function SettingSlider({ question }: SettingSliderProps) { const isMobile = useMediaQuery(theme.breakpoints.down(790)); const isWrappColumn = useMediaQuery(theme.breakpoints.down(980)); - const setInnerName = useDebouncedCallback((value) => { - setQuestionInnerName(question.id, value); - }, 200); - const buttonRatingForm: ButtonRatingFrom[] = [ { name: "star", icon: }, { name: "trophie", icon: }, diff --git a/src/pages/Questions/SliderOptions/settingSlider.tsx b/src/pages/Questions/SliderOptions/settingSlider.tsx index 4ad01637..045bc1a0 100644 --- a/src/pages/Questions/SliderOptions/settingSlider.tsx +++ b/src/pages/Questions/SliderOptions/settingSlider.tsx @@ -1,15 +1,6 @@ -import { - Box, - Tooltip, - Typography, - useMediaQuery, - useTheme, -} from "@mui/material"; -import { setQuestionInnerName, updateQuestion } from "@root/questions/actions"; +import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { updateQuestion } from "@root/questions/actions"; import CustomCheckbox from "@ui_kit/CustomCheckbox"; -import CustomTextField from "@ui_kit/CustomTextField"; -import { useDebouncedCallback } from "use-debounce"; -import InfoIcon from "../../../assets/icons/InfoIcon"; import type { QuizQuestionNumber } from "../../../model/questionTypes/number"; type SettingSliderProps = { @@ -22,10 +13,6 @@ export default function SettingSlider({ question }: SettingSliderProps) { const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990)); const isMobile = useMediaQuery(theme.breakpoints.down(790)); - const setInnerName = useDebouncedCallback((value) => { - setQuestionInnerName(question.id, value); - }, 200); - return ( ); + case "result": + return null; + default: notReachable(question); } diff --git a/src/pages/Questions/UploadFile/settingUpload.tsx b/src/pages/Questions/UploadFile/settingUpload.tsx index 21a468fa..4bfdece1 100644 --- a/src/pages/Questions/UploadFile/settingUpload.tsx +++ b/src/pages/Questions/UploadFile/settingUpload.tsx @@ -1,15 +1,6 @@ -import { - Box, - Tooltip, - Typography, - useMediaQuery, - useTheme, -} from "@mui/material"; -import { setQuestionInnerName, updateQuestion } from "@root/questions/actions"; +import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { updateQuestion } from "@root/questions/actions"; import CustomCheckbox from "@ui_kit/CustomCheckbox"; -import CustomTextField from "@ui_kit/CustomTextField"; -import { useDebouncedCallback } from "use-debounce"; -import InfoIcon from "../../../assets/icons/InfoIcon"; import type { QuizQuestionFile } from "../../../model/questionTypes/file"; type SettingsUploadProps = { @@ -20,10 +11,6 @@ export default function SettingsUpload({ question }: SettingsUploadProps) { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(790)); - const setInnerName = useDebouncedCallback((value) => { - setQuestionInnerName(question.id, value); - }, 200); - return ( { - setQuestionInnerName(question.id, value); - }, 200); - return ( ("text"); - const updateQuestionHint = useDebouncedCallback((value) => { + const updateQuestionHint = (value: string) => { updateQuestion(question.id, (question) => { question.content.hint.text = value; }); - }, 200); + }; return ( updateQuestionHint(target.value || " ")} maxLength={100} /> diff --git a/src/pages/main.tsx b/src/pages/main.tsx index a8a9efe0..d5bcf8d8 100755 --- a/src/pages/main.tsx +++ b/src/pages/main.tsx @@ -3,7 +3,7 @@ import Sidebar from "@ui_kit/Sidebar/Sidebar"; import Box from "@mui/material/Box"; import { useTheme, useMediaQuery, IconButton } from "@mui/material"; import HeaderFull from "@ui_kit/Header/HeaderFull"; -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { SidebarMobile } from "../ui_kit/Sidebar/SidebarMobile"; import { setShowConfirmLeaveModal } from "@root/uiTools/actions"; import { setCurrentStep, setQuizes } from "@root/quizes/actions"; @@ -35,7 +35,6 @@ export default function Main({ sidebar, header, footer, Page }: Props) { const theme = useTheme(); const quiz = useCurrentQuiz(); const quizConfig = quiz?.config; - const { questions } = useQuestionsStore(); const { editQuizId } = useQuizStore(); const currentStep = useQuizStore((state) => state.currentStep); const { isTestServer } = useDomainDefine(); @@ -73,12 +72,12 @@ export default function Main({ sidebar, header, footer, Page }: Props) { const [nextStep, setNextStep] = useState(0); const [openBranchingPage, setOpenBranchingPage] = useState(false); - const openBranchingPageHC = () => { + const openBranchingPageHC = useCallback(() => { if (!openBranchingPage) { - deleteTimeoutedQuestions(questions, quiz); + deleteTimeoutedQuestions(); } setOpenBranchingPage((old) => !old); - }; + }, [openBranchingPage, setOpenBranchingPage]); const isConditionMet = [1].includes(currentStep) && quizConfig?.type !== "form"; diff --git a/src/pages/startPage/EditPage.tsx b/src/pages/startPage/EditPage.tsx index f810181d..83dd3801 100755 --- a/src/pages/startPage/EditPage.tsx +++ b/src/pages/startPage/EditPage.tsx @@ -59,7 +59,7 @@ export default function EditPage({ const quiz = useCurrentQuiz(); const { editQuizId } = useQuizStore(); const { questions } = useQuestionsStore(); - const { whyCantCreatePublic, showConfirmLeaveModal, nextStep } = useUiTools(); + const { showConfirmLeaveModal, nextStep } = useUiTools(); const theme = useTheme(); const navigate = useNavigate(); const currentStep = useQuizStore((state) => state.currentStep); diff --git a/src/stores/questions.ts b/src/stores/questions.ts index 1c19d6ee..810d2712 100644 --- a/src/stores/questions.ts +++ b/src/stores/questions.ts @@ -380,15 +380,15 @@ export const findQuestionById = (quizId: number) => { let found = null; questionStore .getState() - ["listQuestions"][quizId].some( - (quiz: AnyTypedQuizQuestion, index: number) => { - if (quiz.backendId === quizId) { - found = { quiz, index }; - return true; - } - return false; - }, - ); + [ + "listQuestions" + ][quizId].some((quiz: AnyTypedQuizQuestion, index: number) => { + if (quiz.backendId === quizId) { + found = { quiz, index }; + return true; + } + return false; + }); return found; }; diff --git a/src/stores/questions/actions.ts b/src/stores/questions/actions.ts index 4cee7725..99202212 100644 --- a/src/stores/questions/actions.ts +++ b/src/stores/questions/actions.ts @@ -13,21 +13,14 @@ import { UntypedQuizQuestion, createQuestionVariant, } from "@model/questionTypes/shared"; -import { defaultQuestionByType } from "../../constants/default"; import { produce } from "immer"; import { nanoid } from "nanoid"; import { enqueueSnackbar } from "notistack"; +import { defaultQuestionByType } from "../../constants/default"; import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError"; -import { RequestQueue } from "../../utils/requestQueue"; -import { updateRootContentId } from "@root/quizes/actions"; -import { useCurrentQuiz } from "@root/quizes/hooks"; -import { QuestionsStore, useQuestionsStore } from "./store"; -import { useUiTools } from "../uiTools/store"; -import { withErrorBoundary } from "react-error-boundary"; -import { QuizQuestionResult } from "@model/questionTypes/result"; import { replaceEmptyLinesToSpace } from "../../utils/replaceEmptyLinesToSpace"; -import { useQuizPreviewStore } from "@root/quizPreview"; -import { useQuizStore } from "@root/quizes/store"; +import { RequestQueue } from "../../utils/requestQueue"; +import { QuestionsStore, useQuestionsStore } from "./store"; export const setQuestions = (questions: RawQuestion[] | null | undefined) => setProducedState( @@ -244,15 +237,16 @@ export const cancelQuestionDeletion = (questionId: string) => }, ); -const REQUEST_DEBOUNCE = 200; const requestQueue = new RequestQueue(); -let requestTimeoutId: ReturnType; +let rollbackQuestions: ReturnType; export const updateQuestion = async ( questionId: string, updateFn: (question: T) => void, skipQueue = false, ) => { + if (!rollbackQuestions) rollbackQuestions = useQuestionsStore.getState(); + setProducedState( (state) => { const question = @@ -275,8 +269,6 @@ export const updateQuestion = async ( }, ); - // clearTimeout(requestTimeoutId); - const request = async () => { const q = useQuestionsStore.getState().questions.find((q) => q.id === questionId) || @@ -291,6 +283,7 @@ export const updateQuestion = async ( const response = await questionApi.edit( questionToEditQuestionRequest(replaceEmptyLinesToSpace(q)), ); + rollbackQuestions = useQuestionsStore.getState(); //Если мы делаем листочек веточкой - удаляем созданный к нему результ const questionResult = useQuestionsStore @@ -311,6 +304,8 @@ export const updateQuestion = async ( } catch (error) { if (isAxiosCanceledError(error)) return; + useQuestionsStore.setState(rollbackQuestions); + devlog("Error editing question", { error, questionId }); enqueueSnackbar("Не удалось сохранить вопрос"); } @@ -321,9 +316,7 @@ export const updateQuestion = async ( return; } - // requestTimeoutId = setTimeout(() => { - requestQueue.enqueue(request); - // }, REQUEST_DEBOUNCE); + requestQueue.enqueue(`updateQuestion-${questionId}`, request); }; export const addQuestionVariant = (questionId: string) => { @@ -453,7 +446,7 @@ export const createTypedQuestion = async ( questionId: string, type: QuestionType, ) => - requestQueue.enqueue(async () => { + requestQueue.enqueue(`createTypedQuestion-${questionId}`, async () => { const questions = useQuestionsStore.getState().questions; const question = questions.find((q) => q.id === questionId); if (!question) return; @@ -501,7 +494,7 @@ export const createTypedQuestion = async ( }); export const deleteQuestion = async (questionId: string) => - requestQueue.enqueue(async () => { + requestQueue.enqueue(`deleteQuestion-${questionId}`, async () => { const question = useQuestionsStore .getState() .questions.find((q) => q.id === questionId); @@ -525,7 +518,7 @@ export const deleteQuestion = async (questionId: string) => }); export const copyQuestion = async (questionId: string, quizId: number) => - requestQueue.enqueue(async () => { + requestQueue.enqueue(`copyQuestion-${quizId}-${questionId}`, async () => { const question = useQuestionsStore .getState() .questions.find((q) => q.id === questionId); @@ -585,7 +578,7 @@ export const copyQuestion = async (questionId: string, quizId: number) => } }); -function setProducedState( +function setProducedState( recipe: (state: QuestionsStore) => void, action?: A, ) { @@ -635,7 +628,7 @@ export const createResult = async ( quizId: number | null | undefined, parentContentId?: string, ) => - requestQueue.enqueue(async () => { + requestQueue.enqueue(`createResult-${quizId}`, async () => { if (!quizId || !parentContentId) { console.error( "Нет данных для создания результата. quizId: ", diff --git a/src/stores/quizes/actions.ts b/src/stores/quizes/actions.ts index b703f97e..05279454 100644 --- a/src/stores/quizes/actions.ts +++ b/src/stores/quizes/actions.ts @@ -3,15 +3,14 @@ import { devlog, getMessageFromFetchError } from "@frontend/kitui"; import { quizToEditQuizRequest } from "@model/quiz/edit"; import { Quiz, RawQuiz, rawQuizToQuiz } from "@model/quiz/quiz"; import { QuizConfig, maxQuizSetupSteps } from "@model/quizSettings"; +import { createUntypedQuestion, updateQuestion } from "@root/questions/actions"; +import { useQuestionsStore } from "@root/questions/store"; import { produce } from "immer"; import { enqueueSnackbar } from "notistack"; import { NavigateFunction } from "react-router-dom"; import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError"; import { RequestQueue } from "../../utils/requestQueue"; import { QuizStore, useQuizStore } from "./store"; -import { createUntypedQuestion, updateQuestion } from "@root/questions/actions"; -import { useCurrentQuiz } from "./hooks"; -import { useQuestionsStore } from "@root/questions/store"; export const setEditQuizId = (quizId: number | null) => setProducedState( @@ -157,7 +156,7 @@ export const updateQuiz = ( clearTimeout(requestTimeoutId); requestTimeoutId = setTimeout(async () => { requestQueue - .enqueue(async () => { + .enqueue(`updateQuiz-${quizId}`, async () => { const quiz = useQuizStore .getState() .quizes.find((q) => q.id === quizId); @@ -178,7 +177,7 @@ export const updateQuiz = ( }; export const createQuiz = async (navigate: NavigateFunction) => - requestQueue.enqueue(async () => { + requestQueue.enqueue("createQuiz", async () => { try { const rawQuiz = await quizApi.create(); const quiz = rawQuizToQuiz(rawQuiz); @@ -196,7 +195,7 @@ export const createQuiz = async (navigate: NavigateFunction) => }); export const deleteQuiz = async (quizId: string) => - requestQueue.enqueue(async () => { + requestQueue.enqueue(`deleteQuiz-${quizId}`, async () => { const quiz = useQuizStore.getState().quizes.find((q) => q.id === quizId); if (!quiz) return; diff --git a/src/stores/quizes/hooks.ts b/src/stores/quizes/hooks.ts index f27bbe0d..b95ec60d 100644 --- a/src/stores/quizes/hooks.ts +++ b/src/stores/quizes/hooks.ts @@ -34,3 +34,11 @@ export function useCurrentQuiz() { return quiz; } + +export function getCurrentQuiz() { + const { quizes, editQuizId } = useQuizStore.getState(); + + const quiz = quizes.find((q) => q.backendId === editQuizId); + + return quiz; +} diff --git a/src/stores/results/actions.ts b/src/stores/results/actions.ts index 72601a28..b90ae09b 100644 --- a/src/stores/results/actions.ts +++ b/src/stores/results/actions.ts @@ -35,7 +35,7 @@ const removeResult = (resultId: string) => }); export const deleteResult = async (resultId: number) => - requestQueue.enqueue(async () => { + requestQueue.enqueue(`deleteResult-${resultId}`, async () => { const result = useResultStore .getState() .results.find((r) => r.id === resultId); @@ -56,32 +56,35 @@ export const obsolescenceResult = async ( resultId: string, editQuizId: number, ) => - requestQueue.enqueue(async () => { - const result = useResultStore - .getState() - .results.find((r) => r.id === resultId); - if (!result) return; - if (result.new === false) return; - let lossDebouncer: null | ReturnType = null; - let lossId: string[] = [] as string[]; - if (!lossId.includes(resultId)) lossId.push(resultId); - if (typeof lossDebouncer === "number") clearTimeout(lossDebouncer); - lossDebouncer = setTimeout(async () => { - //стреляем на лишение новизны - try { - await resultApi.obsolescence(lossId); - //сбрасываем массив - lossId = []; - } catch (error) { - devlog("Error", error); + requestQueue.enqueue( + `obsolescenceResult-${resultId}-${editQuizId}`, + async () => { + const result = useResultStore + .getState() + .results.find((r) => r.id === resultId); + if (!result) return; + if (result.new === false) return; + let lossDebouncer: null | ReturnType = null; + let lossId: string[] = [] as string[]; + if (!lossId.includes(resultId)) lossId.push(resultId); + if (typeof lossDebouncer === "number") clearTimeout(lossDebouncer); + lossDebouncer = setTimeout(async () => { + //стреляем на лишение новизны + try { + await resultApi.obsolescence(lossId); + //сбрасываем массив + lossId = []; + } catch (error) { + devlog("Error", error); - const message = getMessageFromFetchError(error) ?? ""; - enqueueSnackbar(`Ошибка. ${message}`); - } - }, 3000); - const resultList = await resultApi.getList(editQuizId); - setResults(resultList); - }); + const message = getMessageFromFetchError(error) ?? ""; + enqueueSnackbar(`Ошибка. ${message}`); + } + }, 3000); + const resultList = await resultApi.getList(editQuizId); + setResults(resultList); + }, + ); export const ExportResults = async ( filterNew: string, @@ -110,7 +113,7 @@ export const ExportResults = async ( } }; -function setProducedState( +function setProducedState( recipe: (state: ResultStore) => void, action?: A, ) { diff --git a/src/utils/deleteFunc.ts b/src/utils/deleteFunc.ts index e250975b..273a256e 100644 --- a/src/utils/deleteFunc.ts +++ b/src/utils/deleteFunc.ts @@ -6,15 +6,17 @@ import { getQuestionByContentId, updateQuestion, } from "@root/questions/actions"; +import { useQuestionsStore } from "@root/questions/store"; import { updateRootContentId } from "@root/quizes/actions"; +import { getCurrentQuiz } from "@root/quizes/hooks"; //Всё здесь нужно сделать последовательно. И пусть весь мир ждёт. -export const DeleteFunction = async ( - questions: any, - question: any, - quiz: any, -) => { +export const DeleteFunction = async (question: any) => { + const questions = useQuestionsStore.getState().questions; + const quiz = getCurrentQuiz(); + if (!quiz) throw new Error("Quiz is null"); + if (question.type !== null) { if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам diff --git a/src/utils/deleteTimeoutedQuestions.ts b/src/utils/deleteTimeoutedQuestions.ts index f712381b..6deb3820 100644 --- a/src/utils/deleteTimeoutedQuestions.ts +++ b/src/utils/deleteTimeoutedQuestions.ts @@ -1,17 +1,11 @@ -import { - AnyTypedQuizQuestion, - UntypedQuizQuestion, -} from "@model/questionTypes/shared"; -import { Quiz } from "@model/quiz/quiz"; +import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; +import { useQuestionsStore } from "@root/questions/store"; import { updateSomeWorkBackend } from "@root/uiTools/actions"; import { DeleteFunction } from "@utils/deleteFunc"; -type allQuestionsTypes = AnyTypedQuizQuestion | UntypedQuizQuestion; +export const deleteTimeoutedQuestions = async () => { + const questions = useQuestionsStore.getState().questions; -export const deleteTimeoutedQuestions = async ( - questions: allQuestionsTypes[], - quiz: Quiz | undefined, -) => { const questionsForDeletion = questions.filter( ({ type, deleted }) => type && type !== "result" && deleted, ) as AnyTypedQuizQuestion[]; @@ -19,9 +13,7 @@ export const deleteTimeoutedQuestions = async ( updateSomeWorkBackend(true); await Promise.allSettled( - questionsForDeletion.map((question) => - DeleteFunction(questions, question, quiz), - ), + questionsForDeletion.map((question) => DeleteFunction(question)), ); updateSomeWorkBackend(false); diff --git a/src/utils/requestQueue.ts b/src/utils/requestQueue.ts index 5fd1c67a..49a5f6b8 100644 --- a/src/utils/requestQueue.ts +++ b/src/utils/requestQueue.ts @@ -1,14 +1,15 @@ -export class RequestQueue { +export class RequestQueue { private pendingPromise = false; private items: Array<{ + id: IdType; action: () => Promise; resolve: (value: T) => void; reject: (reason?: any) => void; }> = []; - enqueue(action: () => Promise) { + enqueue(id: IdType, action: () => Promise) { return new Promise((resolve, reject) => { - this.items.push({ action, resolve, reject }); + this.items.push({ action, resolve, reject, id }); this.dequeue(); }); } @@ -19,6 +20,9 @@ export class RequestQueue { const item = this.items.shift(); if (!item) return; + // remove tasks with same id since they are outdated + this.items = this.items.filter((i) => i.id !== item.id); + try { this.pendingPromise = true; const payload = await item.action();