diff --git a/src/components/Practice/Mechanics3.jsx b/src/components/Practice/Mechanics3.jsx index f54eed6f..e0dbffb1 100644 --- a/src/components/Practice/Mechanics3.jsx +++ b/src/components/Practice/Mechanics3.jsx @@ -1,4 +1,4 @@ -import { Box, Typography } from "@mui/material"; +import { Box, Grid, Typography } from "@mui/material"; import HomophonesFinder from "homophones"; import React, { createRef, useEffect, useState } from "react"; import { @@ -13,6 +13,7 @@ import { import MainLayout from "../Layouts.jsx/MainLayout"; import correctSound from "../../assets/audio/correct.wav"; import wrongSound from "../../assets/audio/wrong.wav"; +import removeSound from "../../assets/audio/remove.wav"; import VoiceAnalyser from "../../utils/VoiceAnalyser"; const Mechanics2 = ({ @@ -48,6 +49,8 @@ const Mechanics2 = ({ setEnableNext, loading, setOpenMessageDialog, + options, + audio, }) => { const [words, setWords] = useState([]); const [sentences, setSentences] = useState([]); @@ -57,6 +60,13 @@ const Mechanics2 = ({ const [shake, setShake] = useState(false); const [wordToFill, setWordToFill] = useState(""); const [disabledWords, setDisabledWords] = useState(false); + const [answer, setAnswer] = useState({ + text: "", + audio_url: "", + image_url: "", + isAns: false, + }); + const lang = getLocalData("lang"); let wordToCheck = type === "audio" ? parentWords : wordToFill; @@ -68,7 +78,7 @@ const Mechanics2 = ({ try { await getSimilarWords(wordsArr[randomIndex]); setWordToFill(wordsArr[randomIndex]); - wordsArr[randomIndex] = "dash"; + // wordsArr[randomIndex] = "dash"; setSentences(wordsArr); setSelectedWord(""); } catch (error) { @@ -161,6 +171,34 @@ const Mechanics2 = ({ } }; + useEffect(() => { + if (!enableNext) { + setAnswer({ text: "", audio_url: "", image_url: "", isAns: false }); + } + }, [parentWords]); + + const handleAnswerFillInTheBlank = (word) => { + setAnswer(word); + + const isSoundCorrect = word.isAns; + let audio = new Audio(isSoundCorrect ? correctSound : wrongSound); + if (!isSoundCorrect) { + setEnableNext(false); + } + audio.play(); + setShake(true); + setTimeout(() => { + setShake(false); + }, 800); + }; + + const handleRemoveWord = () => { + let audio = new Audio(removeSound); + setAnswer({ text: "", audio_url: "", image_url: "", isAns: false }); + audio.play(); + setEnableNext(false); + }; + const audioRef = createRef(null); const [duration, setDuration] = useState(0); const [isReady, setIsReady] = React.useState(false); @@ -180,7 +218,7 @@ const Mechanics2 = ({ }; const [currrentProgress, setCurrrentProgress] = React.useState(0); - const progressBarWidth = isNaN(currrentProgress / duration) + const progressBarWidth = Number.isNaN(currrentProgress / duration) ? 0 : currrentProgress / duration; @@ -305,122 +343,89 @@ const Mechanics2 = ({ ) : ( <> - - {/* */} - - {sentences?.map((elem, index) => ( - - {elem === "dash" ? ( - 0 && "10px", - minWidth: "120px", - height: "80px", - borderBottom: "3px solid #5F6C92", - position: "relative", - }} - > - {selectedWord && ( - handleWord(selectedWord, true)} - className={ - elem === "dash" - ? selectedWord === wordToCheck - ? `audioSelectedWord` - : `audioSelectedWrongWord ${ - shake ? "shakeImage" : "" - }` - : "" - } - sx={{ - textAlign: "center", - px: "25px", - py: "12px", - // background: "transparent", - m: 1, - textTransform: "none", - borderRadius: "12px", - border: `1px solid ${ - elem === "dash" - ? selectedWord === wordToCheck - ? "#58CC02" - : "#C30303" - : "#333F61" - }`, - background: "#FFF", - cursor: "pointer", - }} - > - - {selectedWord} - - - )} - - ) : ( - 0 && "10px", - }} - > - + )} + + + + + {answer?.text !== "" ? ( + <> + {parentWords?.split("_____")[0]} {/* Before the blank */} + - {elem} - - + {answer?.text} + + {parentWords?.split("_____")[1]} {/* After the blank */} + + ) : ( + <>{parentWords} )} - - ))} + + )} @@ -432,49 +437,104 @@ const Mechanics2 = ({ marginBottom: "30px", }} > - {words?.map((elem, ind) => ( - handleWord(elem)} - sx={{ - textAlign: "center", - px: "25px", - py: "12px", - // background: "transparent", - m: 1, - textTransform: "none", - borderRadius: "12px", - border: `1px solid rgba(51, 63, 97, 0.10)`, - background: "#FFF", - cursor: "pointer", - opacity: disabledWords ? 0.25 : 1, - pointerEvents: disabledWords ? "none" : "initial", - }} - > - ( + handleWord(elem)} + sx={{ + textAlign: "center", + px: "25px", + py: "12px", + // background: "transparent", + m: 1, + textTransform: "none", + borderRadius: "12px", + border: `1px solid rgba(51, 63, 97, 0.10)`, + background: "#FFF", + cursor: "pointer", + opacity: disabledWords ? 0.25 : 1, + pointerEvents: disabledWords ? "none" : "initial", }} > - {elem} - - - ))} + + {elem} + + + ))} + <> + {type === "fillInTheBlank" && + Array.isArray(options) && + options.map( + (elem, ind) => + answer?.text !== elem.text && ( + handleAnswerFillInTheBlank(elem)} + sx={{ + textAlign: "center", + px: { xs: "10px", sm: "20px", md: "25px" }, // Responsive padding + py: { xs: "8px", sm: "10px", md: "12px" }, // Responsive padding + m: 1, + textTransform: "none", + borderRadius: "12px", + border: `1px solid rgba(51, 63, 97, 0.10)`, + background: "#FFF", + cursor: "pointer", + opacity: disabledWords ? 0.25 : 1, + pointerEvents: disabledWords ? "none" : "initial", + display: "flex", // Flex display for better alignment + justifyContent: "center", // Centering text + alignItems: "center", // Centering text vertically + }} + > + + {elem?.text} + + + ) + )} + { @@ -488,6 +548,7 @@ const Mechanics2 = ({ originalText={parentWords} enableNext={getEnableButton()} handleNext={handleNext} + audioLink={audio ? audio : ""} {...{ contentId, contentType, @@ -496,7 +557,7 @@ const Mechanics2 = ({ callUpdateLearner, isShowCase, setEnableNext, - showOnlyListen: selectedWord != wordToCheck, + showOnlyListen: !answer?.isAns, setOpenMessageDialog, }} /> diff --git a/src/components/Practice/Mechanics6.jsx b/src/components/Practice/Mechanics6.jsx new file mode 100644 index 00000000..9b87bccb --- /dev/null +++ b/src/components/Practice/Mechanics6.jsx @@ -0,0 +1,519 @@ +import { Box, Typography } from "@mui/material"; +import HomophonesFinder from "homophones"; +import React, { createRef, useEffect, useState } from "react"; +import { + AudioBarColoredSvg, + AudioBarSvg, + AudioPlayerSvg, + PlayAudioButton, + StopAudioButton, + getLocalData, + randomizeArray, +} from "../../utils/constants"; +import MainLayout from "../Layouts.jsx/MainLayout"; +import correctSound from "../../assets/audio/correct.wav"; +import wrongSound from "../../assets/audio/wrong.wav"; +import VoiceAnalyser from "../../utils/VoiceAnalyser"; + +const Mechanics2 = ({ + page, + setPage, + type, + handleNext, + background, + header, + parentWords, + image, + setVoiceText, + setRecordedAudio, + setVoiceAnimate, + storyLine, + enableNext, + showTimer, + points, + steps, + currentStep, + contentId, + contentType, + level, + isDiscover, + progressData, + showProgress, + playTeacherAudio = () => {}, + callUpdateLearner, + disableScreen, + isShowCase, + handleBack, + allWords, + setEnableNext, + loading, + setOpenMessageDialog, +}) => { + const [words, setWords] = useState([]); + const [sentences, setSentences] = useState([]); + + const [selectedWord, setSelectedWord] = useState(""); + // const [loading, setLoading] = useState(false); + const [shake, setShake] = useState(false); + const [wordToFill, setWordToFill] = useState(""); + const [disabledWords, setDisabledWords] = useState(false); + const lang = getLocalData("lang"); + let wordToCheck = type === "audio" ? parentWords : wordToFill; + + useEffect(() => { + const initializeFillInTheBlank = async () => { + if (type === "fillInTheBlank" && parentWords?.length) { + let wordsArr = parentWords.split(" "); + let randomIndex = Math.floor(Math.random() * wordsArr.length); + try { + await getSimilarWords(wordsArr[randomIndex]); + setWordToFill(wordsArr[randomIndex]); + wordsArr[randomIndex] = "dash"; + setSentences(wordsArr); + setSelectedWord(""); + } catch (error) { + console.error("Error in initializeFillInTheBlank:", error); + } + } + }; + initializeFillInTheBlank(); + }, [contentId, parentWords]); + + useEffect(() => { + const initializeAudio = async () => { + if (type === "audio" && parentWords) { + setDisabledWords(true); + setSelectedWord(""); + try { + await getSimilarWords(parentWords); + } catch (error) { + console.error("Error in initializeAudio:", error); + } + } + }; + initializeAudio(); + }, [contentId, parentWords]); + + const getSimilarWords = async (wordForFindingHomophones) => { + const lang = getLocalData("lang"); + // const isFillInTheBlanks = type === "fillInTheBlank"; + const wordToSimilar = wordForFindingHomophones + ? wordForFindingHomophones + : parentWords; + + if (lang === "en") { + const finder = new HomophonesFinder(); + const homophones = await finder.find(wordToSimilar); + let wordsArr = [homophones[8], wordToSimilar, homophones[6]]; + setWords(randomizeArray(wordsArr)); + } else { + let wordsToShow = []; + if (type == "audio") { + wordsToShow = allWords?.filter((elem) => elem != wordToSimilar); + } + if (type == "fillInTheBlank") { + wordsToShow = allWords + ?.join(" ") + ?.split(" ") + .filter((elem) => elem !== wordToSimilar && elem.length > 2); + } + + wordsToShow = randomizeArray(wordsToShow).slice(0, 2); + wordsToShow.push(wordToSimilar); + setWords(randomizeArray(wordsToShow)); + } + }; + + const handleWord = (word, removeWord) => { + if (removeWord) { + setWords([...words, word]); + setSelectedWord(""); + setEnableNext(false); + } else { + let wordsArr = [...words]; + + if (type !== "audio") { + let index = wordsArr?.findIndex((elem) => elem === word); + if (index !== -1) { + wordsArr?.splice(index, 1); + } + } + + if (selectedWord && type !== "audio") { + wordsArr.push(selectedWord); + } + + // if (type === "audio") { + const isSoundCorrect = word === wordToCheck; + let audio = new Audio(isSoundCorrect ? correctSound : wrongSound); + if (!isSoundCorrect) { + setEnableNext(false); + } + audio.play(); + setShake(true); + setTimeout(() => { + setShake(false); + }, 800); + // } + + setWords(wordsArr); + setSelectedWord(word); + } + }; + + const audioRef = createRef(null); + const [duration, setDuration] = useState(0); + const [isReady, setIsReady] = React.useState(false); + + const [isPlaying, setIsPlaying] = React.useState(false); + + const togglePlayPause = () => { + if (isPlaying) { + audioRef.current?.pause(); + setIsPlaying(false); + } else { + audioRef.current.pause(); + audioRef.current.load(); + audioRef.current.play(); + setIsPlaying(true); + } + }; + + const [currrentProgress, setCurrrentProgress] = React.useState(0); + const progressBarWidth = isNaN(currrentProgress / duration) + ? 0 + : currrentProgress / duration; + + const getEnableButton = () => { + if (type === "fillInTheBlank") { + return enableNext; + } + if (type === "audio") { + return selectedWord === wordToCheck; + } + }; + return ( + +
+ {header} +
+ + + {type === "audio" ? ( + + {/* */} + + {contentId && ( + + )} + + + + + + + + + + { + togglePlayPause(); + setDisabledWords(false); + }} + > + {isReady && ( + <>{isPlaying ? : } + )} + + + + ) : ( + <> + + + + {sentences?.map((elem, index) => ( + + {elem === "dash" ? ( + 0 && "10px", + minWidth: "120px", + height: "80px", + borderBottom: "3px solid #5F6C92", + position: "relative", + }} + > + {selectedWord && ( + handleWord(selectedWord, true)} + className={ + elem === "dash" + ? selectedWord === wordToCheck + ? `audioSelectedWord` + : `audioSelectedWrongWord ${ + shake ? "shakeImage" : "" + }` + : "" + } + sx={{ + textAlign: "center", + px: "25px", + py: "12px", + // background: "transparent", + m: 1, + textTransform: "none", + borderRadius: "12px", + border: `1px solid ${ + elem === "dash" + ? selectedWord === wordToCheck + ? "#58CC02" + : "#C30303" + : "#333F61" + }`, + background: "#FFF", + cursor: "pointer", + }} + > + + {selectedWord} + + + )} + + ) : ( + 0 && "10px", + }} + > + + {elem} + + + )} + + ))} + + )} + + + {words?.map((elem) => ( + handleWord(elem)} + sx={{ + textAlign: "center", + px: "25px", + py: "12px", + // background: "transparent", + m: 1, + textTransform: "none", + borderRadius: "12px", + border: `1px solid rgba(51, 63, 97, 0.10)`, + background: "#FFF", + cursor: "pointer", + opacity: disabledWords ? 0.25 : 1, + pointerEvents: disabledWords ? "none" : "initial", + }} + > + + {elem} + + + ))} + + { + + + + } + {/* + + */} +
+ ); +}; + +export default Mechanics2; diff --git a/src/utils/AudioCompare.js b/src/utils/AudioCompare.js index 702df659..317eba82 100644 --- a/src/utils/AudioCompare.js +++ b/src/utils/AudioCompare.js @@ -122,29 +122,33 @@ const AudioRecorder = (props) => { {props?.originalText && (!props.dontShowListen || props.recordedAudio) && ( <> - {!props.pauseAudio ? ( -
{ - props.playAudio(true); - }} - > - - - -
- ) : ( - { - props.playAudio(false); - }} - > - + {!props.isShowCase && ( + + {!props.pauseAudio ? ( +
{ + props.playAudio(true); + }} + > + + + +
+ ) : ( + { + props.playAudio(false); + }} + > + + + )}
)} {props.recordedAudio ? ( diff --git a/src/utils/VoiceAnalyser.js b/src/utils/VoiceAnalyser.js index 13142d00..a5f78e1f 100644 --- a/src/utils/VoiceAnalyser.js +++ b/src/utils/VoiceAnalyser.js @@ -91,11 +91,16 @@ function VoiceAnalyser(props) { }, [props.contentId]); const playAudio = async (val) => { + if (isStudentAudioPlaying) { + return; + } + const { audioLink } = props; try { let audio = new Audio( - `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/all-audio-files/${lang}/${props.contentId}.wav` + audioLink + ? audioLink + : `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/all-audio-files/${lang}/${props.contentId}.wav` ); - audio.addEventListener("canplaythrough", () => { set_temp_audio(audio); setPauseAudio(val); @@ -114,6 +119,9 @@ function VoiceAnalyser(props) { }; const playRecordedAudio = (val) => { + if (pauseAudio) { + return; + } try { const audio = new Audio(recordedAudio); @@ -618,6 +626,7 @@ function VoiceAnalyser(props) { ? props.isShowCase && !recordedAudio : props.dontShowListen } + isShowCase={props.isShowCase} isAudioPreprocessing={isAudioPreprocessing} recordedAudio={recordedAudio} setEnableNext={props.setEnableNext} diff --git a/src/utils/constants.js b/src/utils/constants.js index 25c1fed4..f1c42be4 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -3403,13 +3403,13 @@ export const levelGetContent = { title: "P5", criteria: "sentence", template: "simple", - mechanism: { id: "", name: "fillInTheBlank" }, + mechanism: { id: "mechanic_1", name: "fillInTheBlank" }, }, { title: "P6", criteria: "sentence", template: "simple", - mechanism: { id: "", name: "fillInTheBlank" }, + mechanism: { id: "mechanic_1", name: "fillInTheBlank" }, }, { title: "P7", criteria: "sentence", template: "simple" }, { title: "P8", criteria: "sentence", template: "simple" }, @@ -3443,7 +3443,7 @@ export const levelGetContent = { title: "P5", criteria: "sentence", template: "simple", - mechanism: { id: "", name: "fillInTheBlank" }, + mechanism: { id: "mechanic_1", name: "fillInTheBlank" }, }, { title: "P6", diff --git a/src/views/Practice/Practice.jsx b/src/views/Practice/Practice.jsx index 6f17ed70..350356b9 100644 --- a/src/views/Practice/Practice.jsx +++ b/src/views/Practice/Practice.jsx @@ -21,7 +21,7 @@ import { Typography } from "@mui/material"; import config from "../../utils/urlConstants.json"; import { MessageDialog } from "../../components/Assesment/Assesment"; import { Log } from "../../services/telementryService"; -import elephant from "../../assets/images/elephant.svg"; +import Mechanics6 from "../../components/Practice/Mechanics6"; const Practice = () => { const [page, setPage] = useState(""); @@ -44,6 +44,7 @@ const Practice = () => { const limit = 6; const [disableScreen, setDisableScreen] = useState(false); const [mechanism, setMechanism] = useState(""); + const [play] = useSound(LevelCompleteAudio); const [livesData, setLivesData] = useState(); const [gameOverData, setGameOverData] = useState(); @@ -303,7 +304,10 @@ const Practice = () => { return; } const resGetContent = await axios.get( - `${process.env.REACT_APP_LEARNER_AI_APP_HOST}/${config.URLS.GET_CONTENT}/${currentGetContent.criteria}/${virtualId}?language=${lang}&contentlimit=${limit}&gettargetlimit=${limit}` + `${process.env.REACT_APP_LEARNER_AI_APP_HOST}/${config.URLS.GET_CONTENT}/${currentGetContent.criteria}/${virtualId}?language=${lang}&contentlimit=${limit}&gettargetlimit=${limit}` + + (currentGetContent?.mechanism?.id + ? `&mechanics_id=${currentGetContent?.mechanism?.id}` + : "") ); setTotalSyllableCount(resGetContent?.data?.totalSyllableCount); @@ -458,6 +462,7 @@ const Practice = () => { }); quesArr = [...quesArr, ...(resWord?.data?.content || [])]; setCurrentContentType(currentGetContent.criteria); + setCurrentCollectionId(resWord?.data?.content?.[0]?.collectionId); setAssessmentResponse(resWord); @@ -535,7 +540,10 @@ const Practice = () => { ); let quesArr = []; const resWord = await axios.get( - `${process.env.REACT_APP_LEARNER_AI_APP_HOST}/${config.URLS.GET_CONTENT}/${currentGetContent.criteria}/${virtualId}?language=${lang}&contentlimit=${limit}&gettargetlimit=${limit}` + `${process.env.REACT_APP_LEARNER_AI_APP_HOST}/${config.URLS.GET_CONTENT}/${currentGetContent.criteria}/${virtualId}?language=${lang}&contentlimit=${limit}&gettargetlimit=${limit}` + + (currentGetContent?.mechanism?.id + ? `&mechanics_id=${currentGetContent?.mechanism?.id}` + : "") ); setTotalSyllableCount(resWord?.data?.totalSyllableCount); setLivesData({ @@ -755,10 +763,7 @@ const Practice = () => { }} /> ); - } else if ( - mechanism.name === "fillInTheBlank" || - mechanism.name === "audio" - ) { + } else if (mechanism.name === "fillInTheBlank" && mechanism.id !== "") { return ( { : questions[currentQuestion]?.contentType === "image" ? `Guess the below image` : `Speak the below ${questions[currentQuestion]?.contentType}`, - parentWords: - questions[currentQuestion]?.contentSourceData?.[0]?.text, + parentWords: questions[currentQuestion]?.mechanics_data?.[0]?.text, contentType: currentContentType, contentId: questions[currentQuestion]?.contentId, setVoiceText, @@ -781,7 +785,14 @@ const Practice = () => { setVoiceAnimate, storyLine, handleNext, - image: elephant, + image: questions[currentQuestion]?.mechanics_data + ? `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_images/` + + questions[currentQuestion]?.mechanics_data[0]?.image_url + : null, + audio: questions[currentQuestion]?.mechanics_data + ? `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/mechanics_audios/` + + questions[currentQuestion]?.mechanics_data[0]?.audio_url + : null, enableNext, showTimer: false, points, @@ -803,6 +814,9 @@ const Practice = () => { [], loading, setOpenMessageDialog, + options: questions[currentQuestion]?.mechanics_data + ? questions[currentQuestion]?.mechanics_data[0]?.options + : null, }} /> ); @@ -963,6 +977,67 @@ const Practice = () => { }} /> ); + } else if ( + mechanism.name === "audio" || + (mechanism.name === "fillInTheBlank" && mechanism.id === "") + ) { + return ( + elem?.contentSourceData?.[0]?.text) || + [], + loading, + setOpenMessageDialog, + options: questions[currentQuestion]?.mechanics_data + ? questions[currentQuestion]?.mechanics_data[0]?.options + : null, + }} + /> + ); } else if (page === 1) { return ; }