yuri-becker / react-jigsaw-puzzle

🧩 Simple plug-and-play React component for jigsaw puzzles
https://yuri-becker.github.io/react-jigsaw-puzzle/
GNU General Public License v3.0
24 stars 14 forks source link

ResizeObserver loop completed with undelivered notifications error #28

Closed skiffter1337 closed 1 year ago

skiffter1337 commented 1 year ago

Hi, I really like your library, but I'm facing a problem. When I ran your project locally, no errors appeared, but when I tried to integrate the library into my project I received an error. When resizing the browser window, the following errors are displayed in the console:

ResizeObserver loop completed with undelivered notifications. at handleError (http://localhost:3001/static/js/bundle.js:349867:58) at http://localhost:3001/static/js/bundle.js:349886:7.

Has anyone encountered something similar?

yuri-becker commented 1 year ago

Huh, interesting. Does that project happen to be open-source, or could you post the code of the component that uses ?

skiffter1337 commented 1 year ago

I'm using it in a large piece of code that renders different types of exercises(exerciseTypes.PUZZLE). Not an open-source project.

import React, {useState, useEffect, useRef, useContext} from 'react'; import {observer} from 'mobx-react'; import {superAxiosRequest} from 'axiosApi'; import {localisationMaster} from "Localisation/Localisation"; import {move} from "components/Modules/Ebook/functions" import {checkPermList} from "helpers"; import {permissions} from "settings/permissions"; import Stores from "Stores"; import asideModalStore from "Stores/asideModalStore"; import EbookChapterStore, {exerciseTypes} from 'Stores/ebookChapterStore'; import cogoToast from "cogo-toast"; import swal from "sweetalert"; import ModalExercise from "components/ContentBox/Global/GlobalModalSingle/templates/Ebooks/ModalExercise/ModalExercise"; import { QuestionImagesList, QuestionImagesThumb, QuestionSimpleText, QuestionUploadedVideo, QuestionAttention, } from 'components/ContentBox/Lessons/questions'; import { ImagesCarousel, EnterWords, SelectWords, DragWords, MatchWords, ImagesMatchText, MakeSentence, MakeSentences, MakeWord, MakeWords, QuestionAudios, QuestionTest, SimpleLink, VideoLinks, WritingEssay, GroupWords } from "./types" import {LessonContext} from "../../BookChapter/LessonContext"; import LessonTeacherStore from "components/Modules/Ebook/LessonTeacher/lessonTeacherStore"; import LessonStudentStore from "components/Modules/Ebook/LessonStudent/lessonStudentStore"; import AssignExercise from "components/ContentBox/Global/GlobalModalSingle/templates/Ebooks/AssignExercise"; import ExerciseHeader from "./components/ExerciseHeader"; import {MainExerciseImage} from "../MainExerciseImage/MainExerciseImage"; import SideMenuEbook from '../SideMenuEbook/SideMenuEbook'; import GradeRow from '../GradeRow/GradeRow'; import ExerciseEditPanel from "./components/ExerciseEditPanel"; import ExerciseAttemptsPanel from "components/Modules/Ebook/LessonStudent/ExerciseAttemptsPanel"; import st from "./Exercise.module.scss"; import AccentSaveAnswer from "../../LessonStudent/AccentSaveAnswer"; import WriteText from "./types/WriteText"; import ExerciseTitle from "./components/ExerciseTitle"; import OrderSentences from "./types/OrderSentences"; import ChooseSentencesForm from "./types/ChooseSentencesForm/ChooseSentencesForm"; import QuestionExample from "../../../../ContentBox/Lessons/questions/QuestionExample/QuestionExample"; import ImagesWriteText from "./types/ImagesWriteText"; import ImagesSelectText from "./types/ImagesSelectText"; import Crossword from "./types/Crossword"; import {Puzzle} from "./types/Puzzle/Puzzle";

const questionsWithAnswers = [ exerciseTypes.ESSAY, exerciseTypes.ENTER_WORDS, exerciseTypes.CROSSWORD, exerciseTypes.ORDER_SENTENCES, exerciseTypes.MAKE_SENTENCE, exerciseTypes.MAKE_SENTENCES, exerciseTypes.MAKE_WORD, exerciseTypes.MAKE_WORDS, exerciseTypes.CHOOSE_SENTENCES_FORM, exerciseTypes.WRITE_WORDS, exerciseTypes.WRITE_PHRASES, exerciseTypes.SELECT_WORDS, exerciseTypes.GROUP_WORDS, exerciseTypes.MATCH_WORDS_IMAGES, exerciseTypes.MATCH_WORDS, exerciseTypes.TEST, ]

const Exercise = ({translate, ex, ...props}) => { const refEx = useRef();

const {setSection, updateExercise, removeExercise, addExercise} = EbookChapterStore const {chapter, assignExercisesData, assignExercises, assignExercise, inHomework, addHomework} = LessonTeacherStore const {lessonData, lesson, addExAnswers, dropFocus, inFavorites} = LessonStudentStore

const context = useContext(LessonContext)

const isTeacher = checkPermList(permissions.add_init_lesson) const isStudent = Stores.isClient

const section = EbookChapterStore.section || LessonTeacherStore.section

const isResponseRequired = questionsWithAnswers.includes(ex.type_exercise) const [student, setStudent] = useState(null) const [answers, setAnswers] = useState([]) const [sendAnswersButtonActive, setSendAnswersButtonActive] = useState(true) const [activeTry, setActiveTry] = useState(1) const [attempts, setAttempts] = useState([])

// Π‘ΠΎΠΊΠΎΠ²Ρ‹Π΅ мСню для Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Ρ… Ρ€Π΅ΠΆΠΈΠΌΠΎΠ² const sideMenuSetting = [ // Π£Ρ‡ΠΈΡ‚Π΅Π»ΡŒ Π½Π° ΡƒΡ€ΠΎΠΊΠ΅ [ {type: 'select_student', text: translate?.provide_exercise, action: () => teacherAction('show')}, {type: 'focus', text: translate?.focus, action:() => teacherAction('focus')}, ...(isResponseRequired && ex.type_exercise !== (exerciseTypes.ESSAY || exerciseTypes.ENTER_WORDS) ? [{ type: 'checkbox', text: translate?.show_answers, action: () => teacherAction('showRightAnswers') }] : []), { type: 'home', text: translate?.add_to_homework, isPressed: inHomework(ex.id), action: () => teacherAction('homework') }, ], // Π£Ρ‡ΠΈΡ‚Π΅Π»ΡŒ Π½Π° ΡƒΡ€ΠΎΠΊΠ΅: Π’Ρ‹Π±ΠΎΡ€ ΡƒΡ‡Π΅Π½ΠΈΠΊΠ° [ {type: 'users', text: translate?.select_student}, ], // Π£Ρ‡Π΅Π½ΠΈΠΊ Π½Π° ΡƒΡ€ΠΎΠΊΠ΅ [ { type: 'save', text: translate?.save_answer, action: () => sendAnswersButtonActive && saveTry(), isDisabled: !sendAnswersButtonActive }, ], [ ...(inFavorites(ex.id) ? [{ type: 'dislike', text: "Dislike", isPressed: true, action: () => studentAction('remove_from_favorites') }] : [{ type: 'like', text: translate?.add_to_favorites, action: () => studentAction('add_to_favorites') }]) ], [ { type: 'delete_homework', text: translate?.delete, action: props.deleteExerciseFromHomework }, ], ]

// Условия, ΠΏΡ€ΠΈ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Π½Π΅ отобраТаСтся главная ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ° ΠΈ тСкст Π² Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… упраТнСниях const dontShowImageAndTextCond = ex.type_exercise !== exerciseTypes.ATTENTION

const dataForExTemplate = { isStudent: isStudent, isTeacher: isTeacher, is_teacher_view: isTeacher, // Π΄ΡƒΠ±Π»ΡŒ try: activeTry, // активная ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠ° handleAnswers: setAnswersFromQuestion, answers: answers, attempts, studentAnswer: attempts[activeTry - 1] }

// ДСйствия ΠΏΠΎ ΡƒΠΏΡ€Π°ΠΆΠ½Π΅Π½ΠΈΡŽ const action = action => { switch (action) { case 'edit': asideModalStore.activity({ title: 'Π Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ упраТнСния', modalBodyComponent: () => , button: 'Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ', valid: { condition_exercise: 'required' }, is_put: true, link: 'ebooks/exercise/' + ex.id, onSubmitSuccess: updateExercise, isResponsive: true, }) break; case 'copy': const copyExercise = {...ex}; delete copyExercise.position;

    superAxiosRequest({method: 'post', link: 'ebooks/copy_exercise'}, {exercise_id: ex.id})
      .then(({data}) => addExercise(data));
    break;
  case 'off':
    break;
  case 'delete':
    swal({
      title: localisationMaster('text_2', 'alert'),
      text: `Π’Ρ‹ Π΄Π΅ΠΉΡΡ‚Π²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ ΡƒΠΏΡ€Π°ΠΆΠ½Π΅Π½ΠΈΠ΅?`,
      icon: "warning",
      buttons: [localisationMaster('button_2', 'alert'), localisationMaster('button_1', 'alert')],
      dangerMode: true,
    }).then(yes => {
      if (yes) {
        const apiConf = {
          method: 'delete',
          link: 'ebooks/exercise/' + ex.id,
        }
        superAxiosRequest(apiConf).then(() => removeExercise(ex.id))
      }
    })
    break;
  case 'up':
  case 'down':
    let arr = section.exercises.map(x => x.id)
    arr = move(arr, arr.indexOf(ex.id), arr.indexOf(ex.id) + (action === 'up' ? -1 : 1))

    const apiConf = {
      method: 'put',
      //link: 'lessons/exercise_position/' + section.id
      link: 'ebooks/exercise_position/' + section.id
    }
    let body = {
      exercise_position: arr
    }

    superAxiosRequest(apiConf, body).then(res => {
      props.scrollTo(res.data.exercises.findIndex(x => x.id === ex.id))
      setSection(res.data)
    }).catch(() => {
      props.scrollTo(section.exercises.findIndex(x => x.id === ex.id))
    })
    break;
  default:
    break;
}

}

// ДСйствия прСподаватСля const teacherAction = action => { switch (action) { case 'show': const ms = { title: 'Select Students', modalBodyComponent: () => <AssignExercise allStudents={chapter.students} />, button: 'Save', link: 'ebooks/assign_exercise', onSubmitSuccess: assignExercise, } const md = { uuid: chapter.uuid, exercise_id: ex.id, students: assignExercises.find(x => x.exercise_id === ex.id)?.students.map(x => x.student_id) || [], } asideModalStore.activity(ms, md) break; case 'focus': const body = { type: "notifications.notify_focus_on_exercise", message: {exercise_id: ex.id}, } props.sendMessage(body)

    cogoToast.info(
      `Π£Ρ‡Π΅Π½ΠΈΠΊΠΈ ΡΡ„ΠΎΠΊΡƒΡΠΈΡ€ΠΎΠ²Π°Π»ΠΈΡΡŒ Π½Π° ΡƒΠΏΡ€Π°ΠΆΠ½Π΅Π½ΠΈΠΈ ${props.number}`,
      {position: "bottom-right"}
    )
    break;
  case 'homework':
    swal({
      title: "Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΡƒΠΏΡ€Π°ΠΆΠ½Π΅Π½ΠΈΠ΅ Π² домашнСС Π·Π°Π΄Π°Π½ΠΈΠ΅?",
      icon: "info",
      buttons: ["НСт", "Π”Π°"],
    }).then(yes => {
      if (yes) {
        superAxiosRequest(
          //{method: 'post', link: 'ebooks/add_to_hw'},
          {method: 'post', link: 'ebooks/homework_exercise'},
          {uuid: chapter.uuid, exersice_id: ex.id}
        ).then(res => {
          addHomework(res.data.exercises)
        }).catch(() => {

        })
      }
    })
    break;
  case 'showRightAnswers':
    superAxiosRequest(
        {method: 'get', link: 'ebooks/correct_answers/' + ex.id},
    ).then(res => {
      setAttempts([res.data])
      setActiveTry(1)
      setStudent(null)
    }).catch((error) => {
      console.log(`Some error occurred: ${error.response.data.error}`)
    })
    break;
  default:
    break;
}

}

