diff --git a/package-lock.json b/package-lock.json
index 8e7dbe69..0df16a38 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
"express": "^4.19.2",
"i18n": "^0.15.1",
"i18next": "^23.10.1",
+ "jquery": "^3.7.1",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.3.0",
"react-countdown": "^2.3.5",
@@ -20,6 +21,7 @@
"react-router-dom": "^6.22.1"
},
"devDependencies": {
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"jest": "^29.7.0"
}
},
@@ -130,6 +132,18 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
+ "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/helper-compilation-targets": {
"version": "7.23.6",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
@@ -146,6 +160,29 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.24.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz",
+ "integrity": "sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.22.5",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-member-expression-to-functions": "^7.23.0",
+ "@babel/helper-optimise-call-expression": "^7.22.5",
+ "@babel/helper-replace-supers": "^7.24.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
"node_modules/@babel/helper-environment-visitor": {
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
@@ -180,6 +217,18 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz",
+ "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.23.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/helper-module-imports": {
"version": "7.24.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz",
@@ -211,6 +260,18 @@
"@babel/core": "^7.0.0"
}
},
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz",
+ "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/helper-plugin-utils": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz",
@@ -220,6 +281,23 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.24.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz",
+ "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-member-expression-to-functions": "^7.23.0",
+ "@babel/helper-optimise-call-expression": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
"node_modules/@babel/helper-simple-access": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
@@ -232,6 +310,18 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz",
+ "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/helper-split-export-declaration": {
"version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
@@ -383,6 +473,25 @@
"node": ">=6.0.0"
}
},
+ "node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz",
+ "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==",
+ "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-create-class-features-plugin": "^7.21.0",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-syntax-async-generators": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
@@ -530,10 +639,10 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@babel/plugin-syntax-top-level-await": {
+ "node_modules/@babel/plugin-syntax-private-property-in-object": {
"version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
- "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
+ "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
"dev": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.14.5"
@@ -545,13 +654,13 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@babel/plugin-syntax-typescript": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz",
- "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==",
+ "node_modules/@babel/plugin-syntax-top-level-await": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
"dev": true,
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.14.5"
},
"engines": {
"node": ">=6.9.0"
@@ -560,15 +669,13 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@babel/plugin-transform-modules-commonjs": {
+ "node_modules/@babel/plugin-syntax-typescript": {
"version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz",
- "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz",
+ "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==",
"dev": true,
"dependencies": {
- "@babel/helper-module-transforms": "^7.23.3",
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-simple-access": "^7.22.5"
+ "@babel/helper-plugin-utils": "^7.24.0"
},
"engines": {
"node": ">=6.9.0"
@@ -3831,6 +3938,11 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
+ "node_modules/jquery": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
+ "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
diff --git a/package.json b/package.json
index e7b64080..48ad91a2 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"express": "^4.19.2",
"i18n": "^0.15.1",
"i18next": "^23.10.1",
+ "jquery": "^3.7.1",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.3.0",
"react-countdown": "^2.3.5",
@@ -15,6 +16,7 @@
"react-router-dom": "^6.22.1"
},
"devDependencies": {
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"jest": "^29.7.0"
}
}
diff --git a/webapp/package-lock.json b/webapp/package-lock.json
index 41859609..9ecc289d 100644
--- a/webapp/package-lock.json
+++ b/webapp/package-lock.json
@@ -32,6 +32,7 @@
"zxcvbn": "^4.4.2"
},
"devDependencies": {
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"axios-mock-adapter": "^1.22.0",
"expect-puppeteer": "^9.0.2",
"jest": "^29.3.1",
@@ -669,9 +670,17 @@
}
},
"node_modules/@babel/plugin-proposal-private-property-in-object": {
- "version": "7.21.0-placeholder-for-preset-env.2",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
- "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "version": "7.21.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz",
+ "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==",
+ "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-create-class-features-plugin": "^7.21.0",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+ },
"engines": {
"node": ">=6.9.0"
},
@@ -1915,6 +1924,17 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.0-placeholder-for-preset-env.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/preset-env/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
diff --git a/webapp/package.json b/webapp/package.json
index 55422652..e843dbb5 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -53,6 +53,7 @@
]
},
"devDependencies": {
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"axios-mock-adapter": "^1.22.0",
"expect-puppeteer": "^9.0.2",
"jest": "^29.3.1",
diff --git a/webapp/public/correct.mp3 b/webapp/public/correct.mp3
new file mode 100644
index 00000000..37e3cac5
Binary files /dev/null and b/webapp/public/correct.mp3 differ
diff --git a/webapp/public/incorrect.mp3 b/webapp/public/incorrect.mp3
new file mode 100644
index 00000000..f5a83099
Binary files /dev/null and b/webapp/public/incorrect.mp3 differ
diff --git a/webapp/public/tictac.mp3 b/webapp/public/tictac.mp3
new file mode 100644
index 00000000..e0525154
Binary files /dev/null and b/webapp/public/tictac.mp3 differ
diff --git a/webapp/src/components/Instructions.js b/webapp/src/components/Instructions.js
index 160978ab..83ec9bf8 100644
--- a/webapp/src/components/Instructions.js
+++ b/webapp/src/components/Instructions.js
@@ -4,12 +4,14 @@ import {useTranslation} from "react-i18next";
+
function Instructions() {
const[t] = useTranslation("global");
return (
+
{t("instructions.title")}
@@ -57,7 +59,9 @@ function Instructions() {
+
+
);
}
diff --git a/webapp/src/components/fragments/NavBar.js b/webapp/src/components/fragments/NavBar.js
index cddffd1b..d6938b13 100644
--- a/webapp/src/components/fragments/NavBar.js
+++ b/webapp/src/components/fragments/NavBar.js
@@ -30,7 +30,9 @@ function Navbar() {
-
{t("navBar.title")}
+
+
{t("navBar.title")}
+
@@ -53,7 +55,11 @@ function Navbar() {
}
function Profile() {
- return
;
+ return (
+
+
+
+ );
}
function Help() {
diff --git a/webapp/src/components/loginAndRegistration/AddUser.js b/webapp/src/components/loginAndRegistration/AddUser.js
index 0714bd67..629f9c40 100644
--- a/webapp/src/components/loginAndRegistration/AddUser.js
+++ b/webapp/src/components/loginAndRegistration/AddUser.js
@@ -148,6 +148,8 @@ const AddUser = () => {
+
+
diff --git a/webapp/src/components/loginAndRegistration/Login.js b/webapp/src/components/loginAndRegistration/Login.js
index d7381d5f..ae209ddd 100644
--- a/webapp/src/components/loginAndRegistration/Login.js
+++ b/webapp/src/components/loginAndRegistration/Login.js
@@ -29,10 +29,14 @@ const Login = () => {
return (
-
+
@@ -78,6 +84,8 @@ function LinkRegister() {
);
}
+
+
export default Login;
// // src/components/Login.js
diff --git a/webapp/src/components/questionView/QuestionGenerator.js b/webapp/src/components/questionView/QuestionGenerator.js
index 28038332..32facf1a 100644
--- a/webapp/src/components/questionView/QuestionGenerator.js
+++ b/webapp/src/components/questionView/QuestionGenerator.js
@@ -9,6 +9,34 @@ class QuestionGenerator{
}
async generateQuestions(lang) {
+
+ // 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"]},'+
+ // '"1":{"question":"¿Which is the population of Gijon?","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"]},'+
+ // '"4":{"question":"¿Cuál es la capital de España?","answers":["Madrid","a","b","c"]},'+
+ // '"5":{"question":"¿Cuál es la capital de Turquía?","answers":["Ankara","a","b","c"]}}')
+
+ // let i = 0;
+ // var questions = [];
+ // for (const key in receivedQuestions) {
+ // questions[i] = new Question(receivedQuestions[key]);
+ // i += 1;
+ // }
+ // console.log(questions);
+ // return questions;
+ // } catch (error) {
+ // throw new Error(error);
+ // }
+
+
+
try {
const response = await axios.get(this.apiUrl + '/' + lang);
const receivedQuestions = await response.data;
@@ -22,9 +50,9 @@ class QuestionGenerator{
} catch (error) {
throw new Error(error);
}
+
}
}
-export default QuestionGenerator;
-
+export default QuestionGenerator;
\ No newline at end of file
diff --git a/webapp/src/components/questionView/QuestionView.js b/webapp/src/components/questionView/QuestionView.js
index 96d0978c..ca598e11 100644
--- a/webapp/src/components/questionView/QuestionView.js
+++ b/webapp/src/components/questionView/QuestionView.js
@@ -14,10 +14,13 @@ const creationHistoricalRecord = new CreationHistoricalRecord();
const questionGenerator = new QuestionGenerator();
var points = 0;
function QuestionView(){
+
const [numQuestion, setnumQuestion] = useState(-1);
const [questions, setQuestions] = useState(null);
const[t, i18n] = useTranslation("global");
const {user} = useUserContext();
+ const [audio] = useState(new Audio('/tictac.mp3'));
+
const generateQuestions = async (numQuestion) => {
if (numQuestion < 0) {
@@ -33,24 +36,34 @@ function QuestionView(){
}
}
- function revealColorsForAnswers(){
- let colorCorrectAnswer='#6EF26E';//green
- let colorIncorrectAnswer='#FF6666'; //red
+ function revealColorsForAnswers(correctAnswer, answerGiven){
+ let colorCorrectAnswer = '#6EF26E'; // verde
+ let colorIncorrectAnswer = '#FF6666'; // rojo
+ let audioCorrect = new Audio('/correct.mp3');
+ let audioIncorrect = new Audio('/incorrect.mp3');
+
$('.answerButton').each(function() {
var dataValue = $(this).attr('data-value');
- if (dataValue === false || dataValue === "false")
+ if (dataValue === false || dataValue === "false") {
$(this).css('background-color', colorIncorrectAnswer); // Cambia el color de fondo del botón actual a rojo
-
- else{
+ } else {
$(this).css({
'background-color': colorCorrectAnswer,
- 'text-decoration': 'underline'// Underline the text of the button for correct answers
+ 'text-decoration': 'underline' // Subraya el texto del botón para respuestas correctas
});
}
+ if(answerGiven===correctAnswer){
+ audio.pause();
+ audioCorrect.play(); // Reproduce el sonido de respuesta incorrecta
+ }
+ else{
+ audio.pause();
+ audioIncorrect.play(); // Reproduce el sonido de respuesta correcta
+ }
$(this).css('pointer-events', 'none');
- });
-
+ });
}
+
function setColorsBackToNormal() {
let colorOriginal = '#9f97ff';
$('.answerButton').each(function() {
@@ -64,8 +77,10 @@ function QuestionView(){
function computePointsForQuestion(correctAnswer, answerGiven){
if(answerGiven===correctAnswer){
points+=100;
+ audio.pause();
}else if(points-50>=0){
points-=50;
+ audio.pause();
}else{
points = 0;
}
@@ -81,7 +96,7 @@ function QuestionView(){
computePointsForQuestion(questions[numQuestion].getCorrectAnswer(), text);
//reveal answer to user for 1 sec
- revealColorsForAnswers();
+ revealColorsForAnswers(questions[numQuestion].getCorrectAnswer(), text);
setTimeout(function() {
//after one second set colors back to normal
setColorsBackToNormal();
@@ -90,6 +105,7 @@ function QuestionView(){
//Last question sends the record
if(!(numQuestion < questions.length - 1)){
+ audio.pause();
creationHistoricalRecord.setDate(Date.now());
creationHistoricalRecord.setPoints(points);
creationHistoricalRecord.sendRecord(user.username);
@@ -105,18 +121,59 @@ function QuestionView(){
return (
{numQuestion >= 0 ?
- :
+ :
{t("questionView.no_questions_message")}
}
);
}
-function QuestionComponent({questions, numQuestion, handleClick, t, points}){
+function QuestionComponent({questions, numQuestion, handleClick, t, points, audio, language}){
+ const speakQuestion = () => {
+ const speech = new SpeechSynthesisUtterance();
+ speech.lang = language;
+ console.log(language);
+ 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
+ window.speechSynthesis.speak(speech);
+ })
+ .catch(error => {
+ console.error("Error al obtener las voces para el idioma:", error);
+ });
+ };
+
+ // Función para obtener las voces disponibles para un idioma
+ const getVoicesForLanguage = (language) => {
+ return new Promise((resolve, reject) => {
+ const speech = new SpeechSynthesisUtterance();
+ speech.text = questions[numQuestion].getQuestion();
+ speech.lang = language;
+
+ speech.addEventListener("error", reject);
+
+ speech.addEventListener("end", () => {
+ const voices = window.speechSynthesis.getVoices();
+ if (voices.length > 0)
+ resolve(voices);
+ });
+
+ window.speechSynthesis.speak(speech);
+ });
+ };
+
+
+
const renderer = ({seconds, completed }) => {
if (completed) {
+ audio.pause();
return
{t("questionView.end_countdown")}; // Rendered when countdown completes
} else {
+ if (audio.paused) {
+ audio.loop = true; // Loop of tiktak
+ audio.play();
+ }
return
{seconds} {t("questionView.seconds")}; // Render countdown
}
};
@@ -127,7 +184,7 @@ function QuestionComponent({questions, numQuestion, handleClick, t, points}){
-
{questions[numQuestion].getQuestion()}
+
{questions[numQuestion].getQuestion()}
@@ -145,6 +202,7 @@ function QuestionComponent({questions, numQuestion, handleClick, t, points}){
) : (
<>
+
{t("questionView.finished_game")}
{points} {t("questionView.point")}
< RecordList record={creationHistoricalRecord.getRecord().game}/>
diff --git a/webapp/src/components/questionView/QuestionView.test.js b/webapp/src/components/questionView/QuestionView.test.js
index 25392c62..a3a34531 100644
--- a/webapp/src/components/questionView/QuestionView.test.js
+++ b/webapp/src/components/questionView/QuestionView.test.js
@@ -10,12 +10,34 @@ import MockAdapter from 'axios-mock-adapter';
import {configure} from '@testing-library/dom';
+// Función para configurar el mock de global.Audio
+const setupAudioMock = () => {
+ jest.spyOn(global, 'Audio').mockImplementation(() => ({
+ play: jest.fn(),
+ pause: jest.fn(),
+ loop: true
+ }));
+};
+
+// Mock the SpeechSynthesisUtterance and window.speechSynthesis APIs
+global.SpeechSynthesisUtterance = jest.fn(() => ({
+ lang: '',
+ text: '',
+ addEventListener: jest.fn(),
+ dispatchEvent: jest.fn(),
+}));
+
+global.window.speechSynthesis = {
+ getVoices: jest.fn(() => []),
+ speak: jest.fn(),
+};
+
configure({
testIdAttribute: 'data-value',
});
const mockAxios = new MockAdapter(axios);
-
+jest.setTimeout(10000);
i18en.use(initReactI18next).init({
@@ -40,6 +62,26 @@ describe('Question View component', () => {
// Wait for questions to load
});
+
+ // Test for sound functionality
+ it('speaks the question when the speaker button is clicked', async () => {
+ const questionText = "What is the population of Oviedo?";
+ mockAxios.onGet('http://localhost:8000/questions/en').reply(200,
+ [{question: questionText,
+ answers: ["225089","272357","267855","231841"]}]);
+
+ await act(async () => {
+ render(
);
+ });
+
+ fireEvent.click(screen.getByText('🔊'));
+
+ // Check if the SpeechSynthesisUtterance is called with the correct text
+ expect(global.SpeechSynthesisUtterance).toHaveBeenCalledWith();
+
+
+ });
+
it('shows a question and answers',async () => {
mockAxios.onGet('http://localhost:8000/questions/en').reply(200,
@@ -60,7 +102,8 @@ describe('Question View component', () => {
expect(screen.getByText('231841')).toBeInTheDocument()
});
- it('shows colors to reveal correct answer', async () => {
+ it('shows colors to reveal correct answer and it sounds', async () => {
+ setupAudioMock();
mockAxios.onGet('http://localhost:8000/questions/en').reply(200,
[{question: "What is the population of Oviedo?",
answers: ["225089","272357","267855","231841"]}]);
@@ -70,6 +113,8 @@ describe('Question View component', () => {
})
await waitFor(() => expect(screen.getByText('What is the population of Oviedo?')).toBeInTheDocument());
fireEvent.click(screen.getByTestId('true'));//clicamos en la respuesta correcta
+ expect(global.Audio).toHaveBeenCalledWith('/correct.mp3');
+
// Esperar un segundo antes de continuar
await waitFor(() => {
// Clic en un botón de respuesta con data-value=true
@@ -78,7 +123,8 @@ describe('Question View component', () => {
expect(correctAnswerButton).toHaveStyle('background-color: #6EF26E');
}, { timeout: 1000 }); // Esperar 1 segundo
});
- it('shows colors to reveal false answer', async () => {
+ it('shows colors to reveal false answer and it sounds', async () => {
+ setupAudioMock()
mockAxios.onGet('http://localhost:8000/questions/en').reply(200,
[{question: "What is the population of Oviedo?",
answers: ["225089","272357","267855","231841"]}]);
@@ -87,16 +133,19 @@ describe('Question View component', () => {
})
await waitFor(() => expect(screen.getByText('What is the population of Oviedo?')).toBeInTheDocument());
- fireEvent.click(screen.getAllByTestId('false')[0]);
+ fireEvent.click(screen.getAllByTestId('false')[1]);//clicamos en la respuesta incorrecta
+ expect(global.Audio).toHaveBeenCalledWith('/incorrect.mp3');
// Esperar un segundo antes de continuar
await waitFor(() => {
// Clic en un botón de respuesta con data-value=true
- const incorrectAnswerButton = screen.getAllByTestId('false')[0];
+ const incorrectAnswerButton = screen.getAllByTestId('false')[1];
// Verificar que el botón tenga el color esperado
expect(incorrectAnswerButton).toHaveStyle('background-color: #FF6666');
}, { timeout: 1000 }); // Esperar 1 segundo
});
- it('shows timer', async () => {
+
+ it('shows timer and tiktak sound', async () => {
+ setupAudioMock()
mockAxios.onGet('http://localhost:8000/questions/en').reply(200,
[{question: "What is the population of Oviedo?",
answers: ["225089","272357","267855","231841"]}]);
@@ -105,8 +154,32 @@ describe('Question View component', () => {
})
await waitFor(() => expect(screen.getByText('What is the population of Oviedo?')).toBeInTheDocument());
+ 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
- });
+ });
+
+
+ // it('renders end message when countdown completes', async() => {
+
+ // setupAudioMock()
+ // mockAxios.onGet('http://localhost:8000/questions/en').reply(200,
+ // [{question: "What is the population of Oviedo?",
+ // answers: ["225089","272357","267855","231841"]}]);
+ // await act(async () =>{
+ // await render(
);
+
+ // })
+ // await waitFor(() => expect(screen.getByText('What is the population of Oviedo?')).toBeInTheDocument());
+
+ // const timerElement = screen.getByText(new RegExp(`(\\d+) ${i18en.t('questionView.seconds')}`));
+ // expect(timerElement).toBeInTheDocument(); // Verificar que el temporizador esté presente en el DOM
+
+
+ // await waitFor(() => {
+ // expect(screen.getByText("Time's up!")).toBeInTheDocument();
+ // }, { timeout: 9800 }); // Esperar 10 segundos
+ // });
});
\ No newline at end of file
diff --git a/webapp/src/custom.css b/webapp/src/custom.css
index 7abd6447..4718020d 100644
--- a/webapp/src/custom.css
+++ b/webapp/src/custom.css
@@ -272,6 +272,80 @@ button[type="submit"]:hover {
box-shadow: inset 0px 0px 25px #3700ff;
}
+.button-back {
+ display: block;
+ position: relative;
+ width: 56px;
+ height: 56px;
+ margin: 0;
+ overflow: hidden;
+ outline: none;
+ background-color: transparent;
+ cursor: pointer;
+ border: 0;
+}
+
+.button-back:before,
+.button-back:after {
+ content: "";
+ position: absolute;
+ border-radius: 50%;
+ inset: 7px;
+}
+
+.button-back:before {
+ border: 4px solid #f0eeef;
+ transition: opacity 0.4s cubic-bezier(0.77, 0, 0.175, 1) 80ms,
+ transform 0.5s cubic-bezier(0.455, 0.03, 0.515, 0.955) 80ms;
+}
+
+.button-back:after {
+ border: 4px solid #96daf0;
+ transform: scale(1.3);
+ transition: opacity 0.4s cubic-bezier(0.165, 0.84, 0.44, 1),
+ transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
+ opacity: 0;
+}
+
+.button-back:hover:before,
+.button-back:focus:before {
+ opacity: 0;
+ transform: scale(0.7);
+ transition: opacity 0.4s cubic-bezier(0.165, 0.84, 0.44, 1),
+ transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
+}
+
+.button-back:hover:after,
+.button-back:focus:after {
+ opacity: 1;
+ transform: scale(1);
+ transition: opacity 0.4s cubic-bezier(0.77, 0, 0.175, 1) 80ms,
+ transform 0.5s cubic-bezier(0.455, 0.03, 0.515, 0.955) 80ms;
+}
+
+.button-box {
+ display: flex;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.button-elem {
+ display: block;
+ width: 20px;
+ height: 20px;
+ margin: 17px 18px 0 18px;
+ transform: rotate(180deg);
+ fill: #f0eeef;
+}
+
+.button-back :hover .button-box,
+.button-back :focus .button-box {
+ transition: 0.4s;
+ transform: translateX(-56px);
+}
+
+
/*---------------------------Instructions---------------------------*/
.instructions_title {
font-size: 35px;
@@ -1242,6 +1316,21 @@ svg {
max-width: 200px;
}
+.altavoz {
+ margin-right: 10px;
+ background: #000000;
+ border: none;
+ outline: none;
+ border-radius: 40px;
+ box-shadow: 0 0 10px #9f97ff;
+ cursor: pointer;
+ font-size: 21px;
+ color: black;
+ font-weight: 700;
+ margin: 0.5em;
+ max-width: 200px;
+}
+
.questionContainer p, span{
font-size: 20px;
}