From c9774b81120c3c5db4325ccf8779fa0105b6d265 Mon Sep 17 00:00:00 2001 From: lauratbg Date: Mon, 22 Apr 2024 17:11:41 +0200 Subject: [PATCH 01/11] Added keyboard events, added the voice for answers but it continues talking when passed the question --- .../questionView/QuestionGenerator.js | 4 +-- .../components/questionView/QuestionView.js | 36 ++++++++++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/webapp/src/components/questionView/QuestionGenerator.js b/webapp/src/components/questionView/QuestionGenerator.js index 9d204004..f85aa212 100644 --- a/webapp/src/components/questionView/QuestionGenerator.js +++ b/webapp/src/components/questionView/QuestionGenerator.js @@ -9,7 +9,7 @@ class QuestionGenerator{ } async generateQuestions(lang, type, amount) { - /* + try { //const response = await fetch(this.apiUrl); //const receivedQuestions = await response.json(); @@ -35,7 +35,7 @@ class QuestionGenerator{ throw new Error(error); } - */ + try { let response; diff --git a/webapp/src/components/questionView/QuestionView.js b/webapp/src/components/questionView/QuestionView.js index b84d4c4a..eaee5583 100644 --- a/webapp/src/components/questionView/QuestionView.js +++ b/webapp/src/components/questionView/QuestionView.js @@ -1,8 +1,7 @@ import QuestionGenerator from './QuestionGenerator'; import CreationHistoricalRecord from './CreationHistoricalRecord'; -import { useState } from 'react'; import "../../custom.css"; -import React from "react"; +import React, { useState, useEffect } from "react"; import Countdown from 'react-countdown'; import {useTranslation} from "react-i18next"; import $ from 'jquery'; @@ -129,15 +128,42 @@ function QuestionView({type= "COMPETITIVE", amount=5}){ function QuestionComponent({questions, numQuestion, handleClick, t, points, audio, language}){ + useEffect(() => { + const handleKeyPress = (event) => { + if (event.key === 's') { + speakQuestionAndAnswers(); + } else { + const answerIndex = parseInt(event.key) - 1; + if (!isNaN(answerIndex) && answerIndex >= 0 && answerIndex < questions[numQuestion].getAnswers().length) { + handleClick(questions[numQuestion].getAnswers()[answerIndex]); + } + } + }; + + window.addEventListener("keypress", handleKeyPress); + + return () => { + window.removeEventListener("keypress", handleKeyPress); + }; + }, [numQuestion, questions, handleClick]); + const speakQuestionAndAnswers = () => { + // speakQuestion(questions[numQuestion].getQuestion()); + speakAnswers(questions[numQuestion].getAnswers()); + }; - const speakQuestion = () => { + + + const speakAnswers = (answers) => { const speech = new SpeechSynthesisUtterance(); speech.lang = language; - console.log(language); + let concatenatedAnswers = Array.isArray(answers) ? answers.map((answer, index) => `${index + 1}. ${answer}`).join(". ") : ''; + getVoicesForLanguage(language) .then(voices => { // const voice = voices.find(voice => voice.lang === language); // speech.voice = voice || voices[0]; // If there is no voice for the lang, choose the first one + speech.text = concatenatedAnswers; + window.speechSynthesis.speak(speech); }) .catch(error => { @@ -185,7 +211,7 @@ function QuestionComponent({questions, numQuestion, handleClick, t, points, audi
-

{questions[numQuestion].getQuestion()}

+

{questions[numQuestion].getQuestion()}

From af586da3316c0d21aa138cff45fc400c6b91976b Mon Sep 17 00:00:00 2001 From: lauratbg Date: Tue, 23 Apr 2024 17:35:07 +0200 Subject: [PATCH 02/11] Now the tiktak sound is commented, the voice stops if the player answers the question, the time ends or changing of page --- .../components/questionView/QuestionView.js | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/webapp/src/components/questionView/QuestionView.js b/webapp/src/components/questionView/QuestionView.js index eaee5583..097cf601 100644 --- a/webapp/src/components/questionView/QuestionView.js +++ b/webapp/src/components/questionView/QuestionView.js @@ -85,6 +85,9 @@ function QuestionView({type= "COMPETITIVE", amount=5}){ } } function handleClick(text){ + // Detener el síntesis de voz + window.speechSynthesis.cancel(); + //create the record to record the response creationHistoricalRecord.addQuestion(questions[numQuestion].getQuestion(), questions[numQuestion].getAnswers(), @@ -113,7 +116,7 @@ function QuestionView({type= "COMPETITIVE", amount=5}){ }, 1000); } - + if(questions === null) generateQuestions(numQuestion) @@ -146,6 +149,19 @@ function QuestionComponent({questions, numQuestion, handleClick, t, points, audi window.removeEventListener("keypress", handleKeyPress); }; }, [numQuestion, questions, handleClick]); + + //To stop the voice when changing of page + useEffect(() => { + const handleBeforeUnload = () => { + window.speechSynthesis.cancel(); + }; + + window.addEventListener("beforeunload", handleBeforeUnload); + + return () => { + window.removeEventListener("beforeunload", handleBeforeUnload); + }; + }, []); const speakQuestionAndAnswers = () => { // speakQuestion(questions[numQuestion].getQuestion()); speakAnswers(questions[numQuestion].getAnswers()); @@ -199,7 +215,7 @@ function QuestionComponent({questions, numQuestion, handleClick, t, points, audi } else { if (audio.paused) { audio.loop = true; // Loop of tiktak - audio.play(); + // audio.play(); } return {seconds} {t("questionView.seconds")}; // Render countdown } From 1d5ccbb57734ea0c5cd5058257a9822d9736ff08 Mon Sep 17 00:00:00 2001 From: lauratbg Date: Tue, 23 Apr 2024 17:51:02 +0200 Subject: [PATCH 03/11] Improvement of instructions --- webapp/src/components/Instructions.js | 18 +++++++++++++++++- .../components/questionView/QuestionView.js | 2 +- webapp/src/translations/en/global.json | 11 ++++++++--- webapp/src/translations/es/global.json | 14 ++++++++++---- webapp/src/translations/tk/global.json | 11 ++++++++--- 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/webapp/src/components/Instructions.js b/webapp/src/components/Instructions.js index 83ec9bf8..50abfcb5 100644 --- a/webapp/src/components/Instructions.js +++ b/webapp/src/components/Instructions.js @@ -50,6 +50,22 @@ function Instructions() { {t("instructions.time_limit_p1")} +
+

    {t("instructions.voice")}

    +
  • + {t("instructions.voice_p1")} +
  • +
  • + {t("instructions.voice_p2")} +
  • +
  • + {t("instructions.voice_p3")} +
  • +
  • + {t("instructions.voice_p4")} +
  • +
+

    {t("instructions.have_fun")}

  • @@ -57,7 +73,7 @@ function Instructions() {
- +
diff --git a/webapp/src/components/questionView/QuestionView.js b/webapp/src/components/questionView/QuestionView.js index 097cf601..4c1682f5 100644 --- a/webapp/src/components/questionView/QuestionView.js +++ b/webapp/src/components/questionView/QuestionView.js @@ -215,7 +215,7 @@ function QuestionComponent({questions, numQuestion, handleClick, t, points, audi } else { if (audio.paused) { audio.loop = true; // Loop of tiktak - // audio.play(); + audio.play(); } return {seconds} {t("questionView.seconds")}; // Render countdown } diff --git a/webapp/src/translations/en/global.json b/webapp/src/translations/en/global.json index c076edbd..8dccd5cb 100644 --- a/webapp/src/translations/en/global.json +++ b/webapp/src/translations/en/global.json @@ -28,12 +28,17 @@ "how_to_play_p3": "Choose the correct answer from the options provided.", "how_to_play_p4": "Click or tap on your selected answer to submit it.", "scoring": "Scoring:", - "scoring_p1": "Each correct answer earns you x points.", - "scoring_p2": "Incorrect answers do not deduct points.", + "scoring_p1": "Each correct answer earns you 100 points.", + "scoring_p2": "Incorrect answers deduct 50 points.", "time_limit": "Time Limit:", "time_limit_p1": "Some game modes may have a time limit for answering each question. Be quick and accurate to maximize your score.", "have_fun": "Have Fun!:", - "have_fun_p1": "Enjoy the game and test your knowledge. Good luck!" + "have_fun_p1": "Enjoy the game and test your knowledge. Good luck!", + "voice": "Voice and Keyboard Accessibility", +"voice_p1": "You have the option to listen to the question and the answer. By pressing a button, you can hear the question and then the answers with a number preceding each one.", +"voice_p2": "By pressing a button, you can hear the question and then the answers with a number preceding each one.", +"voice_p3": "This number is used to respond by pressing the corresponding number on the keyboard to the answer you choose.", +"voice_p4": "Additionally, you can activate the voice by pressing the letter 's'." }, "login": { "title": "Login", diff --git a/webapp/src/translations/es/global.json b/webapp/src/translations/es/global.json index 898263d1..fbff1484 100644 --- a/webapp/src/translations/es/global.json +++ b/webapp/src/translations/es/global.json @@ -31,13 +31,19 @@ "how_to_play_p4": "Haz clic o toca tu respuesta seleccionada para enviarla.", "scoring": "Puntuación:", - "scoring_p1": "Cada respuesta correcta te otorga x puntos.", - "scoring_p2": "Las respuestas incorrectas no restan puntos.", + "scoring_p1": "Cada respuesta correcta te otorga 100 puntos.", + "scoring_p2": "Las respuestas incorrectas restan 50 puntos.", "time_limit": "Límite de Tiempo:", "time_limit_p1": "Algunos modos de juego pueden tener un límite de tiempo para responder cada pregunta. Sé rápido y preciso para maximizar tu puntuación.", "have_fun": "¡Diviértete!", - "have_fun_p1": "¡Disfruta del juego y pon a prueba tus conocimientos. Buena suerte!" - }, + "have_fun_p1": "¡Disfruta del juego y pon a prueba tus conocimientos. Buena suerte!", + "voice": "Voz y accesibilidad por teclado", + "voice_p1": "Tienes la opción de escuchar la pregunta y la respuesta. Pulsando un botón podrás escuchar la pregunta y seguidamente las respuestas con un número delante.", + "voice_p2": "Pulsando un botón podrás escuchar la pregunta y seguidamente las respuestas con un número delante.", + "voice_p3": "Este número sirve para responder pulsando por teclado el número correspondiente a la respuesta que desees.", + "voice_p4": "Además, podrás activar la voz pulsando la letra s" + + }, "login": { "title": "Inicio de sesión", "username_placeholder": "Nombre de usuario", diff --git a/webapp/src/translations/tk/global.json b/webapp/src/translations/tk/global.json index 3c822aaf..43af4274 100644 --- a/webapp/src/translations/tk/global.json +++ b/webapp/src/translations/tk/global.json @@ -28,12 +28,17 @@ "how_to_play_p3": "Sağlanan seçenekler arasından doğru cevabı seçin.", "how_to_play_p4": "Seçtiğiniz cevabı göndermek için üzerine tıklayın veya dokunun.", "scoring": "Puanlama:", - "scoring_p1": "Her doğru cevap size x puan kazandırır.", - "scoring_p2": "Yanlış cevaplar puan kesintisi yapmaz.", + "scoring_p1": "Her doğru cevap size 100 puan kazandırır.", + "scoring_p2": "Yanlış cevaplar 50 puan keser.", "time_limit": "Zaman Sınırı:", "time_limit_p1": "Bazı oyun modları, her soruyu cevaplama için bir zaman sınırına sahip olabilir. Skorunuzu maksimize etmek için hızlı ve doğru olun.", "have_fun": "İyi Eğlenceler!:", - "have_fun_p1": "Oyunun tadını çıkarın ve bilginizi test edin. İyi şanslar!" + "have_fun_p1": "Oyunun tadını çıkarın ve bilginizi test edin. İyi şanslar!", + "voice": "Ses ve Klavye Erişilebilirliği", +"voice_p1": "Soruyu ve cevabı dinleme seçeneğiniz bulunmaktadır. Bir düğmeye basarak, soruyu dinleyebilir ve ardından her biri önünde bir numara olan cevapları dinleyebilirsiniz.", +"voice_p2": "Bir düğmeye basarak, soruyu dinleyebilir ve ardından her biri önünde bir numara olan cevapları dinleyebilirsiniz.", +"voice_p3": "Bu numara, seçtiğiniz cevaba klavyede karşılık gelen numarayı basarak yanıt vermek için kullanılır.", +"voice_p4": "Ayrıca, 's' harfine basarak sesi aktive edebilirsiniz." }, "login": { "title": "Giriş", From 1f44bcc3f3202ab17b02ef168b98a023c6027bff Mon Sep 17 00:00:00 2001 From: lauratbg Date: Tue, 23 Apr 2024 18:16:23 +0200 Subject: [PATCH 04/11] Solved little bug --- webapp/src/components/questionView/QuestionView.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/webapp/src/components/questionView/QuestionView.js b/webapp/src/components/questionView/QuestionView.js index 4c1682f5..0b56c165 100644 --- a/webapp/src/components/questionView/QuestionView.js +++ b/webapp/src/components/questionView/QuestionView.js @@ -21,6 +21,13 @@ function QuestionView({type= "COMPETITIVE", amount=5}){ const [audio] = useState(new Audio('/tictac.mp3')); + //To stop tiktak sound when changing of page + useEffect(() => { + return () => { + audio.pause(); + }; + }, []); + const generateQuestions = async (numQuestion) => { if (numQuestion < 0) { try { From 7aedeb029745dc3c503ba2ab3f5537e8ce835dfd Mon Sep 17 00:00:00 2001 From: lauratbg Date: Wed, 24 Apr 2024 16:00:27 +0200 Subject: [PATCH 05/11] There are tests of question view that not pass. Removed tiktak sound --- .../questionView/QuestionGenerator.js | 4 +-- .../components/questionView/QuestionView.js | 36 +++++++++---------- .../questionView/QuestionView.test.js | 4 +-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/webapp/src/components/questionView/QuestionGenerator.js b/webapp/src/components/questionView/QuestionGenerator.js index 84180a75..393a9723 100644 --- a/webapp/src/components/questionView/QuestionGenerator.js +++ b/webapp/src/components/questionView/QuestionGenerator.js @@ -9,14 +9,14 @@ class QuestionGenerator{ } async generateQuestions(lang, type, amount, token) { - /* + try { //const response = await fetch(this.apiUrl); //const receivedQuestions = await response.json(); //Mockup console.log("type: "+type+" amount: "+amount) - const receivedQuestions = JSON.parse('{"0":{"question":"¿Cuál es la población de Oviedo?","answers":["225089","191325","220587","121548"]},'+ + const receivedQuestions = JSON.parse('{"0":{"question":"What is the population of Oviedo?","answers":["225089","191325","220587","121548"]},'+ '"1":{"question":"¿Cuál es la población de Gijón?","answers":["275274","159658","233982","305554"]},'+ '"2":{"question":"¿Cuál es la población de Avilés?","answers":["82568","115595","41284","122200"]},'+ '"3":{"question":"¿Cuál es la capital de Asturias?","answers":["Ciudad de Oviedo","a","b","c"]},'+ diff --git a/webapp/src/components/questionView/QuestionView.js b/webapp/src/components/questionView/QuestionView.js index 58ae430b..edf9c099 100644 --- a/webapp/src/components/questionView/QuestionView.js +++ b/webapp/src/components/questionView/QuestionView.js @@ -18,15 +18,15 @@ function QuestionView({type= "COMPETITIVE", amount=5}){ const [questions, setQuestions] = useState(null); const[t, i18n] = useTranslation("global"); const cookie = JSON.parse(Cookies.get('user')) - const [audio] = useState(new Audio('/tictac.mp3')); + // const [audio] = useState(new Audio('/tictac.mp3')); //To stop tiktak sound when changing of page - useEffect(() => { - return () => { - audio.pause(); - }; - }, []); + // useEffect(() => { + // return () => { + // audio.pause(); + // }; + // }, []); const generateQuestions = async (numQuestion) => { if (numQuestion < 0) { @@ -61,11 +61,11 @@ function QuestionView({type= "COMPETITIVE", amount=5}){ }); } if(answerGiven===correctAnswer){ - audio.pause(); + // audio.pause(); audioCorrect.play(); // Reproduce el sonido de respuesta incorrecta } else{ - audio.pause(); + // audio.pause(); audioIncorrect.play(); // Reproduce el sonido de respuesta correcta } $(this).css('pointer-events', 'none'); @@ -85,10 +85,10 @@ function QuestionView({type= "COMPETITIVE", amount=5}){ function computePointsForQuestion(correctAnswer, answerGiven){ if(answerGiven===correctAnswer){ points+=100; - audio.pause(); + // audio.pause(); }else if(points-50>=0){ points-=50; - audio.pause(); + // audio.pause(); }else{ points = 0; } @@ -116,7 +116,7 @@ function QuestionView({type= "COMPETITIVE", amount=5}){ //Last question sends the record if(!(numQuestion < questions.length - 1)){ - audio.pause(); + // audio.pause(); creationHistoricalRecord.setCompetitive(type === 'COMPETITIVE'); creationHistoricalRecord.setDate(Date.now()); creationHistoricalRecord.setPoints(points); @@ -133,12 +133,12 @@ function QuestionView({type= "COMPETITIVE", amount=5}){ return (
{numQuestion >= 0 ? - : + :

{t("questionView.no_questions_message")}

}
); } -function QuestionComponent({questions, numQuestion, handleClick, t, points, audio, language}){ +function QuestionComponent({questions, numQuestion, handleClick, t, points, /*audio,*/ language}){ useEffect(() => { const handleKeyPress = (event) => { @@ -219,13 +219,13 @@ function QuestionComponent({questions, numQuestion, handleClick, t, points, audi const renderer = ({seconds, completed }) => { if (completed) { - audio.pause(); + // audio.pause(); return {t("questionView.end_countdown")}; // Rendered when countdown completes } else { - if (audio.paused) { - audio.loop = true; // Loop of tiktak - audio.play(); - } + // if (audio.paused) { + // audio.loop = true; // Loop of tiktak + // audio.play(); + // } return {seconds} {t("questionView.seconds")}; // Render countdown } }; diff --git a/webapp/src/components/questionView/QuestionView.test.js b/webapp/src/components/questionView/QuestionView.test.js index d00a36e9..67701c98 100644 --- a/webapp/src/components/questionView/QuestionView.test.js +++ b/webapp/src/components/questionView/QuestionView.test.js @@ -130,14 +130,14 @@ describe('Question View component', () => { }, { timeout: 1000 }); // Esperar 1 segundo }); - it('shows timer and tiktak sound', async () => { + it('shows timer', async () => { setupAudioMock() await act(async () =>{ await render(); }) await waitFor(() => expect(screen.getByText('What is the population of Oviedo?')).toBeInTheDocument()); - expect(global.Audio).toHaveBeenCalledWith('/tictac.mp3'); + // expect(global.Audio).toHaveBeenCalledWith('/tictac.mp3'); const timerElement = screen.getByText(new RegExp(`(\\d+) ${i18en.t('questionView.seconds')}`)); expect(timerElement).toBeInTheDocument(); // Verificar que el temporizador esté presente en el DOM From 1137ee163f82ec6fe2017b9e85e0271f2cbde227 Mon Sep 17 00:00:00 2001 From: lauratbg Date: Wed, 24 Apr 2024 21:25:08 +0200 Subject: [PATCH 06/11] Checking things --- .../GameConfigurator/GameConfigurator.js | 4 +- .../questionView/QuestionGenerator.js | 44 +++++++++---------- .../questionView/QuestionView.test.js | 6 +-- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/webapp/src/components/GameConfigurator/GameConfigurator.js b/webapp/src/components/GameConfigurator/GameConfigurator.js index 4a536729..9bfa0cf6 100644 --- a/webapp/src/components/GameConfigurator/GameConfigurator.js +++ b/webapp/src/components/GameConfigurator/GameConfigurator.js @@ -30,7 +30,7 @@ function GameConfigurator(){

{t("gameConfigurator.game_config")}

{t("gameConfigurator.custo_game")}

- +