diff --git a/app.js b/app.js index a4d4cab..ab3cbf0 100644 --- a/app.js +++ b/app.js @@ -7,6 +7,7 @@ const path = require('path'); const cookieParser = require('cookie-parser'); const logger = require('morgan'); const i18n = require('i18n-2'); +const debugLocale = require('debug')('soundbirder:locale'); const indexRouter = require('./routes/index'); const apiRouter = require('./routes/api'); @@ -52,11 +53,11 @@ app.use(function (req, res, next) { if (rxLocale.test(req.url)) { const arr = rxLocale.exec(req.url); const locale = arr[1]; + debugLocale("Setting locale from url prefix", locale); req.i18n.setLocale(locale); + debugLocale("Locale set to", req.i18n.locale); } - if (req.cookies.locale === undefined) { - res.cookie('locale', req.i18n.locale, { maxAge: 90000 }); - } + res.cookie('locale', req.i18n.locale, { maxAge: 900000, sameSite: true }); // add extra logic next(); }); diff --git a/controllers/api.js b/controllers/api.js index cb16470..ce6c0b0 100644 --- a/controllers/api.js +++ b/controllers/api.js @@ -6,7 +6,11 @@ const quizzController = require('./quizz'); const QUIZZ_SIZE = process.env.QUIZZ_SIZE ? process.env.QUIZZ_SIZE : 5; -function check(req, res) { +function getHome(req, res) { + res.render('api', { + title: "SoundBirder api", + version: 0 + }); } function quizz(req, res) { @@ -15,8 +19,8 @@ function quizz(req, res) { const locale = req.i18n.locale; debugLocale("Locale:", locale); quizzController.generateQuizz({ lat, lng }, locale, QUIZZ_SIZE) - .then(({ species, correct, audio }) => { - req.session.correct = correct; + .then(({ species, answer, audio }) => { + req.session.answer = answer; res.json({ species, audio }); debug("Quizz sent"); }) @@ -27,12 +31,26 @@ function quizz(req, res) { }); } - -function getHome(req, res) { - res.render('api', { - title: "SoundBirder api", - version: 0 - }); +function check(req, res) { + const answer = req.query.species; + const correctAnswer = req.session.answer; + let result = {}; + if (answer === correctAnswer.speciesCode) { + debug("Correct answer"); + result = { + correct: true, + message: req.i18n.__('Correct!'), + answer: correctAnswer + }; + } else { + debug("Wrong answer"); + result = { + correct: false, + message: req.i18n.__('Wrong!'), + answer: correctAnswer + }; + } + res.json(result); } const game = { diff --git a/controllers/cache.js b/controllers/cache.js index 58f8580..02044d3 100644 --- a/controllers/cache.js +++ b/controllers/cache.js @@ -18,9 +18,9 @@ function cacheResponse(request, response) { } async function getCached(request) { - debug("Getting cached response", request); const cached = await redisClient.get(request); if (cached) { + debug("Got cached response", request); return JSON.parse(cached); } return null; diff --git a/controllers/quizz.js b/controllers/quizz.js index 44b348d..54bc34b 100644 --- a/controllers/quizz.js +++ b/controllers/quizz.js @@ -16,10 +16,17 @@ async function generateQuizz(coordinates, locale, size) { debugResponses('Localized species selection:', speciesSelectionLocalized); quizz.species = speciesSelectionLocalized; debug("Got species selection", quizz.species); - const answer = await choice(speciesSelectionLocalized); + let answer; + do { + answer = choice(speciesSelectionLocalized); + quizz.answer = answer; + quizz.audio = await getAudio(answer.sciName); + if (quizz.audio == undefined) { + debug("No audio found for species", answer.sciName); + debug("Trying again..."); + } + } while (quizz.audio == undefined); debug("Got answer", answer); - quizz.correct = answer.speciesCode; - quizz.audio = await getAudio(answer.sciName); debug("Got audio", quizz.audio); } catch (error) { debug("Error raised while generating quizz"); diff --git a/locales/en.js b/locales/en.js index 3cbabc1..4fbbea9 100644 --- a/locales/en.js +++ b/locales/en.js @@ -8,5 +8,8 @@ "The project is made with ♥ by Samuel ORTION": "The project is made with ♥ by Samuel ORTION", "SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird.": "SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird.", "The project is made with ♥ by Samuel ORTION.": "The project is made with ♥ by Samuel ORTION.", - "Welcome to SoundBirder' API": "Welcome to SoundBirder' API" + "Welcome to SoundBirder' API": "Welcome to SoundBirder' API", + "Wrong!": "Wrong!", + "Correct!": "Correct!", + "It was a": "It was a" } \ No newline at end of file diff --git a/locales/fr.js b/locales/fr.js index 16f1ae4..0f58d1d 100644 --- a/locales/fr.js +++ b/locales/fr.js @@ -4,5 +4,8 @@ "Contact": "Contact", "SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird": "SoundBirder est une application web open-source qui permet d'apprendre à reconnaitre les chants d'oiseaux. Elle se base sur des enregistrements audios de Xeno-Canto et sur les données de répartitions d'eBird", "Author": "Auteur", - "The project is made with ♥ by Samuel ORTION": "Ce projet est fait avec ♥ par Samuel ORTION" + "The project is made with ♥ by Samuel ORTION": "Ce projet est fait avec ♥ par Samuel ORTION", + "Correct!": "Correct!", + "Wrong!": "Incorrect!", + "It was a": "C'était un" } \ No newline at end of file diff --git a/public/javascripts/api-client.js b/public/javascripts/api-client.js index b3491ef..401d18e 100644 --- a/public/javascripts/api-client.js +++ b/public/javascripts/api-client.js @@ -1,4 +1,3 @@ - const API_VERSION = "0"; const API_URL = `/api/${API_VERSION}`; @@ -30,11 +29,11 @@ function getQuizz(lat, lng) { }); } -function checkResponse(speciesId) { - return query('game/check', { +function checkAnswer(speciesId) { + return query(`game/check`, { species: speciesId - }).then(response => { - return response.data.correct + }).then(data => { + return data; }).catch(error => { throw error; }); @@ -42,7 +41,7 @@ function checkResponse(speciesId) { const client = { getQuizz, - checkResponse + checkAnswer } export default client; \ No newline at end of file diff --git a/public/javascripts/game.js b/public/javascripts/game.js index c889f26..6bc185e 100644 --- a/public/javascripts/game.js +++ b/public/javascripts/game.js @@ -38,12 +38,43 @@ function displayQuizz(quizz) { proposal.classList.add('proposal'); let button = document.createElement('button'); button.classList.add('proposal-button'); - button.value = sp.code; + button.value = sp.speciesCode; button.innerText = sp.comName; proposal.appendChild(button); proposals.appendChild(proposal); + button.addEventListener('click', verifyAnswer); }); } +function verifyAnswer(event) { + let answer = event.target.value; + client.checkAnswer(answer) + .then(data => { + let message_class; + if (data.correct) + message_class = 'success'; + else + message_class = 'error'; + displayResult(message_class, data.message, data.answer); + }).catch(error => { + console.log(error); + }); +} + +function displayResult(message_class, message, species) { + let result = document.querySelector('.game-results-step .message'); + document.querySelector('.game-quizz-step').classList.toggle('none'); + result.classList.remove('success', 'error'); + result.classList.add(message_class); + result.innerText = message; + document.querySelector('.game-results-step').classList.remove('none'); + document.querySelector('.game-results-step .restart-button').addEventListener('click', restart); + document.querySelector('.game-results-step .species .com').innerText = species.comName; + document.querySelector('.game-results-step .species .sci').innerText = species.sciName; +} + +function restart() { + window.location.reload(); +} function game() { geolocationStep(); diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 101ad2b..32cd34c 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -7,8 +7,8 @@ body { margin: 0; } -body > * { - padding: 1em; +body>* { + padding: 1em; } .link { @@ -20,7 +20,7 @@ body > * { } .link:after { - content: ''; + content: ''; height: 1px; display: block; border-bottom: 2px dotted darkblue; @@ -51,25 +51,29 @@ footer .link:after { } main { - min-height: calc(100vh - 15em); + min-height: calc(100vh - 15em); } -#map { height: 50vh; } - -nav { - +#map { + height: 50vh; } +nav {} + nav li { - list-style: none; + list-style: none; } nav ul { - display: flex; - flex-direction: row; - justify-content: space-evenly; + display: flex; + flex-direction: row; + justify-content: space-evenly; } .none { - display: none; + display: none; } + +.species span { + margin-left: 1em; +} \ No newline at end of file diff --git a/routes/api.js b/routes/api.js index 25e6591..391dd29 100644 --- a/routes/api.js +++ b/routes/api.js @@ -9,6 +9,6 @@ router.route('/game/quizz') .get(apiController.game.quizz); router.route('/game/check') - .post(apiController.game.check); + .get(apiController.game.check); module.exports = router; \ No newline at end of file diff --git a/utils/choices.js b/utils/choices.js index 6fc2afc..c89eb44 100644 --- a/utils/choices.js +++ b/utils/choices.js @@ -12,7 +12,7 @@ function choices(array, number) { if (result.includes(c)) { i--; } else { - result.push(choice(array)); + result.push(c); } } return result; diff --git a/views/game.pug b/views/game.pug index ea63aa8..591b59a 100644 --- a/views/game.pug +++ b/views/game.pug @@ -8,9 +8,13 @@ .game-quizz-step.none ul.proposals audio - .game-result-step.none - p.result - button.button.restart-button.disabled + .game-results-step.none + p.message + .species.answer + p #{ __('It was a') } + span.com + span.sci + button.button.restart-button i(data-feather="repeat") link(rel="stylesheet" href="/dist/leaflet/leaflet.css") script(src="/dist/leaflet/leaflet.js")