diff --git a/.github/workflows/all-app-sandbox.yml b/.github/workflows/all-app-sandbox.yml index 9bfbbceb..b3e750a4 100644 --- a/.github/workflows/all-app-sandbox.yml +++ b/.github/workflows/all-app-sandbox.yml @@ -73,8 +73,8 @@ jobs: REACT_APP_IS_AUDIOPREPROCESSING: ${{ vars.REACT_APP_IS_AUDIOPREPROCESSING }} REACT_APP_POST_LEARNER_PROGRESS: ${{ vars.REACT_APP_POST_LEARNER_PROGRESS }} REACT_APP_IS_APP_IFRAME: ${{ vars.REACT_APP_IS_APP_IFRAME }} - REACT_APP_IS_APP_TEST_RIG: ${{ vars.REACT_APP_IS_APP_TEST_RIG }} - REACT_APP_IS_APP_LANGUAGES: ${{ vars.REACT_APP_IS_APP_LANGUAGES }} + REACT_APP_IS_IN_APP_AUTHORISATION: ${{ vars.REACT_APP_IS_IN_APP_AUTHORISATION }} + REACT_APP_LANGUAGES: ${{ vars.REACT_APP_LANGUAGES }} CI: false # Disabling CI to not treat warnings as errors run: npm run build diff --git a/.github/workflows/all-dev-rig.yml b/.github/workflows/all-dev-rig.yml index 78ac4392..fff0cb2f 100644 --- a/.github/workflows/all-dev-rig.yml +++ b/.github/workflows/all-dev-rig.yml @@ -3,7 +3,7 @@ name: ALL rig dev Deployment on: push: branches: - - test-rig-1.1 + - all-1.2 jobs: deploy: @@ -73,8 +73,8 @@ jobs: REACT_APP_IS_AUDIOPREPROCESSING: ${{ vars.REACT_APP_IS_AUDIOPREPROCESSING }} REACT_APP_POST_LEARNER_PROGRESS: ${{ vars.REACT_APP_POST_LEARNER_PROGRESS }} REACT_APP_IS_APP_IFRAME: ${{ vars.REACT_APP_IS_APP_IFRAME }} - REACT_APP_IS_APP_TEST_RIG: ${{ vars.REACT_APP_IS_APP_TEST_RIG }} - REACT_APP_IS_APP_LANGUAGES: ${{ vars.REACT_APP_IS_APP_LANGUAGES }} + REACT_APP_IS_IN_APP_AUTHORISATION: ${{ vars.REACT_APP_IS_IN_APP_AUTHORISATION }} + REACT_APP_LANGUAGES: ${{ vars.REACT_APP_LANGUAGES }} CI: false # Disabling CI to not treat warnings as errors run: npm run build diff --git a/.github/workflows/all-dev-tn.yml b/.github/workflows/all-dev-tn.yml index c72ff627..eba10455 100644 --- a/.github/workflows/all-dev-tn.yml +++ b/.github/workflows/all-dev-tn.yml @@ -3,12 +3,12 @@ name: ALL tn dev Deployment on: push: branches: - - test + - all-1.2-tn-dev jobs: deploy: runs-on: ubuntu-latest - environment: all-dev-rig + environment: all-dev-tn steps: - name: Checkout code @@ -17,60 +17,76 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v3 with: - node-version: '16' + node-version: '18' - name: Install AWS CLI run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y awscli + - name: Configure AWS credentials run: | aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }} aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws configure set default.region ${{ secrets.AWS_REGION }} - - name: Build and Package Application - run: | - npm install - - - name: Build Production + + - name: Clean up node_modules and package-lock.json run: | - REACT_APP_API_KEY=${{ secrets.REACT_APP_API_KEY }} \ - REACT_APP_MODE=play \ - REACT_APP_authToken="" \ - REACT_APP_PID=learner-ai-story-demo \ - REACT_APP_UID=anonymous \ - REACT_APP_ID=all.ll.app \ - REACT_APP_VER="0.3.0" \ - REACT_APP_TIMEDIFF="0" \ - REACT_APP_HOST=https://telemetry-dev.theall.ai \ - REACT_APP_ENDPOINT=telemetry \ - REACT_APP_APISLUG="/v1/" \ - REACT_APP_CHANNEL="learner-ai-demo" \ - REACT_APP_ENV="all-player" \ - REACT_APP_BATCHSIZE=1 \ - REACT_APP_CONTENT_SIZE=5 \ - REACT_APP_LANGUAGE=ta \ - REACT_APP_TELEMETRY_MODE=DT \ - REACT_APP_LEARNER_AI_APP_HOST="https://www.learnerai-dev.theall.ai" \ - REACT_APP_CAPTURE_AUDIO=true \ - REACT_APP_AWS_S3_BUCKET_NAME=all-dev-storage \ - REACT_APP_AWS_S3_BUCKET_URL=https://all-dev-storage.s3.ap-south-1.amazonaws.com \ - REACT_APP_AWS_S3_REGION=ap-south-1 \ - REACT_APP_AWS_ACCESS_KEY_ID=${{ secrets.REACT_APP_AWS_ACCESS_KEY_ID }} \ - REACT_APP_AWS_SECRET_ACCESS_KEY=${{ secrets.REACT_APP_AWS_SECRET_ACCESS_KEY }} \ - REACT_APP_AWS_S3_BUCKET_CONTENT_URL=https://all-dev-content-service.s3.ap-south-1.amazonaws.com \ - REACT_APP_MIN_DECIBELS=-75 \ - REACT_APP_IS_AUDIOPREPROCESSING=false \ - npm run build + rm -rf node_modules + rm -f package-lock.json + + - name: Install Dependencies + run: npm install --legacy-peer-deps + + - name: Run Husky Install + run: npm run prepare + + - name: Build and Package Application + env: + SKIP_PREFLIGHT_CHECK: ${{ vars.SKIP_PREFLIGHT_CHECK }} + REACT_APP_MODE: ${{ vars.REACT_APP_MODE }} + REACT_APP_authToken: ${{ vars.REACT_APP_authToken }} + REACT_APP_PID: ${{ vars.REACT_APP_PID }} + REACT_APP_UID: ${{ vars.REACT_APP_UID }} + REACT_APP_ID: ${{ vars.REACT_APP_ID }} + REACT_APP_VER: ${{ vars.REACT_APP_VER }} + REACT_APP_TIMEDIFF: ${{ vars.REACT_APP_TIMEDIFF }} + REACT_APP_HOST: ${{ vars.REACT_APP_HOST }} + REACT_APP_ENDPOINT: ${{ vars.REACT_APP_ENDPOINT }} + REACT_APP_APISLUG: ${{ vars.REACT_APP_APISLUG }} + REACT_APP_CHANNEL: ${{ vars.REACT_APP_CHANNEL }} + REACT_APP_ENV: ${{ vars.REACT_APP_ENV }} + REACT_APP_BATCHSIZE: ${{ vars.REACT_APP_BATCHSIZE }} + REACT_APP_CONTENT_SIZE: ${{ vars.REACT_APP_CONTENT_SIZE }} + REACT_APP_LANGUAGE: ${{ vars.REACT_APP_LANGUAGE }} + REACT_APP_TELEMETRY_MODE: ${{ vars.REACT_APP_TELEMETRY_MODE }} + REACT_APP_VIRTUAL_ID_HOST: ${{ vars.REACT_APP_VIRTUAL_ID_HOST }} + REACT_APP_LEARNER_AI_ORCHESTRATION_HOST: ${{ vars.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST }} + REACT_APP_LEARNER_AI_APP_HOST: ${{ vars.REACT_APP_LEARNER_AI_APP_HOST }} + REACT_APP_CONTENT_SERVICE_APP_HOST: ${{ vars.REACT_APP_CONTENT_SERVICE_APP_HOST }} + REACT_APP_CAPTURE_AUDIO: ${{ vars.REACT_APP_CAPTURE_AUDIO }} + REACT_APP_AWS_S3_BUCKET_NAME: ${{ vars.REACT_APP_AWS_S3_BUCKET_NAME }} + REACT_APP_AWS_S3_BUCKET_URL: ${{ vars.REACT_APP_AWS_S3_BUCKET_URL }} + REACT_APP_AWS_S3_REGION: ${{ vars.REACT_APP_AWS_S3_REGION }} + REACT_APP_AWS_S3_BUCKET_CONTENT_URL: ${{ vars.REACT_APP_AWS_S3_BUCKET_CONTENT_URL }} + REACT_APP_MIN_DECIBELS: ${{ vars.REACT_APP_MIN_DECIBELS }} + REACT_APP_IS_AUDIOPREPROCESSING: ${{ vars.REACT_APP_IS_AUDIOPREPROCESSING }} + REACT_APP_POST_LEARNER_PROGRESS: ${{ vars.REACT_APP_POST_LEARNER_PROGRESS }} + REACT_APP_IS_APP_IFRAME: ${{ vars.REACT_APP_IS_APP_IFRAME }} + REACT_APP_IS_IN_APP_AUTHORISATION: ${{ vars.REACT_APP_IS_IN_APP_AUTHORISATION }} + REACT_APP_LANGUAGES: ${{ vars.REACT_APP_LANGUAGES }} + CI: false # Disabling CI to not treat warnings as errors + run: npm run build + - name: Deploy to S3 Bucket - run: | - aws s3 sync ./build s3://sballappliance/assets/sb-ai/ + run: aws s3 sync ./build s3://sb-all/assets/test-rig/ + - name: Debug Environment Variables run: | echo "REACT_APP_AWS_S3_BUCKET_NAME: $REACT_APP_AWS_S3_BUCKET_NAME" echo "AWS_REGION: $AWS_REGION" - echo "secrate": ${{secrets.REACT_APP_AWS_S3_BUCKET_NAME}} - ### Invalidate Cloudfront + echo "secrate": ${{ vars.REACT_APP_AWS_S3_BUCKET_NAME }} + - name: Cloudfront Invalidation uses: chetan/invalidate-cloudfront-action@master env: diff --git a/.github/workflows/all-prod-rig.yml b/.github/workflows/all-prod-rig.yml index 6ae94336..98b6d296 100644 --- a/.github/workflows/all-prod-rig.yml +++ b/.github/workflows/all-prod-rig.yml @@ -3,7 +3,7 @@ name: ALL rig prod Deployment on: push: branches: - - test + - test-rig-1.1-prod jobs: deploy: @@ -73,13 +73,13 @@ jobs: REACT_APP_IS_AUDIOPREPROCESSING: ${{ vars.REACT_APP_IS_AUDIOPREPROCESSING }} REACT_APP_POST_LEARNER_PROGRESS: ${{ vars.REACT_APP_POST_LEARNER_PROGRESS }} REACT_APP_IS_APP_IFRAME: ${{ vars.REACT_APP_IS_APP_IFRAME }} - REACT_APP_IS_APP_TEST_RIG: ${{ vars.REACT_APP_IS_APP_TEST_RIG }} - REACT_APP_IS_APP_LANGUAGES: ${{ vars.REACT_APP_IS_APP_LANGUAGES }} + REACT_APP_IS_IN_APP_AUTHORISATION: ${{ vars.REACT_APP_IS_IN_APP_AUTHORISATION }} + REACT_APP_LANGUAGES: ${{ vars.REACT_APP_LANGUAGES }} CI: false # Disabling CI to not treat warnings as errors run: npm run build - name: Deploy to S3 Bucket - run: aws s3 sync ./build s3://sb-all-rig/all-automation/ + run: aws s3 sync ./build s3://sb-all-rig/ - name: Debug Environment Variables run: | diff --git a/.github/workflows/all-staging-tn.yml b/.github/workflows/all-staging-tn.yml new file mode 100644 index 00000000..5abd8faf --- /dev/null +++ b/.github/workflows/all-staging-tn.yml @@ -0,0 +1,97 @@ +name: ALL tn staging Deployment + +on: + push: + branches: + - all-1.2-tn-staging + +jobs: + deploy: + runs-on: ubuntu-latest + environment: all-staging-tn + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install AWS CLI + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y awscli + + - name: Configure AWS credentials + run: | + aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }} + aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws configure set default.region ${{ secrets.AWS_REGION }} + + - name: Clean up node_modules and package-lock.json + run: | + rm -rf node_modules + rm -f package-lock.json + + - name: Install Dependencies + run: npm install --legacy-peer-deps + + - name: Run Husky Install + run: npm run prepare + + - name: Build and Package Application + env: + SKIP_PREFLIGHT_CHECK: ${{ vars.SKIP_PREFLIGHT_CHECK }} + REACT_APP_MODE: ${{ vars.REACT_APP_MODE }} + REACT_APP_authToken: ${{ vars.REACT_APP_authToken }} + REACT_APP_PID: ${{ vars.REACT_APP_PID }} + REACT_APP_UID: ${{ vars.REACT_APP_UID }} + REACT_APP_ID: ${{ vars.REACT_APP_ID }} + REACT_APP_VER: ${{ vars.REACT_APP_VER }} + REACT_APP_TIMEDIFF: ${{ vars.REACT_APP_TIMEDIFF }} + REACT_APP_HOST: ${{ vars.REACT_APP_HOST }} + REACT_APP_ENDPOINT: ${{ vars.REACT_APP_ENDPOINT }} + REACT_APP_APISLUG: ${{ vars.REACT_APP_APISLUG }} + REACT_APP_CHANNEL: ${{ vars.REACT_APP_CHANNEL }} + REACT_APP_ENV: ${{ vars.REACT_APP_ENV }} + REACT_APP_BATCHSIZE: ${{ vars.REACT_APP_BATCHSIZE }} + REACT_APP_CONTENT_SIZE: ${{ vars.REACT_APP_CONTENT_SIZE }} + REACT_APP_LANGUAGE: ${{ vars.REACT_APP_LANGUAGE }} + REACT_APP_TELEMETRY_MODE: ${{ vars.REACT_APP_TELEMETRY_MODE }} + REACT_APP_VIRTUAL_ID_HOST: ${{ vars.REACT_APP_VIRTUAL_ID_HOST }} + REACT_APP_LEARNER_AI_ORCHESTRATION_HOST: ${{ vars.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST }} + REACT_APP_LEARNER_AI_APP_HOST: ${{ vars.REACT_APP_LEARNER_AI_APP_HOST }} + REACT_APP_CONTENT_SERVICE_APP_HOST: ${{ vars.REACT_APP_CONTENT_SERVICE_APP_HOST }} + REACT_APP_CAPTURE_AUDIO: ${{ vars.REACT_APP_CAPTURE_AUDIO }} + REACT_APP_AWS_S3_BUCKET_NAME: ${{ vars.REACT_APP_AWS_S3_BUCKET_NAME }} + REACT_APP_AWS_S3_BUCKET_URL: ${{ vars.REACT_APP_AWS_S3_BUCKET_URL }} + REACT_APP_AWS_S3_REGION: ${{ vars.REACT_APP_AWS_S3_REGION }} + REACT_APP_AWS_S3_BUCKET_CONTENT_URL: ${{ vars.REACT_APP_AWS_S3_BUCKET_CONTENT_URL }} + REACT_APP_MIN_DECIBELS: ${{ vars.REACT_APP_MIN_DECIBELS }} + REACT_APP_IS_AUDIOPREPROCESSING: ${{ vars.REACT_APP_IS_AUDIOPREPROCESSING }} + REACT_APP_POST_LEARNER_PROGRESS: ${{ vars.REACT_APP_POST_LEARNER_PROGRESS }} + REACT_APP_IS_APP_IFRAME: ${{ vars.REACT_APP_IS_APP_IFRAME }} + REACT_APP_IS_IN_APP_AUTHORISATION: ${{ vars.REACT_APP_IS_IN_APP_AUTHORISATION }} + REACT_APP_LANGUAGES: ${{ vars.REACT_APP_LANGUAGES }} + CI: false # Disabling CI to not treat warnings as errors + run: npm run build + + - name: Deploy to S3 Bucket + run: aws s3 sync ./build s3://sballtnlanglab/assets/test-rig/ + + - name: Debug Environment Variables + run: | + echo "REACT_APP_AWS_S3_BUCKET_NAME: $REACT_APP_AWS_S3_BUCKET_NAME" + echo "AWS_REGION: $AWS_REGION" + echo "secrate": ${{ vars.REACT_APP_AWS_S3_BUCKET_NAME }} + + - name: Cloudfront Invalidation + uses: chetan/invalidate-cloudfront-action@master + env: + DISTRIBUTION: ${{ secrets.AWS_DISTRIBUTION }} + PATHS: '/*' + AWS_REGION: ${{ secrets.AWS_REGION }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/package.json b/package.json index 7948ced6..95b5bdbf 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,9 @@ "canvas-confetti": "^1.9.2", "character-error-rate": "^1.1.4", "classnames": "^2.3.1", + "eslint-plugin-import": "^2.28.0", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.1", "faker": "^5.5.3", "homophones": "^1.0.1", "jwt-decode": "^4.0.0", @@ -38,15 +41,13 @@ "react-virtualized": "^9.22.3", "react-virtualized-auto-sizer": "^1.0.6", "react-window": "^1.8.6", + "recordrtc": "^5.6.2", "redux": "^4.1.2", "redux-saga": "^1.1.3", "sass": "^1.44.0", "split-graphemes": "^0.5.0", "use-sound": "^4.0.1", - "web-vitals": "^2.1.4", - "eslint-plugin-import": "^2.28.0", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-react": "^7.33.1" + "web-vitals": "^2.1.4" }, "scripts": { "start": "react-scripts start", @@ -87,16 +88,16 @@ ] }, "devDependencies": { + "@mui/styles": "^5.15.10", "eslint": "^7.32.0", - "prettier": "^2.3.2", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^3.4.0", "husky": "^9.0.11", "lint-staged": "^11.0.0", - "react": "^18.2.0", - "@mui/styles": "^5.15.10" + "prettier": "^2.3.2", + "react": "^18.2.0" }, - "lint-staged": { + "lint-staged": { "src/**/*.{js,jsx}": [ "npx eslint --fix", "npx prettier --write" diff --git a/src/assets/images/logout.svg b/src/assets/images/logout.svg new file mode 100644 index 00000000..8546b939 --- /dev/null +++ b/src/assets/images/logout.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/components/Assesment/Assesment.jsx b/src/components/Assesment/Assesment.jsx index f22d788a..3a6385bb 100644 --- a/src/components/Assesment/Assesment.jsx +++ b/src/components/Assesment/Assesment.jsx @@ -4,8 +4,11 @@ import { Box, Grid, IconButton, + Tooltip, Typography, } from "../../../node_modules/@mui/material/index"; +import LogoutImg from "../../assets/images/logout.svg"; +import { styled } from "@mui/material/styles"; import { RoundTick, SelectLanguageButton, @@ -16,9 +19,7 @@ import { setLocalData, } from "../../utils/constants"; import practicebg from "../../assets/images/practice-bg.svg"; -import { - useNavigate, -} from "../../../node_modules/react-router-dom/dist/index"; +import { useNavigate } from "../../../node_modules/react-router-dom/dist/index"; import { useEffect, useState } from "react"; import axios from "../../../node_modules/axios/index"; // import { useDispatch } from 'react-redux'; @@ -42,6 +43,7 @@ import config from "../../utils/urlConstants.json"; import panda from "../../assets/images/panda.svg"; import cryPanda from "../../assets/images/cryPanda.svg"; import { uniqueId } from "../../services/utilService"; +import { end } from "../../services/telementryService"; export const LanguageModal = ({ lang, setLang, setOpenLangModal }) => { const [selectedLang, setSelectedLang] = useState(lang); @@ -169,7 +171,8 @@ export const LanguageModal = ({ lang, setLang, setOpenLangModal }) => { { try { - if (process.env.REACT_APP_IS_APP_IFRAME === 'true') { - window.parent.postMessage({ type: 'restore-iframe-content' }, '*'); - navigate("/") + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + window.parent.postMessage({ type: "restore-iframe-content" }); + navigate("/"); } else { - navigate("/discover-start") + navigate("/discover-start"); } } catch (error) { console.error("Error posting message:", error); } }; + const handleLogout = () => { + localStorage.clear(); + end({}); + navigate("/login"); + }; + + const CustomIconButton = styled(IconButton)({ + padding: "6px 20px", + color: "white", + fontSize: "20px", + fontWeight: 500, + borderRadius: "8px", + marginRight: "10px", + fontFamily: '"Lato", "sans-serif"', + position: "relative", + display: "flex", + alignItems: "center", + justifyContent: "center", + "& .logout-img": { + display: "block", + filter: "invert(1)", + }, + }); + + const CustomTooltip = styled(({ className, ...props }) => ( + + ))({ + [`& .MuiTooltip-tooltip`]: { + fontSize: "1.2rem", // Adjust the font size as needed + }, + }); + return ( <> {!!openMessageDialog && ( @@ -377,7 +412,13 @@ export const ProfileHeader = ({ zIndex: 5555, }} > - + {handleBack && ( @@ -388,11 +429,19 @@ export const ProfileHeader = ({ {username && ( <> - profile-pic + profile-pic - {username || ""} + {username || ""} )} - + */} - + @@ -470,6 +519,20 @@ export const ProfileHeader = ({ + {process.env.REACT_APP_IS_IN_APP_AUTHORISATION === "true" && ( + + + + Logout + + + + )} @@ -491,7 +554,7 @@ const Assesment = ({ discoverStart }) => { const [level, setLevel] = useState(""); const dispatch = useDispatch(); const [openLangModal, setOpenLangModal] = useState(false); - const [lang, setLang] = useState(getLocalData("lang") || "en"); + const [lang, setLang] = useState(getLocalData("lang") || "en"); const [points, setPoints] = useState(0); useEffect(() => { @@ -524,11 +587,11 @@ const Assesment = ({ discoverStart }) => { ); let session_id = localStorage.getItem("sessionId"); - if (!session_id){ + if (!session_id) { session_id = uniqueId(); - localStorage.setItem("sessionId", session_id) + localStorage.setItem("sessionId", session_id); } - + 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}` @@ -555,9 +618,9 @@ const Assesment = ({ discoverStart }) => { ); let sessionId = getLocalData("sessionId"); - if (!sessionId || sessionId === 'null'){ + if (!sessionId || sessionId === "null") { sessionId = uniqueId(); - localStorage.setItem("sessionId", sessionId) + localStorage.setItem("sessionId", sessionId); } if (virtualId) { @@ -607,7 +670,7 @@ const Assesment = ({ discoverStart }) => { height: "100vh", backgroundImage: `url(${images?.[`desktopLevel${level || 1}`]})`, backgroundRepeat: "round", - backgroundSize: 'auto', + backgroundSize: "auto", position: "relative", }; @@ -675,68 +738,68 @@ const Assesment = ({ discoverStart }) => { ) : ( - - - {discoverStart - ? "Let's test your language skills" - : "You have good language skills"} - - - {level > 0 - ? `Take the assessment to complete Level ${level}.` - : "Take the assessment to discover your level"} + {discoverStart + ? "Let's test your language skills" + : "You have good language skills"} + + + {level > 0 + ? `Take the assessment to complete Level ${level}.` + : "Take the assessment to discover your level"} + + + + + - - - - - + )} ); diff --git a/src/components/AssesmentEnd/AssesmentEnd.jsx b/src/components/AssesmentEnd/AssesmentEnd.jsx index db5a1456..e3b1001a 100644 --- a/src/components/AssesmentEnd/AssesmentEnd.jsx +++ b/src/components/AssesmentEnd/AssesmentEnd.jsx @@ -39,7 +39,7 @@ const AssesmentEnd = () => { const { data } = getMilestoneDetails; setLevel(data.data.milestone_level); setLocalData("userLevel", data.data.milestone_level?.replace("m", "")); - const sessionId = getLocalData("sessionId"); + let sessionId = getLocalData("sessionId"); if (!sessionId){ sessionId = uniqueId(); localStorage.setItem("sessionId", sessionId) diff --git a/src/components/Mechanism/WordsOrImage.jsx b/src/components/Mechanism/WordsOrImage.jsx index f8f4018f..c6ea0bc1 100644 --- a/src/components/Mechanism/WordsOrImage.jsx +++ b/src/components/Mechanism/WordsOrImage.jsx @@ -46,7 +46,7 @@ const WordsOrImage = ({ loading, setOpenMessageDialog, isNextButtonCalled, - setIsNextButtonCalled + setIsNextButtonCalled, }) => { const audioRef = createRef(null); const [duration, setDuration] = useState(0); @@ -90,7 +90,7 @@ const WordsOrImage = ({ livesData, gameOverData, loading, - setIsNextButtonCalled + setIsNextButtonCalled, }} > + display={"flex"} + mb={4} + sx={{ + width: "100%", + justifyContent: "center", + flexWrap: "wrap", + }} + > {highlightWords(words, matchedChar)} )} - + )} diff --git a/src/config/awsS3.js b/src/config/awsS3.js index 34d950f4..6fa0f244 100644 --- a/src/config/awsS3.js +++ b/src/config/awsS3.js @@ -1,9 +1,9 @@ -// import { S3Client } from '@aws-sdk/client-s3'; +import { S3Client } from "@aws-sdk/client-s3"; -// export default new S3Client({ -// region: process.env.REACT_APP_AWS_S3_REGION, -// credentials: { -// accessKeyId: process.env.REACT_APP_AWS_ACCESS_KEY_ID, -// secretAccessKey: process.env.REACT_APP_AWS_SECRET_ACCESS_KEY, -// }, -// }); +export default new S3Client({ + region: process.env.REACT_APP_AWS_S3_REGION, + credentials: { + accessKeyId: process.env.REACT_APP_AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.REACT_APP_AWS_SECRET_ACCESS_KEY, + }, +}); diff --git a/src/index.css b/src/index.css index f97a3937..217a9822 100644 --- a/src/index.css +++ b/src/index.css @@ -118,7 +118,7 @@ color: #1cc871; -webkit-text-stroke-width: 0.5; -webkit-text-stroke-color: #00b359; - font-family: Quicksand; + font-family: sans-serif; font-size: 40px; font-style: normal; font-weight: 700; @@ -130,7 +130,7 @@ color: #c30303; -webkit-text-stroke-width: 0.5; -webkit-text-stroke-color: #c30303; - font-family: Quicksand; + font-family: sans-serif; font-size: 40px; font-style: normal; font-weight: 700; diff --git a/src/routes/index.js b/src/routes/index.js index 375ea6fc..db7142a2 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -3,6 +3,12 @@ import * as reviews from "../views"; const routData = [ + { + id: "route-001", + path: "/", + component: reviews.DiscoverStart, + requiresAuth: true, + }, { id: "route-002", path: "/discover", @@ -53,17 +59,20 @@ const routData = [ component: reviews.PracticeRedirectPage, requiresAuth: true, }, -]; - -// add login route for test rig -if (process.env.REACT_APP_IS_APP_TEST_RIG === 'true') { - routData.push( { - id: "route-001", - path: "/", + id: "route-010", + path: "/login", component: reviews.LoginPage, - requiresAuth: true, + requiresAuth: false, }, +]; +// add login route for test rig + +const virtualId = localStorage.getItem('virtualId'); +const isLogin = process.env.REACT_APP_IS_IN_APP_AUTHORISATION === 'true'; + +if (isLogin && !virtualId) { + routData.push( { id: "route-000", path: "*", @@ -73,12 +82,6 @@ if (process.env.REACT_APP_IS_APP_TEST_RIG === 'true') { ); }else { routData.push( - { - id: "route-001", - path: "/", - component: reviews.DiscoverStart, - requiresAuth: false, - }, { id: "route-000", path: "*", diff --git a/src/services/telementryService.js b/src/services/telementryService.js index 2f1157e6..cd51adbe 100644 --- a/src/services/telementryService.js +++ b/src/services/telementryService.js @@ -72,6 +72,7 @@ export const start = (duration) => { mode: 'play', stageid: url, duration: Number((duration / 1e3).toFixed(2)), + dspec: window.navigator.userAgent }, }); } catch (error) { diff --git a/src/utils/AudioCompare.js b/src/utils/AudioCompare.js index 1dba58c1..c4545242 100644 --- a/src/utils/AudioCompare.js +++ b/src/utils/AudioCompare.js @@ -1,87 +1,105 @@ -import React, { useState } from "react"; -import AudioAnalyser from "react-audio-analyser"; +import React, { useState, useEffect, useRef } from "react"; +import RecordRTC from "recordrtc"; import { Box } from "@mui/material"; import { ListenButton, RetryIcon, SpeakButton, StopButton } from "./constants"; import RecordVoiceVisualizer from "./RecordVoiceVisualizer"; -import useAudioDetection from "./useAudioDetection"; -const AudioRecorderCompair = (props) => { - const { startDetection, stopDetection, isSilent, isRunning, audioDetected } = useAudioDetection(); +const AudioRecorder = (props) => { + const [isRecording, setIsRecording] = useState(false); const [status, setStatus] = useState(""); - const [audioSrc, setAudioSrc] = useState(""); - const [recordingInitialized, setRecordingInitialized] = useState(false); - const audioType = "audio/wav"; + const [audioBlob, setAudioBlob] = useState(null); + const recorderRef = useRef(null); + const mediaStreamRef = useRef(null); - const controlAudio = async (status) => { - if (status === "recording") { - await startDetection(); - } else { - stopDetection(); - } - setStatus(status); - }; - - const resetRecording = () => { - setAudioSrc(""); - setRecordingInitialized(false); - }; + useEffect(() => { + // Cleanup when component unmounts + return () => { + if (recorderRef.current) { + recorderRef.current.destroy(); + } + if (mediaStreamRef.current) { + mediaStreamRef.current.getTracks().forEach((track) => track.stop()); + } + }; + }, []); - const handleMic = async () => { + const startRecording = async () => { + setStatus("recording"); if (props.setEnableNext) { props.setEnableNext(false); } - document.getElementById("startaudio_compair").click(); - resetRecording(); + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + mediaStreamRef.current = stream; + + // Use RecordRTC with specific configurations to match the blob structure + recorderRef.current = new RecordRTC(stream, { + type: "audio", + mimeType: "audio/wav", // Ensuring the same MIME type as AudioRecorderCompair + recorderType: RecordRTC.StereoAudioRecorder, // Use StereoAudioRecorder for better compatibility + numberOfAudioChannels: 1, // Match the same number of audio channels + desiredSampRate: 16000, // Adjust the sample rate if necessary to match + disableLogs: true, + }); + + recorderRef.current.startRecording(); + + setIsRecording(true); + } catch (err) { + console.error("Failed to start recording:", err); + } }; - const handleStop = () => { + const stopRecording = () => { + 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."); + } + + // Stop the media stream + if (mediaStreamRef.current) { + mediaStreamRef.current.getTracks().forEach((track) => track.stop()); + } + + setIsRecording(false); + }); + } if (props.setEnableNext) { props.setEnableNext(true); } - document.getElementById("stopaudio_compair").click(); }; - const audioProps = { - audioType, - status, - audioSrc, - timeslice: 1000, - startCallback: (e) => { - setAudioSrc(""); - setRecordingInitialized(true); - props.setRecordedAudio(""); - }, - pauseCallback: (e) => {}, - stopCallback: (e) => { - const temp_audioSrc = window.URL.createObjectURL(e); - setAudioSrc(temp_audioSrc); - if (!audioDetected) { - props?.setOpenMessageDialog({ - message: "Please Speak Louder and Clear", - isError: true, - }); - if (props.setEnableNext) { - props.setEnableNext(false); - } - } else { - props.setRecordedAudio(temp_audioSrc); - } - setRecordingInitialized(false); - }, - onRecordCallback: (e) => {}, - errorCallback: (err) => {}, - backgroundColor: "hsla(0, 100%, 0%, 0)", - strokeColor: "#73DD24", + const saveBlob = (blob) => { + const url = window.URL.createObjectURL(blob); + props?.setRecordedAudio(url); }; return (
{(() => { - if (status === "recording" && recordingInitialized) { + if (status === "recording") { return ( -
- +
+ @@ -91,26 +109,51 @@ const AudioRecorderCompair = (props) => { ); } else { return ( -
- {(!props.dontShowListen || props.recordedAudio) && ( - <> - {!props.pauseAudio ? ( -
{ props.playAudio(true); }}> - - +
+ {props?.originalText && + (!props.dontShowListen || props.recordedAudio) && ( + <> + {!props.pauseAudio ? ( +
{ + props.playAudio(true); + }} + > + + + +
+ ) : ( + { + props.playAudio(false); + }} + > + -
- ) : ( - { props.playAudio(false); }}> - - - )} - - )} + )} + + )}
- {!props.showOnlyListen && ( - + {props?.originalText && !props.showOnlyListen && ( + {!props.recordedAudio ? : } )} @@ -119,20 +162,9 @@ const AudioRecorderCompair = (props) => { ); } })()} - -
-
- - -
-
); }; -export default AudioRecorderCompair; +export default AudioRecorder; diff --git a/src/utils/VoiceAnalyser.js b/src/utils/VoiceAnalyser.js index 93e43bcc..2e8e2694 100644 --- a/src/utils/VoiceAnalyser.js +++ b/src/utils/VoiceAnalyser.js @@ -30,7 +30,8 @@ import { } from "./constants"; import config from "./urlConstants.json"; import { filterBadWords } from "./Badwords"; -// import S3Client from '../config/awsS3'; +import S3Client from "../config/awsS3"; +import { PutObjectCommand } from "@aws-sdk/client-s3"; /* eslint-disable */ const AudioPath = { @@ -87,8 +88,8 @@ function VoiceAnalyser(props) { recordedAudio ? recordedAudio : props.contentId - ? `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/all-audio-files/${lang}/${props.contentId}.wav` - : AudioPath[1][10] + ? `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/all-audio-files/${lang}/${props.contentId}.wav` + : AudioPath[1][10] ); set_temp_audio(audio); setPauseAudio(val); @@ -189,19 +190,19 @@ function VoiceAnalyser(props) { } }, [recordedAudio]); - useEffect(()=>{ - if(props.isNextButtonCalled){ + useEffect(() => { + if (props.isNextButtonCalled) { if (recordedAudioBase64 !== "") { const lang = getLocalData("lang") || "ta"; fetchASROutput(lang, recordedAudioBase64); - setLoader(true) + setLoader(true); } } - },[props.isNextButtonCalled]) + }, [props.isNextButtonCalled]); useEffect(() => { if (recordedAudioBase64 !== "") { - if( props.setIsNextButtonCalled){ + if (props.setIsNextButtonCalled) { props.setIsNextButtonCalled(false); } } @@ -258,7 +259,7 @@ function VoiceAnalyser(props) { const { originalText, contentType, contentId, currentLine } = props; const responseStartTime = new Date().getTime(); let responseText = ""; - let profanityWord = "" + let profanityWord = ""; let newThresholdPercentage = 0; let data = {}; @@ -279,16 +280,21 @@ function VoiceAnalyser(props) { ); data = updateLearnerData; responseText = data.responseText; - profanityWord = await filterBadWords(data.responseText); + profanityWord = await filterBadWords(data.responseText); if (profanityWord !== data.responseText) { props?.setOpenMessageDialog({ message: "Please avoid using inappropriate language.", isError: true, }); - } + } newThresholdPercentage = data?.subsessionTargetsCount || 0; - if (contentType.toLowerCase() !== 'word') { - handlePercentageForLife(newThresholdPercentage, contentType, data?.subsessionFluency, lang); + if (contentType.toLowerCase() !== "word") { + handlePercentageForLife( + newThresholdPercentage, + contentType, + data?.subsessionFluency, + lang + ); } } @@ -358,10 +364,11 @@ function VoiceAnalyser(props) { // TODO: Remove false when REACT_APP_AWS_S3_BUCKET_NAME and keys added var audioFileName = ""; - if (process.env.REACT_APP_CAPTURE_AUDIO === "true" && false) { + if (process.env.REACT_APP_CAPTURE_AUDIO === "true") { let getContentId = currentLine; - audioFileName = `${process.env.REACT_APP_CHANNEL - }/${sessionId}-${Date.now()}-${getContentId}.wav`; + audioFileName = `${ + process.env.REACT_APP_CHANNEL + }/${sessionId}-${Date.now()}-${getContentId}.wav`; const command = new PutObjectCommand({ Bucket: process.env.REACT_APP_AWS_S3_BUCKET_NAME, @@ -373,7 +380,7 @@ function VoiceAnalyser(props) { }); try { const response = await S3Client.send(command); - } catch (err) { } + } catch (err) {} } response( @@ -400,23 +407,23 @@ function VoiceAnalyser(props) { ); setApiResponse(callUpdateLearner ? data.status : "success"); - if(props.handleNext){ + if (props.handleNext) { props.handleNext(); - if(temp_audio !== null){ + if (temp_audio !== null) { temp_audio.pause(); setPauseAudio(false); } } setLoader(false); - if( props.setIsNextButtonCalled){ + if (props.setIsNextButtonCalled) { props.setIsNextButtonCalled(false); } } catch (error) { setLoader(false); - if(props.handleNext){ + if (props.handleNext) { props.handleNext(); } - if( props.setIsNextButtonCalled){ + if (props.setIsNextButtonCalled) { props.setIsNextButtonCalled(false); } setRecordedAudioBase64(""); @@ -425,92 +432,95 @@ function VoiceAnalyser(props) { } }; - const handlePercentageForLife = (percentage, contentType, fluencyScore, language) => { + const handlePercentageForLife = ( + percentage, + contentType, + fluencyScore, + language + ) => { try { - if (livesData) { - let totalSyllables = livesData.totalTargets; - if (language === "en") { - if (totalSyllables > 50) { - totalSyllables = 50; - } + if (livesData) { + let totalSyllables = livesData.totalTargets; + if (language === "en") { + if (totalSyllables > 50) { + totalSyllables = 50; } - // Calculate the current percentage based on total targets. - percentage = Math.round((percentage / totalSyllables) * 100); - - // Define the total number of lives and adjust the threshold based on syllables. - const totalLives = 5; - let threshold = 30; // Default threshold - - // Adjust the threshold based on total syllables. - if (totalSyllables <= 100) threshold = 30; - else if (totalSyllables > 100 && totalSyllables <= 150) threshold = 25; - else if (totalSyllables > 150 && totalSyllables <= 175) threshold = 20; - else if (totalSyllables > 175 && totalSyllables <= 250) threshold = 15; - else if (totalSyllables > 250 && totalSyllables <= 500) threshold = 10; - else if (totalSyllables > 500) threshold = 5; - - // Calculate lives lost based on percentage. - let livesLost = Math.floor(percentage / (threshold / totalLives)); - - // Check fluency criteria and adjust lives lost accordingly. - let meetsFluencyCriteria; - switch (contentType.toLowerCase()) { - case 'word': - meetsFluencyCriteria = fluencyScore < 2; - break; - case 'sentence': - meetsFluencyCriteria = fluencyScore < 6; - break; - case 'paragraph': - meetsFluencyCriteria = fluencyScore < 10; - break; - default: - meetsFluencyCriteria = true; // Assume criteria met if not specified. - } + } + // Calculate the current percentage based on total targets. + percentage = Math.round((percentage / totalSyllables) * 100); + + // Define the total number of lives and adjust the threshold based on syllables. + const totalLives = 5; + let threshold = 30; // Default threshold + + // Adjust the threshold based on total syllables. + if (totalSyllables <= 100) threshold = 30; + else if (totalSyllables > 100 && totalSyllables <= 150) threshold = 25; + else if (totalSyllables > 150 && totalSyllables <= 175) threshold = 20; + else if (totalSyllables > 175 && totalSyllables <= 250) threshold = 15; + else if (totalSyllables > 250 && totalSyllables <= 500) threshold = 10; + else if (totalSyllables > 500) threshold = 5; + + // Calculate lives lost based on percentage. + let livesLost = Math.floor(percentage / (threshold / totalLives)); + + // Check fluency criteria and adjust lives lost accordingly. + let meetsFluencyCriteria; + switch (contentType.toLowerCase()) { + case "word": + meetsFluencyCriteria = fluencyScore < 2; + break; + case "sentence": + meetsFluencyCriteria = fluencyScore < 6; + break; + case "paragraph": + meetsFluencyCriteria = fluencyScore < 10; + break; + default: + meetsFluencyCriteria = true; // Assume criteria met if not specified. + } - // If fluency criteria are not met, reduce an additional life, but ensure it doesn't exceed the total lives. - if (!meetsFluencyCriteria && livesLost < totalLives) { - livesLost = Math.min(livesLost + 1, totalLives); - } + // If fluency criteria are not met, reduce an additional life, but ensure it doesn't exceed the total lives. + if (!meetsFluencyCriteria && livesLost < totalLives) { + livesLost = Math.min(livesLost + 1, totalLives); + } - // Determine the number of red and black lives to show. - const redLivesToShow = totalLives - livesLost; - let blackLivesToShow = 5; - if(livesLost <= 5){ - blackLivesToShow = livesLost; - } + // Determine the number of red and black lives to show. + const redLivesToShow = totalLives - livesLost; + let blackLivesToShow = 5; + if (livesLost <= 5) { + blackLivesToShow = livesLost; + } - // Prepare the new lives data. - let newLivesData = { - ...livesData, - blackLivesToShow, - redLivesToShow, - meetsFluencyCriteria: meetsFluencyCriteria, - }; - - // Play audio based on the change in lives. - const HeartGaain = - livesData.redLivesToShow === undefined - ? 5 - newLivesData.redLivesToShow - : livesData.redLivesToShow - newLivesData.redLivesToShow; - let isLiveLost; - if (HeartGaain > 0) { - isLiveLost = true; - } else { - isLiveLost = false; - } - const audio = new Audio(isLiveLost ? livesCut : livesAdd); - audio.play(); + // Prepare the new lives data. + let newLivesData = { + ...livesData, + blackLivesToShow, + redLivesToShow, + meetsFluencyCriteria: meetsFluencyCriteria, + }; - // Update the state or data structure with the new lives data. - setLivesData(newLivesData); + // Play audio based on the change in lives. + const HeartGaain = + livesData.redLivesToShow === undefined + ? 5 - newLivesData.redLivesToShow + : livesData.redLivesToShow - newLivesData.redLivesToShow; + let isLiveLost; + if (HeartGaain > 0) { + isLiveLost = true; + } else { + isLiveLost = false; } + const audio = new Audio(isLiveLost ? livesCut : livesAdd); + audio.play(); + + // Update the state or data structure with the new lives data. + setLivesData(newLivesData); + } } catch (e) { - console.log("error", e); + console.log("error", e); } -}; - - + }; // const getpermision = () => { // navigator.getUserMedia = @@ -543,6 +553,7 @@ function VoiceAnalyser(props) { //alert("Microphone Permission Denied"); }); }; + return (
{loader ? ( @@ -557,6 +568,7 @@ function VoiceAnalyser(props) { <> - appLanguages.includes(lang.lang) +export const languages = AllLanguages.filter((lang) => + appLanguages.includes(lang.lang) ); export const randomizeArray = (arr) => { diff --git a/src/views/AppContent/AppContent.jsx b/src/views/AppContent/AppContent.jsx index fb9ef9a6..d054867a 100644 --- a/src/views/AppContent/AppContent.jsx +++ b/src/views/AppContent/AppContent.jsx @@ -1,14 +1,13 @@ import React, { useEffect, Fragment } from "react"; -import { Routes, Route } from "react-router-dom"; +import { Routes, Route, useNavigate } from "react-router-dom"; import CustomizedSnackbars from "../../views/Snackbar/CustomSnackbar"; -import { useSelector } from "react-redux"; const PrivateRoute = (props) => { - const { virtualId } = useSelector((state) => state.user); - // const navigate = useNavigate(); + const virtualId = localStorage.getItem('virtualId'); + const navigate = useNavigate(); useEffect(() => { if (!virtualId && props.requiresAuth) { - // navigate("/"); + navigate("/login"); } }, [virtualId]); diff --git a/src/views/Practice/Practice.jsx b/src/views/Practice/Practice.jsx index ef33e3d7..3fe6ac43 100644 --- a/src/views/Practice/Practice.jsx +++ b/src/views/Practice/Practice.jsx @@ -3,9 +3,7 @@ import Mechanics2 from "../../components/Practice/Mechanics2"; import Mechanics3 from "../../components/Practice/Mechanics3"; import Mechanics4 from "../../components/Practice/Mechanics4"; import Mechanics5 from "../../components/Practice/Mechanics5"; -import { - useNavigate, -} from "../../../node_modules/react-router-dom/dist/index"; +import { useNavigate } from "react-router-dom"; import { callConfetti, getLocalData, @@ -13,7 +11,7 @@ import { practiceSteps, setLocalData, } from "../../utils/constants"; -import axios from "../../../node_modules/axios/index"; +import axios from "axios"; import WordsOrImage from "../../components/Mechanism/WordsOrImage"; import { uniqueId } from "../../services/utilService"; import useSound from "use-sound"; @@ -53,19 +51,15 @@ const Practice = () => { const TARGETS_PERCENTAGE = 0.3; const [openMessageDialog, setOpenMessageDialog] = useState(""); const lang = getLocalData("lang"); - const [totalSyllableCount, setTotalSyllableCount] = useState(''); - const [percentage, setPercentage] = useState(''); + const [totalSyllableCount, setTotalSyllableCount] = useState(""); + const [percentage, setPercentage] = useState(""); const [fluency, setFluency] = useState(false); const [isNextButtonCalled, setIsNextButtonCalled] = useState(false); const gameOver = (data, isUserPass) => { - let userWon = isUserPass ? true : false; - const meetsFluencyCriteria = livesData.meetsFluencyCriteria ? true : false; - setGameOverData({ gameOver: true, userWon, ...data, meetsFluencyCriteria}); - }; - - const isFirefox = () => { - return typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().includes('firefox'); + const userWon = isUserPass; + const meetsFluencyCriteria = livesData?.meetsFluencyCriteria; + setGameOverData({ gameOver: true, userWon, ...data, meetsFluencyCriteria }); }; useEffect(() => { @@ -73,6 +67,7 @@ const Practice = () => { setLivesData({ ...livesData, lives: LIVES }); } }, [startShowCase]); + const callConfettiAndPlay = () => { play(); callConfetti(); @@ -92,13 +87,9 @@ const Practice = () => { callConfettiAndPlay(); setTimeout(() => { - // alert( - // `You have successfully completed ${practiceSteps[currentPracticeStep].fullName} ` - // ); setOpenMessageDialog({ message: `You have successfully completed ${practiceSteps[currentPracticeStep].fullName} `, }); - // setDisableScreen(false); }, 1200); } }, [currentQuestion]); @@ -115,7 +106,6 @@ const Practice = () => { message: "Sorry I couldn't hear a voice. Could you please speak again?", isError: true, }); - // alert("Sorry I couldn't hear a voice. Could you please speak again?"); setVoiceText(""); setEnableNext(false); } @@ -128,7 +118,7 @@ const Practice = () => { }, [voiceText]); const send = (score) => { - if (process.env.REACT_APP_IS_APP_IFRAME === 'true') { + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { window.parent.postMessage({ score: score, message: "all-test-rig-score", @@ -137,23 +127,23 @@ const Practice = () => { }; const checkFluency = (contentType, fluencyScore) => { - switch (contentType.toLowerCase()) { - case 'word': - setFluency(fluencyScore < 2); - break; - case 'sentence': - setFluency(fluencyScore < 6); - break; - case 'paragraph': - setFluency(fluencyScore < 10); - break; - default: - setFluency(true); - } - } + switch (contentType.toLowerCase()) { + case "word": + setFluency(fluencyScore < 2); + break; + case "sentence": + setFluency(fluencyScore < 6); + break; + case "paragraph": + setFluency(fluencyScore < 10); + break; + default: + setFluency(true); + } + }; const handleNext = async (isGameOver) => { - setIsNextButtonCalled(true) + setIsNextButtonCalled(true); setEnableNext(false); try { @@ -192,7 +182,7 @@ const Practice = () => { currentPracticeProgress = Math.round( ((currentQuestion + 1 + currentPracticeStep * limit) / (practiceSteps.length * limit)) * - 100 + 100 ); } @@ -215,6 +205,7 @@ const Practice = () => { currentQuestion === questions.length - 1 || isGameOver ? currentPracticeStep + 1 : currentPracticeStep; + newPracticeStep = Number(newPracticeStep); let newQuestionIndex = currentQuestion === questions.length - 1 ? 0 : currentQuestion + 1; @@ -243,22 +234,22 @@ const Practice = () => { Log(data, "practice", "ET"); setPercentage(getSetData?.data?.percentage); checkFluency(currentContentType, getSetData?.data?.fluency); - if(process.env.REACT_APP_POST_LEARNER_PROGRESS === "true"){ - await axios.post( - `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.CREATE_LEARNER_PROGRESS}`, - { - userId: virtualId, - sessionId: sessionId, - subSessionId: sub_session_id, - milestoneLevel: getSetData?.data?.currentLevel, - totalSyllableCount: totalSyllableCount, - language: localStorage.getItem("lang"), - } - ); - } + if (process.env.REACT_APP_POST_LEARNER_PROGRESS === "true") { + await axios.post( + `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.CREATE_LEARNER_PROGRESS}`, + { + userId: virtualId, + sessionId: sessionId, + subSessionId: sub_session_id, + milestoneLevel: getSetData?.data?.currentLevel, + totalSyllableCount: totalSyllableCount, + language: localStorage.getItem("lang"), + } + ); + } setLocalData("previous_level", getSetData.data.previous_level); if (getSetData.data.sessionResult === "pass") { - try{ + try { await axios.post( `${process.env.REACT_APP_LEARNER_AI_ORCHESTRATION_HOST}/${config.URLS.ADD_LESSON}`, { @@ -273,13 +264,10 @@ const Practice = () => { ); gameOver({ link: "/assesment-end" }, true); return; - } - catch(e){ + } catch (e) { // catch error } } - - // navigate("/assesment-end"); } let quesArr = []; @@ -309,20 +297,20 @@ const Practice = () => { if (newPracticeStep === 0 || newPracticeStep === 5 || isGameOver) { gameOver(); return; - // navigate("/assesment-end"); } 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}` ); - setTotalSyllableCount(resGetContent?.data?.totalSyllableCount) + setTotalSyllableCount(resGetContent?.data?.totalSyllableCount); setLivesData({ ...livesData, totalTargets: resGetContent?.data?.totalSyllableCount, targetsForLives: resGetContent?.data?.subsessionTargetsCount * TARGETS_PERCENTAGE, targetPerLive: - (resGetContent?.data?.subsessionTargetsCount * TARGETS_PERCENTAGE) / LIVES, + (resGetContent?.data?.subsessionTargetsCount * TARGETS_PERCENTAGE) / + LIVES, }); let showcaseLevel = @@ -373,18 +361,13 @@ const Practice = () => { } }; - // const playAudio = () => { - // // const myAudio = localStorage.getItem("recordedAudio"); - // set_temp_audio(new Audio(recordedAudio)); - // }; - useEffect(() => { learnAudio(); }, [temp_audio]); const playTeacherAudio = () => { const contentId = questions[currentQuestion]?.contentId; - var audio = new Audio( + let audio = new Audio( `${process.env.REACT_APP_AWS_S3_BUCKET_CONTENT_URL}/all-audio-files/${lang}/${contentId}.wav` ); audio.addEventListener("canplaythrough", () => { @@ -402,11 +385,11 @@ const Practice = () => { setLoading(true); const lang = getLocalData("lang"); const virtualId = getLocalData("virtualId"); - const sessionId = getLocalData("sessionId"); + let sessionId = getLocalData("sessionId"); - if (!sessionId){ + if (!sessionId) { sessionId = uniqueId(); - localStorage.setItem("sessionId", sessionId) + localStorage.setItem("sessionId", sessionId); } const getMilestoneDetails = await axios.get( @@ -454,11 +437,12 @@ const Practice = () => { 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}` ); - setTotalSyllableCount(resWord?.data?.totalSyllableCount) + setTotalSyllableCount(resWord?.data?.totalSyllableCount); setLivesData({ ...livesData, totalTargets: resWord?.data?.totalSyllableCount, - targetsForLives: resWord?.data?.subsessionTargetsCount * TARGETS_PERCENTAGE, + targetsForLives: + resWord?.data?.subsessionTargetsCount * TARGETS_PERCENTAGE, targetPerLive: (resWord?.data?.subsessionTargetsCount * TARGETS_PERCENTAGE) / LIVES, }); @@ -543,11 +527,12 @@ const Practice = () => { 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}` ); - setTotalSyllableCount(resWord?.data?.totalSyllableCount) + setTotalSyllableCount(resWord?.data?.totalSyllableCount); setLivesData({ ...livesData, totalTargets: resWord?.data?.totalSyllableCount, - targetsForLives: resWord?.data?.subsessionTargetsCount * TARGETS_PERCENTAGE, + targetsForLives: + resWord?.data?.subsessionTargetsCount * TARGETS_PERCENTAGE, targetPerLive: (resWord?.data?.subsessionTargetsCount * TARGETS_PERCENTAGE) / LIVES, }); @@ -564,10 +549,10 @@ const Practice = () => { setCurrentQuestion(practiceProgress[virtualId]?.currentQuestion || 0); setLocalData("practiceProgress", JSON.stringify(practiceProgress)); } else { - if (process.env.REACT_APP_IS_APP_IFRAME === 'true') { + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { navigate("/"); - }else { - navigate("/discover-start") + } else { + navigate("/discover-start"); } } }; @@ -586,10 +571,6 @@ const Practice = () => { return str2.length - str1.length; }); - let fontSize = - questions[currentQuestion]?.contentType?.toLowerCase() === "paragraph" - ? 30 - : 40; let type = currentContentType?.toLowerCase(); if (type === "char" || type === "word") { const word = splitGraphemes(words[0].toLowerCase()).filter( @@ -610,7 +591,7 @@ const Practice = () => { variant="h5" component="h4" sx={{ - fontSize: `${fontSize}px`, + fontSize: "clamp(1.6rem, 2.5vw, 3.8rem)", fontWeight: 700, fontFamily: "Quicksand", lineHeight: "50px", @@ -634,7 +615,7 @@ const Practice = () => { component="h4" sx={{ color: "#333F61", - fontSize: `${fontSize}px`, + fontSize: "clamp(1.6rem, 2.5vw, 3.8rem)", fontWeight: 700, fontFamily: "Quicksand", lineHeight: "50px", @@ -660,7 +641,7 @@ const Practice = () => { component="h4" ml={1} sx={{ - fontSize: `${fontSize}px`, + fontSize: "clamp(1.6rem, 2.5vw, 3.8rem)", fontWeight: 700, fontFamily: "Quicksand", lineHeight: "50px", @@ -679,7 +660,7 @@ const Practice = () => { ml={1} sx={{ color: "#333F61", - fontSize: `${fontSize}px`, + fontSize: "clamp(1.6rem, 2.5vw, 3.8rem)", fontWeight: 700, fontFamily: "Quicksand", lineHeight: "50px", @@ -697,11 +678,12 @@ const Practice = () => { useEffect(() => { if (questions[currentQuestion]?.contentSourceData) { - if (process.env.REACT_APP_IS_APP_IFRAME === 'true') { - const contentSourceData = questions[currentQuestion]?.contentSourceData || []; - const stringLengths = contentSourceData.map(item => item.text.length); + if (process.env.REACT_APP_IS_APP_IFRAME === "true") { + const contentSourceData = + questions[currentQuestion]?.contentSourceData || []; + const stringLengths = contentSourceData.map((item) => item.text.length); const length = stringLengths[0]; - window.parent.postMessage({ type: 'stringLengths', length }, '*'); + window.parent.postMessage({ type: "stringLengths", length }); } } }, [questions[currentQuestion]]); @@ -754,7 +736,7 @@ const Practice = () => { setOpenMessageDialog, setEnableNext, isNextButtonCalled, - setIsNextButtonCalled + setIsNextButtonCalled, }} /> );