From f3248584c432de3071d5d8c58db7576fa7fa4ec9 Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Tue, 17 Dec 2024 14:36:26 +0530 Subject: [PATCH 01/18] Issueid #231942 feat: audio delay right after completing level in practice --- src/views/Practice/Practice.jsx | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/views/Practice/Practice.jsx b/src/views/Practice/Practice.jsx index 53520294..206d8107 100644 --- a/src/views/Practice/Practice.jsx +++ b/src/views/Practice/Practice.jsx @@ -70,9 +70,32 @@ const Practice = () => { } }, [startShowCase]); + const [audioSrc, setAudioSrc] = useState(null); + + useEffect(() => { + const preloadAudio = async () => { + try { + const response = await fetch(LevelCompleteAudio); + const audioBlob = await response.blob(); + const audioUrl = URL.createObjectURL(audioBlob); + setAudioSrc(audioUrl); + } catch (error) { + console.error("Error loading audio:", error); + } + }; + preloadAudio(); + }, []); + const callConfettiAndPlay = () => { - let audio = new Audio(LevelCompleteAudio); - audio.play(); + if (audioSrc) { + // Play preloaded audio if available + const audio = new Audio(audioSrc); + audio.play(); + } else { + // Fallback to LevelCompleteAudio if preloaded audio is not available + const fallbackAudio = new Audio(LevelCompleteAudio); + fallbackAudio.play(); + } callConfetti(); }; From 5a40064ee2351db95d5270d070879bae78f5b940 Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Tue, 17 Dec 2024 15:08:08 +0530 Subject: [PATCH 02/18] Issueid #231942 feat: audio delay right after completing level in practice --- .github/workflows/all-dev-tn-new.yml | 2 +- src/components/Layouts.jsx/MainLayout.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/all-dev-tn-new.yml b/.github/workflows/all-dev-tn-new.yml index 97a56fc7..234eb2f2 100644 --- a/.github/workflows/all-dev-tn-new.yml +++ b/.github/workflows/all-dev-tn-new.yml @@ -3,7 +3,7 @@ name: ALL new tn dev Deployment on: push: branches: - - all-1.3-tn-dev-hotfix + - all-1.3-feedback-change jobs: deploy: diff --git a/src/components/Layouts.jsx/MainLayout.jsx b/src/components/Layouts.jsx/MainLayout.jsx index 2a2ffe6f..e875a708 100644 --- a/src/components/Layouts.jsx/MainLayout.jsx +++ b/src/components/Layouts.jsx/MainLayout.jsx @@ -1161,7 +1161,7 @@ const MainLayout = (props) => { fontSize={{ md: "14px", xs: "10px" }} > {!gameOverData ? "Start Game ➜" : "Practice ➜"} - + {/* */} From c660319d2098d7285d4e89f3b8784eae24f66437 Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Tue, 17 Dec 2024 15:42:54 +0530 Subject: [PATCH 03/18] Issueid #231949 feat: Show loading until next content is not loaded --- src/utils/AudioCompare.js | 42 +++++++++++++++++++++++--------------- src/utils/VoiceAnalyser.js | 11 ++++++++++ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/utils/AudioCompare.js b/src/utils/AudioCompare.js index a81b3ac2..a1fe716f 100644 --- a/src/utils/AudioCompare.js +++ b/src/utils/AudioCompare.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef } from "react"; import RecordRTC from "recordrtc"; -import { Box } from "@mui/material"; +import { Box, CircularProgress } from "@mui/material"; import { ListenButton, RetryIcon, SpeakButton, StopButton } from "./constants"; import RecordVoiceVisualizer from "./RecordVoiceVisualizer"; import playButton from "../../src/assets/listen.png"; @@ -26,12 +26,12 @@ const AudioRecorder = (props) => { }, []); const startRecording = async () => { - setStatus("recording"); - if (props.setEnableNext) { - props.setEnableNext(false); - } try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + if (props.setEnableNext) { + props.setEnableNext(false); + } + setStatus("recording"); mediaStreamRef.current = stream; // Use RecordRTC with specific configurations to match the blob structure @@ -119,7 +119,8 @@ const AudioRecorder = (props) => { }} className="game-action-button" > - {props?.originalText && + {props.enableAfterLoad && + props?.originalText && (!props.dontShowListen || props.recordedAudio) && ( <> {!props.isShowCase && ( @@ -175,17 +176,24 @@ const AudioRecorder = (props) => { )}
- {props?.originalText && !props.showOnlyListen && ( - - {!props.recordedAudio ? : } + {props.enableAfterLoad ? ( + props?.originalText && + !props.showOnlyListen && ( + + {!props.recordedAudio ? : } + + ) + ) : ( + + )}
diff --git a/src/utils/VoiceAnalyser.js b/src/utils/VoiceAnalyser.js index 1884ab47..b5b4de74 100644 --- a/src/utils/VoiceAnalyser.js +++ b/src/utils/VoiceAnalyser.js @@ -63,6 +63,7 @@ function VoiceAnalyser(props) { const [pauseAudio, setPauseAudio] = useState(false); const [recordedAudio, setRecordedAudio] = useState(""); const [recordedAudioBase64, setRecordedAudioBase64] = useState(""); + const [enableAfterLoad, setEnableAfterLoad] = useState(false); const [audioPermission, setAudioPermission] = useState(null); const [apiResponse, setApiResponse] = useState(""); const [currentIndex, setCurrentIndex] = useState(); @@ -274,10 +275,19 @@ function VoiceAnalyser(props) { const lang = getLocalData("lang") || "ta"; fetchASROutput(lang, recordedAudioBase64); setLoader(true); + setEnableAfterLoad(false); + } else { + alert("please record again"); } } }, [props.isNextButtonCalled]); + useEffect(() => { + if (props.originalText) { + setEnableAfterLoad(true); + } + }, [props.originalText]); + useEffect(() => { if (recordedAudioBase64 !== "") { if (props.setIsNextButtonCalled) { @@ -695,6 +705,7 @@ function VoiceAnalyser(props) { setEnableNext={props.setEnableNext} showOnlyListen={props.showOnlyListen} setOpenMessageDialog={props.setOpenMessageDialog} + enableAfterLoad={enableAfterLoad} /> {/* */} From 15b6002e5c146c2a75715e7caabcf120b74b48ca Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Wed, 18 Dec 2024 14:49:08 +0530 Subject: [PATCH 04/18] Issueid #231949 feat: Show loading until next content is not loaded --- src/index.css | 31 ++++++++++++++++++++++++++ src/utils/AudioCompare.js | 46 ++++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/src/index.css b/src/index.css index 217a9822..38614c5d 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,34 @@ +.stopButtonLoader { + height: 70px; + aspect-ratio: 1; + border-radius: 50%; + /* background: #000; */ + box-shadow: 0 0 0 0 rgba(226, 45, 45, 0.267); + animation: l2 1.5s infinite linear; + position: relative; +} + +.stopButtonLoader:before, +.stopButtonLoader:after { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + box-shadow: 0 0 0 0 #0004; + animation: inherit; + animation-delay: -0.5s; +} + +.stopButtonLoader:after { + animation-delay: -1s; +} + +@keyframes l2 { + 100% { + box-shadow: 0 0 0 40px #fc2c2c00 + } +} + .hide { display: none; } diff --git a/src/utils/AudioCompare.js b/src/utils/AudioCompare.js index a1fe716f..32fa8f2a 100644 --- a/src/utils/AudioCompare.js +++ b/src/utils/AudioCompare.js @@ -12,6 +12,7 @@ const AudioRecorder = (props) => { const [audioBlob, setAudioBlob] = useState(null); const recorderRef = useRef(null); const mediaStreamRef = useRef(null); + const [showLoader, setShowLoader] = useState(false); useEffect(() => { // Cleanup when component unmounts @@ -53,29 +54,33 @@ const AudioRecorder = (props) => { }; const stopRecording = () => { - setStatus("inactive"); - if (recorderRef.current) { - recorderRef.current.stopRecording(() => { - const blob = recorderRef.current.getBlob(); + setShowLoader(true); + setTimeout(() => { + setShowLoader(false); + setStatus("inactive"); + if (recorderRef.current) { + recorderRef.current.stopRecording(() => { + const blob = recorderRef.current.getBlob(); - if (blob) { - setAudioBlob(blob); - saveBlob(blob); // Persist the blob - } else { - console.error("Failed to retrieve audio blob."); - } + if (blob) { + setAudioBlob(blob); + saveBlob(blob); // Persist the blob + } else { + console.error("Failed to retrieve audio blob."); + } - // Stop the media stream - if (mediaStreamRef.current) { - mediaStreamRef.current.getTracks().forEach((track) => track.stop()); - } + // Stop the media stream + if (mediaStreamRef.current) { + mediaStreamRef.current.getTracks().forEach((track) => track.stop()); + } - setIsRecording(false); - }); - } - if (props.setEnableNext) { - props.setEnableNext(true); - } + setIsRecording(false); + }); + } + if (props.setEnableNext) { + props.setEnableNext(true); + } + }, 1000); }; const saveBlob = (blob) => { @@ -100,6 +105,7 @@ const AudioRecorder = (props) => { > From ce83cc2480829e4b04de4588cf99c2b2bbd55620 Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Thu, 19 Dec 2024 17:09:48 +0530 Subject: [PATCH 05/18] Issueid #231949 feat: Show loading until next content is not loaded --- src/index.css | 34 ++++++++++------------------------ src/utils/AudioCompare.js | 2 +- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/src/index.css b/src/index.css index 38614c5d..01c3f5db 100644 --- a/src/index.css +++ b/src/index.css @@ -1,31 +1,17 @@ -.stopButtonLoader { - height: 70px; - aspect-ratio: 1; - border-radius: 50%; - /* background: #000; */ - box-shadow: 0 0 0 0 rgba(226, 45, 45, 0.267); - animation: l2 1.5s infinite linear; - position: relative; -} - -.stopButtonLoader:before, -.stopButtonLoader:after { - content: ""; - position: absolute; - inset: 0; - border-radius: inherit; - box-shadow: 0 0 0 0 #0004; - animation: inherit; - animation-delay: -0.5s; -} - -.stopButtonLoader:after { - animation-delay: -1s; +.loader { + width: 120px; + height: 20px; + margin-bottom: 15px; + border-radius: 20px; + background: + linear-gradient(rgb(80, 216, 1) 0 0) 0/0% no-repeat lightblue; + animation: l2 1s infinite steps(10); + /* border: 2px solid ; */ } @keyframes l2 { 100% { - box-shadow: 0 0 0 40px #fc2c2c00 + background-size: 110% } } diff --git a/src/utils/AudioCompare.js b/src/utils/AudioCompare.js index 32fa8f2a..0514139e 100644 --- a/src/utils/AudioCompare.js +++ b/src/utils/AudioCompare.js @@ -103,9 +103,9 @@ const AudioRecorder = (props) => { margin: "0 auto", }} > + {showLoader &&
} From f29d6d305d9eafb894e1b3365415a4127a70b12e Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Thu, 19 Dec 2024 17:27:50 +0530 Subject: [PATCH 06/18] Issueid #231949 feat: Show loading until next content is not loaded --- src/index.css | 4 ++-- src/utils/AudioCompare.js | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/index.css b/src/index.css index 01c3f5db..110c2205 100644 --- a/src/index.css +++ b/src/index.css @@ -1,7 +1,7 @@ .loader { - width: 120px; + width: 250px; height: 20px; - margin-bottom: 15px; + margin-top: 80px; border-radius: 20px; background: linear-gradient(rgb(80, 216, 1) 0 0) 0/0% no-repeat lightblue; diff --git a/src/utils/AudioCompare.js b/src/utils/AudioCompare.js index 0514139e..92769043 100644 --- a/src/utils/AudioCompare.js +++ b/src/utils/AudioCompare.js @@ -103,16 +103,19 @@ const AudioRecorder = (props) => { margin: "0 auto", }} > - {showLoader &&
} - - - + {showLoader ? ( +
+ ) : ( + + + + )} ); } else { From 67312679e30e1694c1eedb4c21fa0ce916228a1d Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Thu, 19 Dec 2024 17:55:31 +0530 Subject: [PATCH 07/18] Issueid #231949 feat: Show loading until next content is not loaded --- src/index.css | 1 - src/utils/AudioCompare.js | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/index.css b/src/index.css index 110c2205..538f665f 100644 --- a/src/index.css +++ b/src/index.css @@ -6,7 +6,6 @@ background: linear-gradient(rgb(80, 216, 1) 0 0) 0/0% no-repeat lightblue; animation: l2 1s infinite steps(10); - /* border: 2px solid ; */ } @keyframes l2 { diff --git a/src/utils/AudioCompare.js b/src/utils/AudioCompare.js index 92769043..64f3d903 100644 --- a/src/utils/AudioCompare.js +++ b/src/utils/AudioCompare.js @@ -5,6 +5,7 @@ import { ListenButton, RetryIcon, SpeakButton, StopButton } from "./constants"; import RecordVoiceVisualizer from "./RecordVoiceVisualizer"; import playButton from "../../src/assets/listen.png"; import pauseButton from "../../src/assets/pause.png"; +import PropTypes from "prop-types"; const AudioRecorder = (props) => { const [isRecording, setIsRecording] = useState(false); @@ -215,4 +216,11 @@ const AudioRecorder = (props) => { ); }; +AudioRecorder.propTypes = { + enableAfterLoad: PropTypes.bool, + showOnlyListen: PropTypes.bool, + recordedAudio: PropTypes.string, + originalText: PropTypes.string, +}; + export default AudioRecorder; From 7fc647ecc67a520db77d2a76dd5532187b5437f1 Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Thu, 19 Dec 2024 18:34:07 +0530 Subject: [PATCH 08/18] Issueid #231949 feat: Show loading until next content is not loaded --- src/utils/AudioCompare.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/utils/AudioCompare.js b/src/utils/AudioCompare.js index 64f3d903..319d8134 100644 --- a/src/utils/AudioCompare.js +++ b/src/utils/AudioCompare.js @@ -56,32 +56,28 @@ const AudioRecorder = (props) => { const stopRecording = () => { setShowLoader(true); - setTimeout(() => { + const timeoutId = setTimeout(() => { setShowLoader(false); setStatus("inactive"); if (recorderRef.current) { recorderRef.current.stopRecording(() => { const blob = recorderRef.current.getBlob(); - if (blob) { setAudioBlob(blob); - saveBlob(blob); // Persist the blob + saveBlob(blob); } else { console.error("Failed to retrieve audio blob."); } - - // Stop the media stream if (mediaStreamRef.current) { mediaStreamRef.current.getTracks().forEach((track) => track.stop()); } - setIsRecording(false); + props.setEnableNext?.(true); }); } - if (props.setEnableNext) { - props.setEnableNext(true); - } }, 1000); + + return () => clearTimeout(timeoutId); }; const saveBlob = (blob) => { From 958c4eff738e178914697671e8311e002b4ea87b Mon Sep 17 00:00:00 2001 From: Gourav More Date: Sun, 22 Dec 2024 20:01:29 +0530 Subject: [PATCH 09/18] Issue #232165 fix: Performance improvements --- .github/workflows/all-dev-tn.yml | 2 +- .gitignore | 2 +- src/App.js | 16 +++- src/components/Assesment/Assesment.jsx | 12 +-- src/components/AssesmentEnd/AssesmentEnd.jsx | 11 ++- .../DiscoverSentance/DiscoverSentance.jsx | 59 ++++++++------- src/services/telementryService.js | 27 ++++--- src/views/Practice/Practice.jsx | 75 ++++++++++--------- 8 files changed, 119 insertions(+), 85 deletions(-) diff --git a/.github/workflows/all-dev-tn.yml b/.github/workflows/all-dev-tn.yml index 704ff20f..459eaac3 100644 --- a/.github/workflows/all-dev-tn.yml +++ b/.github/workflows/all-dev-tn.yml @@ -3,7 +3,7 @@ name: ALL tn dev Deployment on: push: branches: - - all-1.2-tn-dev + - release-1.3-apicall-optimisation jobs: deploy: diff --git a/.gitignore b/.gitignore index bf1a9da0..52c9b6e8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,7 @@ lerna-debug.log* .env #build build - +*.env* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/src/App.js b/src/App.js index 76eac97c..037205a8 100644 --- a/src/App.js +++ b/src/App.js @@ -6,7 +6,7 @@ import FingerprintJS from "@fingerprintjs/fingerprintjs"; import routes from "./routes"; import { AppContent } from "./views"; import theme from "./assets/styles/theme"; -import { initialize } from "./services/telementryService"; +import { initialize, end } from "./services/telementryService"; import { startEvent } from "./services/callTelemetryIntract"; import "@project-sunbird/telemetry-sdk/index.js"; import { getParameter } from "./utils/constants"; @@ -72,6 +72,20 @@ const App = () => { setFp(); }, []); + useEffect(() => { + const handleBeforeUnload = (event) => { + window.telemetry && window.telemetry.syncEvents && window.telemetry.syncEvents(); + }; + + // Add the event listener + window.addEventListener("beforeunload", handleBeforeUnload); + + // Cleanup the event listener on component unmount + return () => { + window.removeEventListener("beforeunload", handleBeforeUnload); + }; + }, []); + useEffect(() => { let virtualId; diff --git a/src/components/Assesment/Assesment.jsx b/src/components/Assesment/Assesment.jsx index 36a7598f..1a1cf895 100644 --- a/src/components/Assesment/Assesment.jsx +++ b/src/components/Assesment/Assesment.jsx @@ -598,10 +598,12 @@ const Assesment = ({ discoverStart }) => { } localStorage.setItem("lang", lang || "ta"); - const getPointersDetails = await axios.get( - `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${usernameDetails?.data?.result?.virtualID}/${session_id}?language=${lang}` - ); - setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); + if (localStorage.getItem("contentSessionId") !== null) { + const getPointersDetails = await axios.get( + `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${usernameDetails?.data?.result?.virtualID}/${session_id}?language=${lang}` + ); + setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); + } dispatch(setVirtualId(usernameDetails?.data?.result?.virtualID)); })(); @@ -635,7 +637,7 @@ const Assesment = ({ discoverStart }) => { localStorage.setItem("sessionId", sessionId); } - if (virtualId) { + if (virtualId && localStorage.getItem("contentSessionId") !== null) { const getPointersDetails = await axios.get( `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${virtualId}/${sessionId}?language=${lang}` ); diff --git a/src/components/AssesmentEnd/AssesmentEnd.jsx b/src/components/AssesmentEnd/AssesmentEnd.jsx index e3b1001a..a7c682f6 100644 --- a/src/components/AssesmentEnd/AssesmentEnd.jsx +++ b/src/components/AssesmentEnd/AssesmentEnd.jsx @@ -44,10 +44,13 @@ const AssesmentEnd = () => { sessionId = uniqueId(); localStorage.setItem("sessionId", sessionId) } - const getPointersDetails = await axios.get( - `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${virtualId}/${sessionId}?language=${lang}` - ); - setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); + + if (localStorage.getItem("contentSessionId") !== null) { + const getPointersDetails = await axios.get( + `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${virtualId}/${sessionId}?language=${lang}` + ); + setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); + } })(); setTimeout(() => { setShake(false); diff --git a/src/components/DiscoverSentance/DiscoverSentance.jsx b/src/components/DiscoverSentance/DiscoverSentance.jsx index 9b574271..17e85321 100644 --- a/src/components/DiscoverSentance/DiscoverSentance.jsx +++ b/src/components/DiscoverSentance/DiscoverSentance.jsx @@ -118,35 +118,18 @@ const SpeakSentenceComponent = () => { try { const lang = getLocalData("lang"); - if (!(localStorage.getItem("contentSessionId") !== null)) { - const pointsRes = await axios.post( - `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.ADD_POINTER}`, - { - userId: localStorage.getItem("virtualId"), - sessionId: localStorage.getItem("sessionId"), - points: 1, - language: lang, - milestone: "m0", - } - ); - setPoints(pointsRes?.data?.result?.totalLanguagePoints || 0); - } else { - send(1); - // setPoints(localStorage.getItem("currentLessonScoreCount")); - } - - await axios.post( - `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.ADD_LESSON}`, - { - userId: localStorage.getItem("virtualId"), - sessionId: localStorage.getItem("sessionId"), - milestone: `discoveryList/discovery/${currentCollectionId}`, - lesson: localStorage.getItem("storyTitle"), - progress: ((currentQuestion + 1) * 100) / questions.length, - language: lang, - milestoneLevel: "m0", - } - ); + // await axios.post( + // `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.ADD_LESSON}`, + // { + // userId: localStorage.getItem("virtualId"), + // sessionId: localStorage.getItem("sessionId"), + // milestone: `discoveryList/discovery/${currentCollectionId}`, + // lesson: localStorage.getItem("storyTitle"), + // progress: ((currentQuestion + 1) * 100) / questions.length, + // language: lang, + // milestoneLevel: "m0", + // } + // ); if (currentQuestion < questions.length - 1) { setCurrentQuestion(currentQuestion + 1); @@ -164,6 +147,24 @@ const SpeakSentenceComponent = () => { language: localStorage.getItem("lang"), } ); + + if (!(localStorage.getItem("contentSessionId") !== null)) { + const pointsRes = await axios.post( + `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.ADD_POINTER}`, + { + userId: localStorage.getItem("virtualId"), + sessionId: localStorage.getItem("sessionId"), + points: 1, + language: lang, + milestone: "m0", + } + ); + setPoints(pointsRes?.data?.result?.totalLanguagePoints || 0); + } else { + send(5); + // setPoints(localStorage.getItem("currentLessonScoreCount")); + } + setInitialAssesment(false); const { data: getSetData } = getSetResultRes; const data = JSON.stringify(getSetData?.data); diff --git a/src/services/telementryService.js b/src/services/telementryService.js index cd51adbe..c946756a 100644 --- a/src/services/telementryService.js +++ b/src/services/telementryService.js @@ -3,6 +3,7 @@ import { CsTelemetryModule } from '@project-sunbird/client-services/telemetry'; import { uniqueId } from './utilService'; import { jwtDecode } from '../../node_modules/jwt-decode/build/cjs/index'; +let startTime; // Variable to store the timestamp when the start event is raised let contentSessionId; let playSessionId; let url; @@ -65,6 +66,7 @@ export const initialize = async ({ context, config, metadata }) => { export const start = (duration) => { try { + startTime = Date.now(); // Record the start time CsTelemetryModule.instance.telemetryService.raiseStartTelemetry({ options: getEventOptions(), edata: { @@ -114,15 +116,22 @@ export const Log = (context, pageid, telemetryMode) => { }; export const end = (data) => { - CsTelemetryModule.instance.telemetryService.raiseEndTelemetry({ - edata: { - type: 'content', - mode: 'play', - pageid: url, - summary: data?.summary || {}, - duration: data?.duration || '000', - }, - }); + try { + const endTime = Date.now(); // Record the end time + const duration = ((endTime - startTime) / 1000).toFixed(2); // Calculate duration in seconds + + CsTelemetryModule.instance.telemetryService.raiseEndTelemetry({ + edata: { + type: 'content', + mode: 'play', + pageid: url, + summary: data?.summary || {}, + duration: duration, // Log the calculated duration + }, + }); + } catch (error) { + console.error("Error in end telemetry event:", error); + } }; export const interact = (telemetryMode) => { diff --git a/src/views/Practice/Practice.jsx b/src/views/Practice/Practice.jsx index 206d8107..b0b7873d 100644 --- a/src/views/Practice/Practice.jsx +++ b/src/views/Practice/Practice.jsx @@ -177,24 +177,6 @@ const Practice = () => { try { const lang = getLocalData("lang"); - if (localStorage.getItem("contentSessionId") !== null) { - setPoints(1); - if (isShowCase) { - send(1); - } - } else { - const pointsRes = await axios.post( - `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.ADD_POINTER}`, - { - userId: localStorage.getItem("virtualId"), - sessionId: localStorage.getItem("sessionId"), - points: 1, - language: lang, - milestone: `m${level}`, - } - ); - setPoints(pointsRes?.data?.result?.totalLanguagePoints || 0); - } const virtualId = getLocalData("virtualId"); const sessionId = getLocalData("sessionId"); @@ -217,18 +199,18 @@ const Practice = () => { let showcasePercentage = ((currentQuestion + 1) * 100) / questions.length; - await axios.post( - `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.ADD_LESSON}`, - { - userId: virtualId, - sessionId: sessionId, - milestone: isShowCase ? "showcase" : `practice`, - lesson: currentPracticeStep, - progress: isShowCase ? showcasePercentage : currentPracticeProgress, - language: lang, - milestoneLevel: `m${level}`, - } - ); + // await axios.post( + // `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.ADD_LESSON}`, + // { + // userId: virtualId, + // sessionId: sessionId, + // milestone: isShowCase ? "showcase" : `practice`, + // lesson: currentPracticeStep, + // progress: isShowCase ? showcasePercentage : currentPracticeProgress, + // language: lang, + // milestoneLevel: `m${level}`, + // } + // ); let newPracticeStep = currentQuestion === questions.length - 1 || isGameOver @@ -245,6 +227,27 @@ const Practice = () => { ); if (currentQuestion === questions.length - 1 || isGameOver) { + + // Set points + if (localStorage.getItem("contentSessionId") !== null) { + setPoints(1); + if (isShowCase) { + send(5); + } + } else { + const pointsRes = await axios.post( + `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.ADD_POINTER}`, + { + userId: localStorage.getItem("virtualId"), + sessionId: localStorage.getItem("sessionId"), + points: 1, + language: lang, + milestone: `m${level}`, + } + ); + setPoints(pointsRes?.data?.result?.totalLanguagePoints || 0); + } + // navigate or setNextPracticeLevel let currentPracticeStep = practiceProgress[virtualId].currentPracticeStep; @@ -471,12 +474,14 @@ const Practice = () => { // TODO: Handle Error for lessons - no lesson progress - starting point should be P1 - const getPointersDetails = await axios.get( - `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${virtualId}/${sessionId}?language=${lang}` - ); + if (localStorage.getItem("contentSessionId") !== null) { + const getPointersDetails = await axios.get( + `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${virtualId}/${sessionId}?language=${lang}` + ); - // TODO: Just Opss icon - we are trying to fetch the score for you - setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); + // TODO: Just Opss icon - we are trying to fetch the score for you + setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); + } let userState = Number.isInteger( Number(resLessons.data?.result?.result?.lesson) From 03895ff915063ddea1ce06b635eeca8afe44e504 Mon Sep 17 00:00:00 2001 From: Gourav More Date: Sun, 22 Dec 2024 20:37:10 +0530 Subject: [PATCH 10/18] Issue #232165 fix: ALL-1.3 Performance improvements --- src/views/Practice/Practice.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/views/Practice/Practice.jsx b/src/views/Practice/Practice.jsx index b0b7873d..a3c2289c 100644 --- a/src/views/Practice/Practice.jsx +++ b/src/views/Practice/Practice.jsx @@ -227,6 +227,10 @@ const Practice = () => { ); if (currentQuestion === questions.length - 1 || isGameOver) { + // navigate or setNextPracticeLevel + let currentPracticeStep = + practiceProgress[virtualId].currentPracticeStep; + let isShowCase = currentPracticeStep === 4 || currentPracticeStep === 9; // P4 or P8 // Set points if (localStorage.getItem("contentSessionId") !== null) { @@ -247,11 +251,7 @@ const Practice = () => { ); setPoints(pointsRes?.data?.result?.totalLanguagePoints || 0); } - - // navigate or setNextPracticeLevel - let currentPracticeStep = - practiceProgress[virtualId].currentPracticeStep; - let isShowCase = currentPracticeStep === 4 || currentPracticeStep === 9; // P4 or P8 + if (isShowCase || isGameOver) { // assesment From 5d738caf7a3c2c2ca375796f873829dc40ed0e3f Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Tue, 31 Dec 2024 15:22:24 +0530 Subject: [PATCH 11/18] #00000 zoom icon visble event after image is not there --- src/components/Practice/Mechanics3.jsx | 56 +++++++++++---------- src/components/Practice/Mechanics5.jsx | 70 +++++++++++++------------- 2 files changed, 66 insertions(+), 60 deletions(-) diff --git a/src/components/Practice/Mechanics3.jsx b/src/components/Practice/Mechanics3.jsx index e89913f0..3a560810 100644 --- a/src/components/Practice/Mechanics3.jsx +++ b/src/components/Practice/Mechanics3.jsx @@ -261,8 +261,8 @@ const Mechanics2 = ({ }, }} > - - {image && ( + {image?.split("/")?.[4] && ( + setZoomOpen(true)} src={image} @@ -273,32 +273,36 @@ const Mechanics2 = ({ }} alt="" /> - )} - {/* Subtle gradient overlay across the top */} - - {/* Zoom icon positioned in the top-left corner */} - setZoomOpen(true)} - sx={{ color: "white", fontSize: "22px", cursor: "pointer" }} - /> + {/* Subtle gradient overlay across the top */} + + {/* Zoom icon positioned in the top-left corner */} + setZoomOpen(true)} + sx={{ + color: "white", + fontSize: "22px", + cursor: "pointer", + }} + /> + - + )} setZoomOpen(false)} diff --git a/src/components/Practice/Mechanics5.jsx b/src/components/Practice/Mechanics5.jsx index 76121f04..dd9c3552 100644 --- a/src/components/Practice/Mechanics5.jsx +++ b/src/components/Practice/Mechanics5.jsx @@ -185,42 +185,44 @@ const Mechanics5 = ({ > {/* Image with full-width gradient overlay on top */} - - contentImage setZoomOpen(true)} // Open modal on click - /> - - {/* Subtle gradient overlay across the top */} - - {/* Zoom icon positioned in the top-left corner */} - setZoomOpen(true)} - sx={{ color: "white", fontSize: "22px", cursor: "pointer" }} + {image?.split("/")?.[4] && ( + + contentImage setZoomOpen(true)} // Open modal on click /> + + {/* Subtle gradient overlay across the top */} + + {/* Zoom icon positioned in the top-left corner */} + setZoomOpen(true)} + sx={{ color: "white", fontSize: "22px", cursor: "pointer" }} + /> + - + )} {/* Modal for zoomed image with gradient and close icon */} Date: Thu, 2 Jan 2025 15:28:05 +0530 Subject: [PATCH 12/18] Update all-dev-tn.yml --- .github/workflows/all-dev-tn.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/all-dev-tn.yml b/.github/workflows/all-dev-tn.yml index 459eaac3..33c477cb 100644 --- a/.github/workflows/all-dev-tn.yml +++ b/.github/workflows/all-dev-tn.yml @@ -3,7 +3,7 @@ name: ALL tn dev Deployment on: push: branches: - - release-1.3-apicall-optimisation + - all-1.3.1-dev jobs: deploy: From f1958f4e70a842933d9e3af76cee4ec13a9dc5bb Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Thu, 2 Jan 2025 16:34:22 +0530 Subject: [PATCH 13/18] #00000 zoom icon visble event after image is not there --- src/utils/VoiceAnalyser.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/utils/VoiceAnalyser.js b/src/utils/VoiceAnalyser.js index b5b4de74..e575bb54 100644 --- a/src/utils/VoiceAnalyser.js +++ b/src/utils/VoiceAnalyser.js @@ -276,8 +276,6 @@ function VoiceAnalyser(props) { fetchASROutput(lang, recordedAudioBase64); setLoader(true); setEnableAfterLoad(false); - } else { - alert("please record again"); } } }, [props.isNextButtonCalled]); From afa7073a8e0ffd537d05f8db3ce4ffc66fb65f5c Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Fri, 3 Jan 2025 15:20:53 +0530 Subject: [PATCH 14/18] issueid #231942 fix: audio delay right after completing level in practice --- src/components/AssesmentEnd/AssesmentEnd.jsx | 37 +++++++++-- src/components/DiscoverEnd/DiscoverEnd.jsx | 47 +++++++++---- .../DiscoverSentance/DiscoverSentance.jsx | 29 +++++++- src/components/Layouts.jsx/MainLayout.jsx | 66 ++++++++++++++++--- src/views/Practice/Practice.jsx | 7 ++ 5 files changed, 155 insertions(+), 31 deletions(-) diff --git a/src/components/AssesmentEnd/AssesmentEnd.jsx b/src/components/AssesmentEnd/AssesmentEnd.jsx index e3b1001a..ba54b5e4 100644 --- a/src/components/AssesmentEnd/AssesmentEnd.jsx +++ b/src/components/AssesmentEnd/AssesmentEnd.jsx @@ -16,7 +16,7 @@ import { useEffect, useState } from "react"; import LevelCompleteAudio from "../../assets/audio/levelComplete.wav"; import { ProfileHeader } from "../Assesment/Assesment"; import desktopLevel5 from "../../assets/images/assesmentComplete.png"; -import config from '../../utils/urlConstants.json'; +import config from "../../utils/urlConstants.json"; import { uniqueId } from "../../services/utilService"; const AssesmentEnd = () => { @@ -25,10 +25,35 @@ const AssesmentEnd = () => { const [previousLevel, setPreviousLevel] = useState(""); const [points, setPoints] = useState(0); + const [audioSrc, setAudioSrc] = useState(null); + + useEffect(() => { + const preloadAudio = async () => { + try { + const response = await fetch(LevelCompleteAudio); + const audioBlob = await response.blob(); + const audioUrl = URL.createObjectURL(audioBlob); + setAudioSrc(audioUrl); + } catch (error) { + console.error("Error loading audio:", error); + } + }; + preloadAudio(); + + return () => { + // Cleanup blob URL to prevent memory leaks + if (audioSrc) { + URL.revokeObjectURL(audioSrc); + } + }; + }, []); + useEffect(() => { (async () => { - let audio = new Audio(LevelCompleteAudio); - audio.play(); + if (audioSrc) { + let audio = new Audio(audioSrc); + audio.play(); + } const virtualId = getLocalData("virtualId"); const lang = getLocalData("lang"); const previous_level = getLocalData("previous_level"); @@ -40,9 +65,9 @@ const AssesmentEnd = () => { setLevel(data.data.milestone_level); setLocalData("userLevel", data.data.milestone_level?.replace("m", "")); let sessionId = getLocalData("sessionId"); - if (!sessionId){ + if (!sessionId) { sessionId = uniqueId(); - localStorage.setItem("sessionId", sessionId) + localStorage.setItem("sessionId", sessionId); } const getPointersDetails = await axios.get( `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${virtualId}/${sessionId}?language=${lang}` @@ -52,7 +77,7 @@ const AssesmentEnd = () => { setTimeout(() => { setShake(false); }, 4000); - }, []); + }, [audioSrc]); const navigate = useNavigate(); let newLevel = level.replace("m", ""); diff --git a/src/components/DiscoverEnd/DiscoverEnd.jsx b/src/components/DiscoverEnd/DiscoverEnd.jsx index 81a96cb3..7fc4ecc0 100644 --- a/src/components/DiscoverEnd/DiscoverEnd.jsx +++ b/src/components/DiscoverEnd/DiscoverEnd.jsx @@ -8,12 +8,8 @@ import back from "../../assets/images/back-arrow.svg"; import discoverEndLeft from "../../assets/images/discover-end-left.svg"; import discoverEndRight from "../../assets/images/discover-end-right.svg"; import textureImage from "../../assets/images/textureImage.png"; -import { - LetsStart, - getLocalData, - setLocalData, -} from "../../utils/constants"; -import config from '../../utils/urlConstants.json'; +import { LetsStart, getLocalData, setLocalData } from "../../utils/constants"; +import config from "../../utils/urlConstants.json"; const sectionStyle = { backgroundImage: `url(${textureImage})`, @@ -32,12 +28,35 @@ const sectionStyle = { const SpeakSentenceComponent = () => { const [shake, setShake] = useState(true); const [level, setLevel] = useState(""); + const [audioSrc, setAudioSrc] = useState(null); + + useEffect(() => { + const preloadAudio = async () => { + try { + const response = await fetch(LevelCompleteAudio); + const audioBlob = await response.blob(); + const audioUrl = URL.createObjectURL(audioBlob); + setAudioSrc(audioUrl); + } catch (error) { + console.error("Error loading audio:", error); + } + }; + preloadAudio(); + + return () => { + // Cleanup blob URL to prevent memory leaks + if (audioSrc) { + URL.revokeObjectURL(audioSrc); + } + }; + }, []); useEffect(() => { - (async () => { - let audio = new Audio(LevelCompleteAudio); - audio.play(); + if (audioSrc) { + let audio = new Audio(audioSrc); + audio.play(); + } const virtualId = getLocalData("virtualId"); const lang = getLocalData("lang"); const getMilestoneDetails = await axios.get( @@ -50,14 +69,14 @@ const SpeakSentenceComponent = () => { setTimeout(() => { setShake(false); }, 4000); - }, []); + }, [audioSrc]); const handleProfileBack = () => { try { - if (process.env.REACT_APP_IS_APP_IFRAME === 'true') { - navigate("/") + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + navigate("/"); } else { - navigate("/discover-start") + navigate("/discover-start"); } } catch (error) { console.error("Error posting message:", error); @@ -132,7 +151,7 @@ const SpeakSentenceComponent = () => { handleProfileBack()} + onClick={handleProfileBack} sx={{ display: "flex", justifyContent: "center", diff --git a/src/components/DiscoverSentance/DiscoverSentance.jsx b/src/components/DiscoverSentance/DiscoverSentance.jsx index 9b574271..9eaae9d7 100644 --- a/src/components/DiscoverSentance/DiscoverSentance.jsx +++ b/src/components/DiscoverSentance/DiscoverSentance.jsx @@ -36,9 +36,36 @@ const SpeakSentenceComponent = () => { const [openMessageDialog, setOpenMessageDialog] = useState(""); const [totalSyllableCount, setTotalSyllableCount] = useState(""); const [isNextButtonCalled, setIsNextButtonCalled] = useState(false); + const [audioSrc, setAudioSrc] = useState(null); + + useEffect(() => { + const preloadAudio = async () => { + try { + const response = await fetch(LevelCompleteAudio); + const audioBlob = await response.blob(); + const audioUrl = URL.createObjectURL(audioBlob); + setAudioSrc(audioUrl); + } catch (error) { + console.error("Error loading audio:", error); + } + }; + preloadAudio(); + + return () => { + // Cleanup blob URL to prevent memory leaks + if (audioSrc) { + URL.revokeObjectURL(audioSrc); + } + }; + }, []); const callConfettiAndPlay = () => { - let audio = new Audio(LevelCompleteAudio); + let audio; + if (audioSrc) { + audio = new Audio(audioSrc); + } else { + audio = new Audio(LevelCompleteAudio); + } audio.play(); callConfetti(); }; diff --git a/src/components/Layouts.jsx/MainLayout.jsx b/src/components/Layouts.jsx/MainLayout.jsx index e875a708..97058a61 100644 --- a/src/components/Layouts.jsx/MainLayout.jsx +++ b/src/components/Layouts.jsx/MainLayout.jsx @@ -163,27 +163,70 @@ const MainLayout = (props) => { } }; + const [audioCache, setAudioCache] = useState({}); + + useEffect(() => { + const preloadAudio = async () => { + try { + const urls = [LevelCompleteAudio, gameLoseAudio]; + const cache = {}; + + for (const url of urls) { + const response = await fetch(url); + const audioBlob = await response.blob(); + const audioUrl = URL.createObjectURL(audioBlob); + cache[url] = audioUrl; + } + + setAudioCache(cache); + } catch (error) { + console.error("Error preloading audio:", error); + } + }; + + preloadAudio(); + + // Cleanup cached audio URLs on unmount + return () => { + Object.values(audioCache).forEach((audioUrl) => + URL.revokeObjectURL(audioUrl) + ); + }; + }, []); + useEffect(() => { if (isShowCase && gameOverData) { - setShake(gameOverData ? gameOverData.userWon : true); + setShake(gameOverData.userWon ?? true); - let audio = ""; + let audioSrc; if (gameOverData) { - audio = new Audio( - gameOverData.userWon ? LevelCompleteAudio : gameLoseAudio - ); + audioSrc = gameOverData.userWon + ? audioCache[LevelCompleteAudio] + : audioCache[gameLoseAudio]; + } else { + audioSrc = audioCache[LevelCompleteAudio]; + } + + if (audioSrc) { + const audio = new Audio(audioSrc); + audio.play().catch((error) => { + console.error("Error playing audio:", error); + }); + if (!gameOverData?.userWon) { callConfettiSnow(); } - } else { - audio = new Audio(LevelCompleteAudio); } - audio.play(); - setTimeout(() => { + + const shakeTimeout = setTimeout(() => { setShake(false); }, 4000); + + return () => { + clearTimeout(shakeTimeout); + }; } - }, [startShowCase, isShowCase, gameOverData]); + }, [startShowCase, isShowCase, gameOverData, audioCache]); let currentPracticeStep = progressData?.currentPracticeStep; let currentPracticeProgress = progressData?.currentPracticeProgress || 0; @@ -1196,6 +1239,9 @@ MainLayout.propTypes = { storedData: PropTypes.array, resetStoredData: PropTypes.func, pageName: PropTypes.string, + gameOverData: PropTypes.shape({ + userWon: PropTypes.bool, + }), }; export default MainLayout; diff --git a/src/views/Practice/Practice.jsx b/src/views/Practice/Practice.jsx index 206d8107..742acc25 100644 --- a/src/views/Practice/Practice.jsx +++ b/src/views/Practice/Practice.jsx @@ -84,6 +84,13 @@ const Practice = () => { } }; preloadAudio(); + + return () => { + // Cleanup blob URL to prevent memory leaks + if (audioSrc) { + URL.revokeObjectURL(audioSrc); + } + }; }, []); const callConfettiAndPlay = () => { From 2126a0b94915fa44f6a8d0ddf70925c8452a5545 Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Fri, 3 Jan 2025 17:06:53 +0530 Subject: [PATCH 15/18] issueid #231942 fix: audio delay right after completing level in practice --- src/components/AssesmentEnd/AssesmentEnd.jsx | 31 +++------------- src/components/DiscoverEnd/DiscoverEnd.jsx | 30 +++------------- .../DiscoverSentance/DiscoverSentance.jsx | 31 +++------------- src/hooks/usePreloadAudio.jsx | 29 +++++++++++++++ src/views/Practice/Practice.jsx | 35 +++---------------- 5 files changed, 47 insertions(+), 109 deletions(-) create mode 100644 src/hooks/usePreloadAudio.jsx diff --git a/src/components/AssesmentEnd/AssesmentEnd.jsx b/src/components/AssesmentEnd/AssesmentEnd.jsx index ba54b5e4..e88504dd 100644 --- a/src/components/AssesmentEnd/AssesmentEnd.jsx +++ b/src/components/AssesmentEnd/AssesmentEnd.jsx @@ -18,40 +18,19 @@ import { ProfileHeader } from "../Assesment/Assesment"; import desktopLevel5 from "../../assets/images/assesmentComplete.png"; import config from "../../utils/urlConstants.json"; import { uniqueId } from "../../services/utilService"; +import usePreloadAudio from "../../hooks/usePreloadAudio"; const AssesmentEnd = () => { const [shake, setShake] = useState(true); const [level, setLevel] = useState(""); const [previousLevel, setPreviousLevel] = useState(""); const [points, setPoints] = useState(0); - - const [audioSrc, setAudioSrc] = useState(null); - - useEffect(() => { - const preloadAudio = async () => { - try { - const response = await fetch(LevelCompleteAudio); - const audioBlob = await response.blob(); - const audioUrl = URL.createObjectURL(audioBlob); - setAudioSrc(audioUrl); - } catch (error) { - console.error("Error loading audio:", error); - } - }; - preloadAudio(); - - return () => { - // Cleanup blob URL to prevent memory leaks - if (audioSrc) { - URL.revokeObjectURL(audioSrc); - } - }; - }, []); + const levelCompleteAudioSrc = usePreloadAudio(LevelCompleteAudio); useEffect(() => { (async () => { - if (audioSrc) { - let audio = new Audio(audioSrc); + if (levelCompleteAudioSrc) { + let audio = new Audio(levelCompleteAudioSrc); audio.play(); } const virtualId = getLocalData("virtualId"); @@ -77,7 +56,7 @@ const AssesmentEnd = () => { setTimeout(() => { setShake(false); }, 4000); - }, [audioSrc]); + }, [levelCompleteAudioSrc]); const navigate = useNavigate(); let newLevel = level.replace("m", ""); diff --git a/src/components/DiscoverEnd/DiscoverEnd.jsx b/src/components/DiscoverEnd/DiscoverEnd.jsx index 7fc4ecc0..4769d97c 100644 --- a/src/components/DiscoverEnd/DiscoverEnd.jsx +++ b/src/components/DiscoverEnd/DiscoverEnd.jsx @@ -10,6 +10,7 @@ import discoverEndRight from "../../assets/images/discover-end-right.svg"; import textureImage from "../../assets/images/textureImage.png"; import { LetsStart, getLocalData, setLocalData } from "../../utils/constants"; import config from "../../utils/urlConstants.json"; +import usePreloadAudio from "../../hooks/usePreloadAudio"; const sectionStyle = { backgroundImage: `url(${textureImage})`, @@ -28,33 +29,12 @@ const sectionStyle = { const SpeakSentenceComponent = () => { const [shake, setShake] = useState(true); const [level, setLevel] = useState(""); - const [audioSrc, setAudioSrc] = useState(null); - - useEffect(() => { - const preloadAudio = async () => { - try { - const response = await fetch(LevelCompleteAudio); - const audioBlob = await response.blob(); - const audioUrl = URL.createObjectURL(audioBlob); - setAudioSrc(audioUrl); - } catch (error) { - console.error("Error loading audio:", error); - } - }; - preloadAudio(); - - return () => { - // Cleanup blob URL to prevent memory leaks - if (audioSrc) { - URL.revokeObjectURL(audioSrc); - } - }; - }, []); + const levelCompleteAudioSrc = usePreloadAudio(LevelCompleteAudio); useEffect(() => { (async () => { - if (audioSrc) { - let audio = new Audio(audioSrc); + if (levelCompleteAudioSrc) { + let audio = new Audio(levelCompleteAudioSrc); audio.play(); } const virtualId = getLocalData("virtualId"); @@ -69,7 +49,7 @@ const SpeakSentenceComponent = () => { setTimeout(() => { setShake(false); }, 4000); - }, [audioSrc]); + }, [levelCompleteAudioSrc]); const handleProfileBack = () => { try { diff --git a/src/components/DiscoverSentance/DiscoverSentance.jsx b/src/components/DiscoverSentance/DiscoverSentance.jsx index 9eaae9d7..25ad96ed 100644 --- a/src/components/DiscoverSentance/DiscoverSentance.jsx +++ b/src/components/DiscoverSentance/DiscoverSentance.jsx @@ -14,6 +14,7 @@ import LevelCompleteAudio from "../../assets/audio/levelComplete.wav"; import config from "../../utils/urlConstants.json"; import { MessageDialog } from "../Assesment/Assesment"; import { Log } from "../../services/telementryService"; +import usePreloadAudio from "../../hooks/usePreloadAudio"; const SpeakSentenceComponent = () => { const [currentQuestion, setCurrentQuestion] = useState(0); @@ -36,36 +37,12 @@ const SpeakSentenceComponent = () => { const [openMessageDialog, setOpenMessageDialog] = useState(""); const [totalSyllableCount, setTotalSyllableCount] = useState(""); const [isNextButtonCalled, setIsNextButtonCalled] = useState(false); - const [audioSrc, setAudioSrc] = useState(null); - useEffect(() => { - const preloadAudio = async () => { - try { - const response = await fetch(LevelCompleteAudio); - const audioBlob = await response.blob(); - const audioUrl = URL.createObjectURL(audioBlob); - setAudioSrc(audioUrl); - } catch (error) { - console.error("Error loading audio:", error); - } - }; - preloadAudio(); - - return () => { - // Cleanup blob URL to prevent memory leaks - if (audioSrc) { - URL.revokeObjectURL(audioSrc); - } - }; - }, []); + const levelCompleteAudioSrc = usePreloadAudio(LevelCompleteAudio); const callConfettiAndPlay = () => { - let audio; - if (audioSrc) { - audio = new Audio(audioSrc); - } else { - audio = new Audio(LevelCompleteAudio); - } + let audio = new Audio(levelCompleteAudioSrc); + audio.play(); callConfetti(); }; diff --git a/src/hooks/usePreloadAudio.jsx b/src/hooks/usePreloadAudio.jsx new file mode 100644 index 00000000..e190828a --- /dev/null +++ b/src/hooks/usePreloadAudio.jsx @@ -0,0 +1,29 @@ +import { useState, useEffect } from "react"; + +const usePreloadAudio = (audioUrl) => { + const [audioSrc, setAudioSrc] = useState(null); + + useEffect(() => { + const preloadAudio = async () => { + try { + const response = await fetch(audioUrl); + const audioBlob = await response.blob(); + const audioObjectUrl = URL.createObjectURL(audioBlob); + setAudioSrc(audioObjectUrl); + } catch (error) { + console.error("Error preloading audio:", error); + } + }; + + preloadAudio(); + + return () => { + if (audioSrc) { + URL.revokeObjectURL(audioSrc); + } + }; + }, [audioUrl]); + + return audioSrc; +}; +export default usePreloadAudio; diff --git a/src/views/Practice/Practice.jsx b/src/views/Practice/Practice.jsx index 742acc25..4d89b680 100644 --- a/src/views/Practice/Practice.jsx +++ b/src/views/Practice/Practice.jsx @@ -22,6 +22,7 @@ import config from "../../utils/urlConstants.json"; import { MessageDialog } from "../../components/Assesment/Assesment"; import { Log } from "../../services/telementryService"; import Mechanics6 from "../../components/Practice/Mechanics6"; +import usePreloadAudio from "../../hooks/usePreloadAudio"; const Practice = () => { const [page, setPage] = useState(""); @@ -70,39 +71,11 @@ const Practice = () => { } }, [startShowCase]); - const [audioSrc, setAudioSrc] = useState(null); - - useEffect(() => { - const preloadAudio = async () => { - try { - const response = await fetch(LevelCompleteAudio); - const audioBlob = await response.blob(); - const audioUrl = URL.createObjectURL(audioBlob); - setAudioSrc(audioUrl); - } catch (error) { - console.error("Error loading audio:", error); - } - }; - preloadAudio(); - - return () => { - // Cleanup blob URL to prevent memory leaks - if (audioSrc) { - URL.revokeObjectURL(audioSrc); - } - }; - }, []); + const levelCompleteAudioSrc = usePreloadAudio(LevelCompleteAudio); const callConfettiAndPlay = () => { - if (audioSrc) { - // Play preloaded audio if available - const audio = new Audio(audioSrc); - audio.play(); - } else { - // Fallback to LevelCompleteAudio if preloaded audio is not available - const fallbackAudio = new Audio(LevelCompleteAudio); - fallbackAudio.play(); - } + const audio = new Audio(levelCompleteAudioSrc); + audio.play(); callConfetti(); }; From 21a8e23b41d31a21d1ac492a204ceb293591d518 Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Fri, 3 Jan 2025 20:38:39 +0530 Subject: [PATCH 16/18] Issueid #232740 fix: While switching the iframe telemetry events are missing --- src/components/DiscoverSentance/DiscoverSentance.jsx | 2 +- src/components/Layouts.jsx/MainLayout.jsx | 2 +- src/utils/AudioCompare.js | 2 +- src/views/Practice/Practice.jsx | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/DiscoverSentance/DiscoverSentance.jsx b/src/components/DiscoverSentance/DiscoverSentance.jsx index 25ad96ed..146d759a 100644 --- a/src/components/DiscoverSentance/DiscoverSentance.jsx +++ b/src/components/DiscoverSentance/DiscoverSentance.jsx @@ -42,9 +42,9 @@ const SpeakSentenceComponent = () => { const callConfettiAndPlay = () => { let audio = new Audio(levelCompleteAudioSrc); - audio.play(); callConfetti(); + window.telemetry?.syncEvents && window.telemetry.syncEvents(); }; useEffect(() => { diff --git a/src/components/Layouts.jsx/MainLayout.jsx b/src/components/Layouts.jsx/MainLayout.jsx index 97058a61..661ccf18 100644 --- a/src/components/Layouts.jsx/MainLayout.jsx +++ b/src/components/Layouts.jsx/MainLayout.jsx @@ -795,7 +795,7 @@ const MainLayout = (props) => { > gameLost diff --git a/src/utils/AudioCompare.js b/src/utils/AudioCompare.js index 319d8134..e91d228a 100644 --- a/src/utils/AudioCompare.js +++ b/src/utils/AudioCompare.js @@ -75,7 +75,7 @@ const AudioRecorder = (props) => { props.setEnableNext?.(true); }); } - }, 1000); + }, 500); return () => clearTimeout(timeoutId); }; diff --git a/src/views/Practice/Practice.jsx b/src/views/Practice/Practice.jsx index 4d89b680..e796c680 100644 --- a/src/views/Practice/Practice.jsx +++ b/src/views/Practice/Practice.jsx @@ -77,6 +77,7 @@ const Practice = () => { const audio = new Audio(levelCompleteAudioSrc); audio.play(); callConfetti(); + window.telemetry?.syncEvents && window.telemetry.syncEvents(); }; useEffect(() => { From 17240c5a3b76ab618c4f22eda85593b2f1bc8899 Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Mon, 6 Jan 2025 16:33:47 +0530 Subject: [PATCH 17/18] Issueid #232843 fix: Audio sync issue for low network - Showcase [live lost and earn], fill in the blanks and jumble the words mechanism --- src/components/Practice/Mechanics3.jsx | 8 ++++++-- src/components/Practice/Mechanics4.jsx | 11 ++++++++--- src/utils/VoiceAnalyser.js | 5 ++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/components/Practice/Mechanics3.jsx b/src/components/Practice/Mechanics3.jsx index 3a560810..6bbc4b3a 100644 --- a/src/components/Practice/Mechanics3.jsx +++ b/src/components/Practice/Mechanics3.jsx @@ -18,6 +18,7 @@ import VoiceAnalyser from "../../utils/VoiceAnalyser"; import { Modal } from "@mui/material"; import ZoomInIcon from "@mui/icons-material/ZoomIn"; import CloseIcon from "@mui/icons-material/Close"; +import usePreloadAudio from "../../hooks/usePreloadAudio"; // TODO: update it as per File name OR update file name as per export variable name const Mechanics2 = ({ @@ -64,6 +65,9 @@ const Mechanics2 = ({ const [shake, setShake] = useState(false); const [wordToFill, setWordToFill] = useState(""); const [disabledWords, setDisabledWords] = useState(false); + const correctSoundAudio = usePreloadAudio(correctSound); + const wrongSoundAudio = usePreloadAudio(wrongSound); + const removeSoundAudio = usePreloadAudio(removeSound); const [answer, setAnswer] = useState({ text: "", audio_url: "", @@ -85,7 +89,7 @@ const Mechanics2 = ({ setAnswer(word); const isSoundCorrect = word.isAns; - let audio = new Audio(isSoundCorrect ? correctSound : wrongSound); + let audio = new Audio(isSoundCorrect ? correctSoundAudio : wrongSoundAudio); if (!isSoundCorrect) { setEnableNext(false); } @@ -97,7 +101,7 @@ const Mechanics2 = ({ }; const handleRemoveWord = () => { - let audio = new Audio(removeSound); + let audio = new Audio(removeSoundAudio); setAnswer({ text: "", audio_url: "", image_url: "", isAns: false }); audio.play(); setEnableNext(false); diff --git a/src/components/Practice/Mechanics4.jsx b/src/components/Practice/Mechanics4.jsx index 5b22c6c0..8b4fd785 100644 --- a/src/components/Practice/Mechanics4.jsx +++ b/src/components/Practice/Mechanics4.jsx @@ -13,6 +13,7 @@ import wrongSound from "../../assets/audio/wrong.wav"; import addSound from "../../assets/audio/add.mp3"; import removeSound from "../../assets/audio/remove.wav"; import { splitGraphemes } from "split-graphemes"; +import usePreloadAudio from "../../hooks/usePreloadAudio"; const Mechanics4 = ({ page, @@ -51,6 +52,10 @@ const Mechanics4 = ({ const [words, setWords] = useState( type === "word" ? [] : ["Friend", "She is", "My"] ); + const correctSoundAudio = usePreloadAudio(correctSound); + const wrongSoundAudio = usePreloadAudio(wrongSound); + const addSoundAudio = usePreloadAudio(addSound); + const removeSoundAudio = usePreloadAudio(removeSound); const [wordsAfterSplit, setWordsAfterSplit] = useState([]); useEffect(() => { @@ -114,7 +119,7 @@ const Mechanics4 = ({ }, 3000); // audioPlay[word](); if (selectedWords?.length + 1 !== wordsAfterSplit?.length || isSelected) { - let audio = new Audio(isSelected ? removeSound : addSound); + let audio = new Audio(isSelected ? removeSoundAudio : addSoundAudio); audio.play(); setEnableNext(false); } @@ -134,8 +139,8 @@ const Mechanics4 = ({ if (selectedWords?.length + 1 === wordsAfterSplit?.length) { let audio = new Audio( [...selectedWords, word]?.join(" ") === parentWords - ? correctSound - : wrongSound + ? correctSoundAudio + : wrongSoundAudio ); audio.play(); } diff --git a/src/utils/VoiceAnalyser.js b/src/utils/VoiceAnalyser.js index e575bb54..3639cec2 100644 --- a/src/utils/VoiceAnalyser.js +++ b/src/utils/VoiceAnalyser.js @@ -33,6 +33,7 @@ import config from "./urlConstants.json"; import { filterBadWords } from "./Badwords"; import S3Client from "../config/awsS3"; import { PutObjectCommand } from "@aws-sdk/client-s3"; +import usePreloadAudio from "../hooks/usePreloadAudio"; /* eslint-disable */ const AudioPath = { @@ -77,6 +78,8 @@ function VoiceAnalyser(props) { process.env.REACT_APP_IS_AUDIOPREPROCESSING === "true" ); const [isMatching, setIsMatching] = useState(false); + const livesAddAudio = usePreloadAudio(livesAdd); + const livesCutAudio = usePreloadAudio(livesCut); //console.log('audio', recordedAudio, isMatching); @@ -628,7 +631,7 @@ function VoiceAnalyser(props) { } else { isLiveLost = false; } - const audio = new Audio(isLiveLost ? livesCut : livesAdd); + const audio = new Audio(isLiveLost ? livesCutAudio : livesAddAudio); audio.play(); // Update the state or data structure with the new lives data. From 64624c473de7830641fece03c2f724b0c05bfcef Mon Sep 17 00:00:00 2001 From: Ajinkya Pande Date: Mon, 6 Jan 2025 16:54:35 +0530 Subject: [PATCH 18/18] Issueid #232821 fix: Unexpected API Call: /pointer/getPoints/4347715229 Triggered When Not Required --- src/components/Assesment/Assesment.jsx | 13 +++++++------ src/components/AssesmentEnd/AssesmentEnd.jsx | 10 ++++++---- src/views/Practice/Practice.jsx | 12 +++++++----- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/components/Assesment/Assesment.jsx b/src/components/Assesment/Assesment.jsx index 36a7598f..7289a594 100644 --- a/src/components/Assesment/Assesment.jsx +++ b/src/components/Assesment/Assesment.jsx @@ -598,11 +598,12 @@ const Assesment = ({ discoverStart }) => { } localStorage.setItem("lang", lang || "ta"); - const getPointersDetails = await axios.get( - `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${usernameDetails?.data?.result?.virtualID}/${session_id}?language=${lang}` - ); - setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); - + if (process.env.REACT_APP_IS_APP_IFRAME !== "true") { + const getPointersDetails = await axios.get( + `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${usernameDetails?.data?.result?.virtualID}/${session_id}?language=${lang}` + ); + setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); + } dispatch(setVirtualId(usernameDetails?.data?.result?.virtualID)); })(); } else { @@ -635,7 +636,7 @@ const Assesment = ({ discoverStart }) => { localStorage.setItem("sessionId", sessionId); } - if (virtualId) { + if (process.env.REACT_APP_IS_APP_IFRAME !== "true" && virtualId) { const getPointersDetails = await axios.get( `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${virtualId}/${sessionId}?language=${lang}` ); diff --git a/src/components/AssesmentEnd/AssesmentEnd.jsx b/src/components/AssesmentEnd/AssesmentEnd.jsx index e88504dd..72522d54 100644 --- a/src/components/AssesmentEnd/AssesmentEnd.jsx +++ b/src/components/AssesmentEnd/AssesmentEnd.jsx @@ -48,10 +48,12 @@ const AssesmentEnd = () => { sessionId = uniqueId(); localStorage.setItem("sessionId", sessionId); } - const getPointersDetails = await axios.get( - `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${virtualId}/${sessionId}?language=${lang}` - ); - setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); + if (process.env.REACT_APP_IS_APP_IFRAME !== "true") { + const getPointersDetails = await axios.get( + `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${virtualId}/${sessionId}?language=${lang}` + ); + setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); + } })(); setTimeout(() => { setShake(false); diff --git a/src/views/Practice/Practice.jsx b/src/views/Practice/Practice.jsx index e796c680..6ad6ee70 100644 --- a/src/views/Practice/Practice.jsx +++ b/src/views/Practice/Practice.jsx @@ -452,12 +452,14 @@ const Practice = () => { // TODO: Handle Error for lessons - no lesson progress - starting point should be P1 - const getPointersDetails = await axios.get( - `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${virtualId}/${sessionId}?language=${lang}` - ); + if (process.env.REACT_APP_IS_APP_IFRAME !== "true") { + const getPointersDetails = await axios.get( + `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.GET_POINTER}/${virtualId}/${sessionId}?language=${lang}` + ); - // TODO: Just Opss icon - we are trying to fetch the score for you - setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); + // TODO: Just Opss icon - we are trying to fetch the score for you + setPoints(getPointersDetails?.data?.result?.totalLanguagePoints || 0); + } let userState = Number.isInteger( Number(resLessons.data?.result?.result?.lesson)