// ДСйствия ΡƒΡ‡Π΅Π½ΠΈΠΊΠ° const studentAction = action => { switch (action){ case 'add_to_favorites': LessonStudentStore.addToFavorites(ex.id) break; case 'remove_from_favorites': LessonStudentStore.removeFromFavorites(ex.id) break; default: break; } }

const viewAnswers = student => { setStudent(student)

const students = assignExercises.find(ae => ae.exercise_id === ex.id)?.students || []
const answers = students.find(x => x.student_id === student.id)?.answers

setAttempts(answers || [])

setActiveTry(1)

}

function setAnswersFromQuestion(ans, saveButtonIsActive) { //устанавливаСм ΠΎΡ‚Π²Π΅Ρ‚Ρ‹ ΠΈΠ· шаблона задания setAnswers(ans) //Ссли Π² Π·Π°Π΄Π°Π½ΠΈΠΈ Π½Π΅Ρ‚ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ Π½Π° Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎΡΡ‚ΡŒ ΠΊΠ½ΠΎΠΏΠΊΠΈ оставляСм Π΅Π΅ Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎΠΉ if (saveButtonIsActive === undefined) return; //Ссли ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π΅ΡΡ‚ΡŒ присваиваСм Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΈΠ· шаблона вопроса setSendAnswersButtonActive(saveButtonIsActive) //Ссли ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π΅ΡΡ‚ΡŒ присваиваСм Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΈΠ· шаблона вопроса }

const saveTry = () => { setSendAnswersButtonActive(false) let answer const apiConf = { method: 'post', link: 'ebooks/answ_exrs', } switch (ex.type_exercise) { case exerciseTypes.SELECT_WORDS: case exerciseTypes.WRITE_PHRASES: case exerciseTypes.MAKE_WORD: case exerciseTypes.MAKE_SENTENCE: answer = answers.join('|') break; case exerciseTypes.WRITE_WORDS: answer = answers.map(el => el) break; case exerciseTypes.ORDER_SENTENCES: answer = answers.map(el => el) break; case exerciseTypes.CROSSWORD: answer = answers.map(el => el) break; case exerciseTypes.MAKE_WORDS: case exerciseTypes.CHOOSE_SENTENCES_FORM: answer = answers break; case exerciseTypes.MAKE_SENTENCES: answer = answers.map(arr => arr.join('|')) break; case exerciseTypes.MATCH_WORDS: answer = answers.map(pair => { const [word_1, word_2] = pair.split('~~~') return {word_1, word_2} }) break; case exerciseTypes.MATCH_WORDS_IMAGES: answer = answers.map(pair => ({text: pair.description, image: pair.link})) break; case exerciseTypes.GROUP_WORDS: answer = answers.map(ans => ({description: ans.description, value: ans.words.join('|')})) break; case exerciseTypes.ESSAY: answer = answers; break; case exerciseTypes.ENTER_WORDS: answer = answers.map(word => word) break; case exerciseTypes.TEST: answer = answers.slice(1).map((ans, idx) => ({ text: ex.data.questions[idx].text, variants: ans.map(a => ({text: a})) })) break; default: break; } const body = { exercise_id: ex.id, answers: answer, }

if (ex.type_exercise === exerciseTypes.TEST) {
  body.time = answers[0];
}

superAxiosRequest(apiConf, body).then(res => {
  addExAnswers(ex.id, res.data)
  if (activeTry < 11) {
    setAnswers([])
    setActiveTry(activeTry + 1)
  }
}).catch(err => {
  if (err.response)
    alert(err.response?.data.error)
}).finally(() => {
  // setSendAnswersButtonActive(true)
})

}

const renderExTemplate = ({type_exercise: type, ...exercise}) => { let body; switch (type) { case exerciseTypes.IMAGES: switch (exercise.show_like) { case 'sequence': body = break; case 'mini': body = break; case 'slider': body = break;

      default:
        break;
    }
    break;
  case exerciseTypes.GIF:
    body = <QuestionImagesList images={exercise.items} text={exercise.text_exercise}/>
    break;
  case exerciseTypes.AUDIO:
    body = <QuestionAudios items={exercise.items}
                           condition_exercise={exercise.condition_exercise}
                           text={exercise.text_exercise}/>
    break;
  case exerciseTypes.VIDEO_LINK:
    body = <VideoLinks videos={exercise.data} text={exercise.text_exercise}/>
    break;
  case exerciseTypes.ESSAY:
    body = <WritingEssay ex={exercise} {...dataForExTemplate}/>
    break;
  case exerciseTypes.PUZZLE:
    body = <div className={st.puzzle}><Puzzle imageSrc={Stores.baseLink() + exercise.image} columns={2} rows={2} onSolved={() => swal('Success')}/></div>
    // body = <WriteText ex={exercise} {...dataForExTemplate}/>
    break;
  case exerciseTypes.CROSSWORD:
    body = <Crossword ex={exercise} {...dataForExTemplate}/>
    break;
  case exerciseTypes.MATCH_WORDS_IMAGES:
    switch (exercise.subtype) {
      case 'move':
        body = <ImagesMatchText
          data={exercise.data}
          text={exercise.text_exercise}
          {...dataForExTemplate}
        />
        break;
      case 'write':
        body = <ImagesWriteText
          data={exercise.data}
          text={exercise.text_exercise}
          {...dataForExTemplate}
        />
        break;
      case 'select':
        body = <ImagesSelectText
          data={exercise.data}
          text={exercise.text_exercise}
          {...dataForExTemplate}
        />
        break;

      default:
        break;
    }
    break;

  case exerciseTypes.MAKE_SENTENCE:
    body = <MakeSentence sentence={exercise.data.sentence} {...dataForExTemplate} {...exercise}/>
    break;
  case exerciseTypes.MAKE_SENTENCES:
    body = <MakeSentences {...dataForExTemplate} exercise={exercise}/>
    break;
  case exerciseTypes.GROUP_WORDS:
    body = <GroupWords items={exercise.data} {...dataForExTemplate}/>
    break;
  case exerciseTypes.WRITE_PHRASES:
    body = <DragWords items={exercise.data}
                      {...dataForExTemplate}/>
    break;
  case exerciseTypes.SELECT_WORDS:
    body = <SelectWords items={exercise.data}
                        {...dataForExTemplate} mode={context.mode}/>
    break;
  case exerciseTypes.LINK:
    body = <SimpleLink data={exercise.data || {}}/>
    break;

  case exerciseTypes.VIDEO:
    body = <QuestionUploadedVideo videos={exercise.items || []} v_link={exercise.items?.[0]?.file || ''}
                                  text={exercise.text_exercise}/>
    break;
  case exerciseTypes.WRITE_WORDS:
    body = <EnterWords items={exercise.data}{...dataForExTemplate}/>
    break;
  case exerciseTypes.MAKE_WORD:
    body = <MakeWord word={exercise.data} {...dataForExTemplate} {...exercise}/>
    break;
  case exerciseTypes.MAKE_WORDS:
    body = <MakeWords {...dataForExTemplate} {...exercise}/>
    break;
  case exerciseTypes.CHOOSE_SENTENCES_FORM:
    body = <ChooseSentencesForm {...dataForExTemplate} {...exercise}/>
    break;
  case exerciseTypes.MATCH_WORDS:
    body = <MatchWords data={exercise.data} {...dataForExTemplate} {...exercise}/>
    break;

  case exerciseTypes.ORDER_SENTENCES:
    body = <OrderSentences data={exercise.data} {...dataForExTemplate} {...exercise}/>
    break;

  //Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для прСподаватСля
  case exerciseTypes.TEST:
    body = <QuestionTest
      text={exercise.text_exercise}
      questions={exercise.data.questions}
      isTimer={exercise.data.is_timer}
      time={exercise.data.time}
      isPoint={exercise.data.is_point}
      saveTry={saveTry}
      {...dataForExTemplate}/>
    break;
  case exerciseTypes.ATTENTION:
    body = <QuestionAttention images={exercise.items} image={ex.image} text={exercise.text_exercise}/>
    break
  default:
    break;
}
return body;

}

const getStudentAnswers = st => { // ВсС студСнты Π΄Π°Π½Π½ΠΎΠ³ΠΎ упраТнСния const students = assignExercises.find(ae => ae.exercise_id === ex.id)?.students || [] // ΠžΡ‚Π²Π΅Ρ‚Ρ‹ Π²Ρ‹Π±Ρ€Π°Π½Π½ΠΎΠ³ΠΎ студСнта const answers = students.find(x => x.student_id === st.id)?.answers

let arr = []
for (let index = 1; index <= 10; index++) {
  arr[index] = {
    attempt: index,
    result: answers?.[index - 1]?.result || null
  }
}
if (answers) {
  answers.forEach((item) => arr[item.attempt] = item)
}

return ({...st, answers: arr})

}

useEffect(() => { if (!props.focused) return; refEx.current.scrollIntoView({behavior: "smooth"}); dropFocus() }, [dropFocus, props.focused])

useEffect(() => { if (answers.length) return; setSendAnswersButtonActive(false) }, [answers])

useEffect(() => { let answers = [] if (context.mode === 'student'){ answers = lesson?.assign_exercises.find(ae => ae.exercise_id === ex.id)?.answers || [] setActiveTry(Math.min(answers.length + 1, 10)) } else if (context.mode === 'teacher') { const students = [...assignExercises].find(ae => ae.exercise_id === ex.id)?.students || [] if (student) { answers = students.find(st => st.student_id === student.id)?.answers || [] } } else if (context.mode === 'teacher-homework'){ answers = props.answers }

setAttempts(answers)

}, [assignExercisesData, lessonData, props.answers])

useEffect(() => { if (attempts.length <= activeTry) { setSendAnswersButtonActive(false) } }, [activeTry, attempts]);

const onClose = () => { setStudent(null) setAttempts([]) }

const isTitleEx = ex.type_exercise === exerciseTypes.TITLE;

return (

{/*=== Π¨Π°ΠΏΠΊΠ° вопроса для учитСля ===*/} {(context.mode === 'teacher' && student) && } {/*=== Π¨Π°ΠΏΠΊΠ° вопроса для ΡƒΡ‡Π΅Π½ΠΈΠΊΠ° ===*/} {(context.mode === 'student' && isResponseRequired) && ae.exercise_id === ex.id)?.answers || []}/>} {/*=== Π¨Π°ΠΏΠΊΠ° вопроса для Ρ€Π΅ΠΆΠΈΠΌΠ° рСдактирования ΡƒΡ€ΠΎΠΊΠ° ===*/} {context.mode === 'edit' && } {/*Π¨Π°ΠΏΠΊΠ° упраТнСния для учитСля Π² Π”ΠΎΠΌΠ°ΡˆΠ½Π΅ΠΌ Π·Π°Π΄Π°Π½ΠΈΠΈ*/} {(context.mode === 'teacher-homework' && isResponseRequired && props.student) && } {isTitleEx ? : }
{/*{ex.image && dontShowImageAndTextCond &&*/} {/* }*/} {ex.text_exercise && dontShowImageAndTextCond && } {ex.example && } {renderExTemplate(ex)}
{context.mode === 'teacher' &&
{isResponseRequired && }
} {context.mode === 'student' &&
{isResponseRequired && }
} {context.mode === 'teacher-homework' &&
} {/* АкцСнт Π½Π° Π±Π»ΠΎΠΊΠ΅ сохранСния */}
{context.mode === 'student' && isResponseRequired && }

) }

export default observer(Exercise);