diff --git a/src/app/App.ts b/src/app/App.ts index 8481685..ea139c7 100644 --- a/src/app/App.ts +++ b/src/app/App.ts @@ -41,7 +41,7 @@ export class App { this.router.addRoute(GlobalConstants.ROUTE_STATISTICS, (path: string) => statisticsController.resolve(path)); if (!PathBus.getRealCurrentPath() || PathBus.getRealCurrentPath() === '') { - PathBus.setCurrentPath(GlobalConstants.ROUTE_MAIN); + PathBus.setCurrentPath(GlobalConstants.ROUTE_WORDBOOK); } else { PathBus.setCurrentPath(PathBus.getRealCurrentPath()); } diff --git a/src/app/controllers/AudiocallController.ts b/src/app/controllers/AudiocallController.ts index c05c891..664cb97 100644 --- a/src/app/controllers/AudiocallController.ts +++ b/src/app/controllers/AudiocallController.ts @@ -1,21 +1,15 @@ import { AbstractController } from './AbstractController'; -import { AudiocallStartPage } from '../views/audiocall/AudiocallStartPage'; import { Menu } from '../views/menu/Menu'; +import { AudiocallPage } from '../views/audiocall/AudiocallPage'; export class AudiocallController extends AbstractController { resolve(path: string) { this.rootNode.innerHTML = ''; this.rootNode.append(new Menu().getElement()); - const currentPage = +path.split('/')[2] || 1; - const currentGroup = +path.split('/')[1] || 1; - if (!path) { - const startPage = new AudiocallStartPage(); - this.rootNode.append(startPage.getElement()); - } else { - const startPage = new AudiocallStartPage(currentGroup, currentPage); + const currentGroup = Number(path.split('/')[1]) || -1; + const currentPage = Number(path.split('/')[2]) || -1; - this.rootNode.append(startPage.getElement()); - } + this.rootNode.append(new AudiocallPage(currentGroup, currentPage).getElement()); } } diff --git a/src/app/controllers/SprintController.ts b/src/app/controllers/SprintController.ts index e44ff5b..42f2943 100644 --- a/src/app/controllers/SprintController.ts +++ b/src/app/controllers/SprintController.ts @@ -1,21 +1,15 @@ import { AbstractController } from './AbstractController'; -import { SprintStartPage } from '../views/sprint/SprintStartPage'; import { Menu } from '../views/menu/Menu'; +import { SprintPage } from '../views/sprint/SprintPage'; export class SprintController extends AbstractController { resolve(path: string) { this.rootNode.innerHTML = ''; this.rootNode.append(new Menu().getElement()); - const currentPage = +path.split('/')[2] || 1; - const currentGroup = +path.split('/')[1] || 1; - if (!path) { - const startPage = new SprintStartPage(); - this.rootNode.append(startPage.getElement()); - } else { - const startPage = new SprintStartPage(currentGroup, currentPage); + const currentGroup = Number(path.split('/')[1]) || -1; + const currentPage = Number(path.split('/')[2]) || -1; - this.rootNode.append(startPage.getElement()); - } + this.rootNode.append(new SprintPage(currentGroup, currentPage).getElement()); } } diff --git a/src/app/controllers/WordbookController.ts b/src/app/controllers/WordbookController.ts index db6a121..7272213 100644 --- a/src/app/controllers/WordbookController.ts +++ b/src/app/controllers/WordbookController.ts @@ -3,13 +3,20 @@ import { WordService } from '../services/WordService'; import { Wordbook } from '../views/wordBook/Wordbook'; import { GlobalConstants } from '../../GlobalConstants'; import { Menu } from '../views/menu/Menu'; +import { PathBus } from '../services/PathBus'; export class WordbookController extends AbstractController { resolve(path: string) { this.rootNode.innerHTML = ''; this.rootNode.append(new Menu().getElement()); - let currentPage = +path.split('/')[2]; - let currentGroup = +path.split('/')[1]; + + let currentGroup = Number(path.split('/')[1]); + let currentPage = Number(path.split('/')[2]); + + if (!currentGroup || !currentPage) { + PathBus.setCurrentPath(`${GlobalConstants.ROUTE_WORDBOOK}/1/1`); + return; + } if (currentPage < 1) { currentPage = 1; diff --git a/src/app/interfaces/IAudiocallQuestion.ts b/src/app/interfaces/IAudiocallQuestion.ts deleted file mode 100644 index 1df036b..0000000 --- a/src/app/interfaces/IAudiocallQuestion.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IAudiocallAnswer } from './IAudiocallAnswer'; -import { IWord } from './IWord'; - -export interface IAudiocallQuestion { - wordData: IWord, - variants: IAudiocallAnswer[], -} diff --git a/src/app/interfaces/IAudiocallQuestionArray.ts b/src/app/interfaces/IAudiocallQuestionArray.ts deleted file mode 100644 index ad7a75c..0000000 --- a/src/app/interfaces/IAudiocallQuestionArray.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IAudiocallQuestion } from './IAudiocallQuestion'; - -export interface IAudiocallQuestionArray { - questions: Array, - currentQuestion: number, -} diff --git a/src/app/interfaces/IAudiocallAnswer.ts b/src/app/interfaces/IGameAnswer.ts similarity index 68% rename from src/app/interfaces/IAudiocallAnswer.ts rename to src/app/interfaces/IGameAnswer.ts index 17ce38f..f8e0036 100644 --- a/src/app/interfaces/IAudiocallAnswer.ts +++ b/src/app/interfaces/IGameAnswer.ts @@ -1,6 +1,6 @@ import { IWord } from './IWord'; -export interface IAudiocallAnswer { +export interface IGameAnswer { wordData: IWord, isCorrect: boolean, } diff --git a/src/app/interfaces/IGameQuestion.ts b/src/app/interfaces/IGameQuestion.ts new file mode 100644 index 0000000..eb53fa8 --- /dev/null +++ b/src/app/interfaces/IGameQuestion.ts @@ -0,0 +1,7 @@ +import { IGameAnswer } from './IGameAnswer'; +import { IWord } from './IWord'; + +export interface IGameQuestion { + wordData: IWord, + variants: Array, +} diff --git a/src/app/interfaces/IGameQuestionArray.ts b/src/app/interfaces/IGameQuestionArray.ts new file mode 100644 index 0000000..d2f9415 --- /dev/null +++ b/src/app/interfaces/IGameQuestionArray.ts @@ -0,0 +1,6 @@ +import { IGameQuestion } from './IGameQuestion'; + +export interface IGameQuestionArray { + questions: Array, + currentQuestion: number, +} diff --git a/src/app/interfaces/ISprintQuestionData.ts b/src/app/interfaces/ISprintQuestionData.ts deleted file mode 100644 index 483918d..0000000 --- a/src/app/interfaces/ISprintQuestionData.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IWord } from './IWord'; - -export interface ISprintQuestionData { - word: IWord, - translate: string, - isCorrect: boolean, -} diff --git a/src/app/services/UserWordService.ts b/src/app/services/UserWordService.ts index 97e0c85..ad4cf6a 100644 --- a/src/app/services/UserWordService.ts +++ b/src/app/services/UserWordService.ts @@ -1,6 +1,7 @@ import { GlobalConstants } from '../../GlobalConstants'; import { IUserWord } from '../interfaces/IUserWord'; import { IUserWordData } from '../interfaces/IUserWordData'; +import { IUserWordOptional } from '../interfaces/IUserWordOptional'; import { axiosIntance } from './axiosIntance'; export class UserWordService { @@ -70,7 +71,11 @@ export class UserWordService { if (wordData) { return { difficulty: wordData.difficulty, - optional: { ...wordData.optional }, + optional: { + successCounter: wordData.optional.successCounter, + failCounter: wordData.optional.failCounter, + isLearned, + } as IUserWordOptional, } as IUserWordData; } return null; diff --git a/src/app/views/audiocall/AudiocallGameField.ts b/src/app/views/audiocall/AudiocallGameField.ts index 4e896b5..c197b5a 100644 --- a/src/app/views/audiocall/AudiocallGameField.ts +++ b/src/app/views/audiocall/AudiocallGameField.ts @@ -1,77 +1,83 @@ import { dch } from '../dch'; import Renderable from '../Renderable'; -import { IAudiocallQuestionArray } from '../../interfaces/IAudiocallQuestionArray'; import { AudiocallQuestion } from './AudiocallQuestion'; -import { IAudiocallQuestion } from '../../interfaces/IAudiocallQuestion'; -import './AudiocallGameField.scss'; import { IResultData } from '../../interfaces/IResultData'; -import { AudiocallStatisticPage } from './AudiocallStatisticPage'; -import { TokenProvider } from '../../services/TokenProvider'; -import { UserWordService } from '../../services/UserWordService'; +import { IGameQuestionArray } from '../../interfaces/IGameQuestionArray'; +import { IGameQuestion } from '../../interfaces/IGameQuestion'; + +import './AudiocallGameField.scss'; export class AudiocallGameField extends Renderable { - page: number | undefined; + private questionsArray: IGameQuestionArray | null = null; - data: IAudiocallQuestionArray; + private result: Array = []; - result: IResultData[]; + private answerChain = 0; - answerChain: number; + private maxAnswerChain = 0; - maxAnswerChain: number; + private title: HTMLElement; - userId: string | null; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onFinish = (result: IResultData[], answerChain: number) => {}; - startQuestion: number; + constructor() { + super(); - title: HTMLElement; + this.title = dch('h2', ['audiocall-page--title'], 'Audio decoding'); + this.rootNode = dch('div', ['gamefield-container'], '', this.title); + } - constructor(questionArrayData: IAudiocallQuestionArray) { - super(); - this.data = questionArrayData; + setQuestionsArray(questionsArray: IGameQuestionArray) { + this.questionsArray = questionsArray; this.answerChain = 0; this.maxAnswerChain = 1; this.result = []; - this.userId = TokenProvider.getUserId(); - this.title = dch('h2', ['audiocall-page--title'], 'Audio decoding'); - this.rootNode = dch('div', ['gamefield-container'], '', this.title); - this.startQuestion = 0; - this.gameCycle(this.data.questions, this.startQuestion); } - gameCycle(questionArray: IAudiocallQuestion[], index: number) { - if (index >= questionArray.length) { - this.onFinish(this.result, this.maxAnswerChain); + startGame() { + if (!this.questionsArray) { return; } - const cardQuestion = new AudiocallQuestion(this.data.questions[index]); - this.rootNode.append(cardQuestion.getElement()); + + this.answerChain = 0; + this.maxAnswerChain = 1; + this.result = []; + + this.renderCard(this.questionsArray.questions[this.questionsArray.currentQuestion]); + } + + nextQuestion() { + if (!this.questionsArray) { + return; + } + + if (this.questionsArray.questions.length > this.questionsArray.currentQuestion + 1) { + this.questionsArray.currentQuestion += 1; + this.renderCard(this.questionsArray.questions[this.questionsArray.currentQuestion]); + } else { + this.onFinish(this.result, this.maxAnswerChain); + } + } + + renderCard(question: IGameQuestion) { + const cardQuestion = new AudiocallQuestion(question); cardQuestion.onAnswer = (questionData, isCorrect) => { - if (isCorrect) { - if (this.userId && !TokenProvider.checkIsExpired()) { - UserWordService.setWordStatistic(this.userId, questionData.id, isCorrect) - .catch((e) => console.error(e)); - } - this.answerChain += 1; - if (this.answerChain > this.maxAnswerChain) { - this.maxAnswerChain = this.answerChain; - } - } else if (!isCorrect) { - if (this.userId && !TokenProvider.checkIsExpired()) { - UserWordService.setWordStatistic(this.userId, questionData.id, isCorrect) - .catch((e) => console.error(e)); - } + this.result.push({ questionData, isCorrect }); + cardQuestion.destroy(); + this.nextQuestion(); + + if (!isCorrect) { this.answerChain = 0; + return; + } + + this.answerChain += 1; + if (this.answerChain > this.maxAnswerChain) { + this.maxAnswerChain = this.answerChain; } - cardQuestion.destroy(); - this.result.push({ questionData, isCorrect }); - this.gameCycle(questionArray, index + 1); }; - } - onFinish = (result: IResultData[], answerChain: number) => { - this.rootNode.innerHTML = ''; - const resultPage = new AudiocallStatisticPage(result, answerChain); - this.rootNode.append(this.title, resultPage.getElement()); - }; + this.rootNode.append(cardQuestion.getElement()); + } } diff --git a/src/app/views/audiocall/AudiocallPage.ts b/src/app/views/audiocall/AudiocallPage.ts new file mode 100644 index 0000000..830a2b6 --- /dev/null +++ b/src/app/views/audiocall/AudiocallPage.ts @@ -0,0 +1,72 @@ +import { AudiocallStatisticPage } from './AudiocallStatisticPage'; + +import { IWord } from '../../interfaces/IWord'; +import { WordService } from '../../services/WordService'; +import Renderable from '../Renderable'; +import { AudiocallGameField } from './AudiocallGameField'; +import { AudiocallStartPage } from './AudiocallStartPage'; +import { IResultData } from '../../interfaces/IResultData'; +import { IGameQuestionArray } from '../../interfaces/IGameQuestionArray'; +import { IGameQuestion } from '../../interfaces/IGameQuestion'; +import { IGameAnswer } from '../../interfaces/IGameAnswer'; + +export class AudiocallPage extends Renderable { + constructor(group: number, page: number) { + super(); + const startPage = new AudiocallStartPage(group - 1, page - 1); + const gameField = new AudiocallGameField(); + + startPage.onStartGame = (selectedGroup: number, selectedPage: number) => { + WordService.getWordsByGroupAndPage(selectedGroup, selectedPage) + .then((wordData) => { + const questionsData = { + questions: this.createQuestions(wordData.array), + currentQuestion: 0, + } as IGameQuestionArray; + + this.rootNode.innerHTML = ''; + gameField.setQuestionsArray(questionsData); + gameField.startGame(); + this.rootNode.append(gameField.getElement()); + }) + .catch((e) => console.error(e)); + }; + + gameField.onFinish = (result: IResultData[], answerChain: number) => { + this.rootNode.innerHTML = ''; + const resultPage = new AudiocallStatisticPage(result, answerChain); + this.rootNode.append(resultPage.getElement()); + }; + + this.rootNode = startPage.getElement(); + } + + createVariantsForAnswer = (wordsArray: Array, count: number, currentWord: IWord) => { + const onlyDifferentWords = wordsArray.filter((word) => word.id !== currentWord.id); + + const shuffledAndCutArray = [...onlyDifferentWords].sort(() => Math.random() - 0.5).slice(0, count - 1); + + const incorrectVariants = shuffledAndCutArray.map((word) => ({ + wordData: word, + isCorrect: false, + } as IGameAnswer)); + + const correctVariant = { + wordData: currentWord, + isCorrect: true, + } as IGameAnswer; + + return incorrectVariants.concat(correctVariant).sort(() => Math.random() - 0.5); + }; + + createQuestions = (wordsArray: Array) => { + const result = wordsArray.map( + (word) => ({ + wordData: word, + variants: this.createVariantsForAnswer(wordsArray, 4, word), + } as IGameQuestion), + ); + + return result.sort(() => Math.random() - 0.5); + }; +} diff --git a/src/app/views/audiocall/AudiocallQuestion.scss b/src/app/views/audiocall/AudiocallQuestion.scss index c1c7710..2deb933 100644 --- a/src/app/views/audiocall/AudiocallQuestion.scss +++ b/src/app/views/audiocall/AudiocallQuestion.scss @@ -9,6 +9,14 @@ $color-light-m: #f6f6f6; align-items: center; gap: 30px; + .word-container--question-text { + font-weight: 100; + font-size: 36px; + line-height: 60px; + text-align: center; + text-transform: uppercase; + } + &--title { font-size: 36px; line-height: 50px; @@ -20,7 +28,8 @@ $color-light-m: #f6f6f6; .audio-btn { border: 1px solid #767676; background-image: url('../../../assets/img/svg/icon-audio.svg'); - background-size: cover; + background-size: auto; + background-position: center; background-repeat: no-repeat; height: 62px; width: 62px; @@ -33,7 +42,7 @@ $color-light-m: #f6f6f6; } .button-answer { - background: url('/src/assets/img/svg/corner-left-top.svg') top left no-repeat, + background: url('/src/assets/img/svg/corner-left-top.svg') top left no-repeat, url('/src/assets/img/svg/corner-right-bottom.svg') bottom right no-repeat; display: flex; padding: 10px 30px; diff --git a/src/app/views/audiocall/AudiocallQuestion.ts b/src/app/views/audiocall/AudiocallQuestion.ts index 8649887..9d20c9c 100644 --- a/src/app/views/audiocall/AudiocallQuestion.ts +++ b/src/app/views/audiocall/AudiocallQuestion.ts @@ -1,37 +1,51 @@ import { dch } from '../dch'; import Renderable from '../Renderable'; -import { IAudiocallQuestion } from '../../interfaces/IAudiocallQuestion'; import { GlobalConstants } from '../../../GlobalConstants'; import { musicPlayer } from '../../services/SingleMusicPlayer'; import { IWord } from '../../interfaces/IWord'; import './AudiocallQuestion.scss'; +import { IGameQuestion } from '../../interfaces/IGameQuestion'; export class AudiocallQuestion extends Renderable { - questionData: IAudiocallQuestion; + private questionData: IGameQuestion; - audioWord: string; + private audioWord: string; - playAudioButton: HTMLElement; + private playAudioButton: HTMLElement; - title: HTMLElement; + private title: HTMLElement; - answersContainer: HTMLElement; + private answersContainer: HTMLElement; - constructor(questionData: IAudiocallQuestion) { + constructor(questionData: IGameQuestion) { super(); + this.questionData = questionData; this.title = dch('h3', ['word-container--title'], 'In progress'); this.audioWord = `${GlobalConstants.DEFAULT_API_URL}/${this.questionData.wordData.audio}`; this.playAudio(this.audioWord); this.answersContainer = dch('div', ['game-btn-container']); + + const questionText = dch('div', ['word-container--question-text'], 'Choose correct one'); + this.playAudioButton = dch('button', ['audio-btn']); this.playAudioButton.onclick = () => { this.playAudio(this.audioWord); }; - this.rootNode = dch('div', ['word-container'], '', this.title, this.playAudioButton, this.answersContainer); + + this.rootNode = dch( + 'div', + ['word-container'], + '', + this.title, + this.playAudioButton, + questionText, + this.answersContainer, + ); + this.questionData.variants.sort(() => Math.random() - 0.5) .forEach((answer, index) => { - const answerBtnNum = dch('div', ['button-answer--count'], `${index}`); + const answerBtnNum = dch('div', ['button-answer--count'], `${index + 1}`); const answerBtn = dch('div', ['button-answer--text'], answer.wordData.wordTranslate); const answerBtnContainer = dch('div', ['button-answer'], '', answerBtn, answerBtnNum); this.answersContainer.append(answerBtnContainer); @@ -39,16 +53,17 @@ export class AudiocallQuestion extends Renderable { this.onAnswer(this.questionData.wordData, answer.isCorrect); }; }); - document.addEventListener('keydown', this.handlerKey); + document.addEventListener('keyup', this.handlerKey); } - playAudio = (audio: string) => { + private playAudio = (audio: string) => { musicPlayer.setPlayList([audio]); musicPlayer.play() .catch((e) => console.error(e)); }; destroy() { + document.removeEventListener('keyup', this.handlerKey); this.rootNode.remove(); } @@ -58,16 +73,16 @@ export class AudiocallQuestion extends Renderable { handlerKey = (event: KeyboardEvent) => { if (event.code === 'Digit1') { this.onAnswer(this.questionData.wordData, this.questionData.variants[0].isCorrect); - document.removeEventListener('keydown', this.handlerKey); + document.removeEventListener('keyup', this.handlerKey); } else if (event.code === 'Digit2') { this.onAnswer(this.questionData.wordData, this.questionData.variants[1].isCorrect); - document.removeEventListener('keydown', this.handlerKey); + document.removeEventListener('keyup', this.handlerKey); } else if (event.code === 'Digit3') { this.onAnswer(this.questionData.wordData, this.questionData.variants[2].isCorrect); - document.removeEventListener('keydown', this.handlerKey); + document.removeEventListener('keyup', this.handlerKey); } else if (event.code === 'Digit4') { this.onAnswer(this.questionData.wordData, this.questionData.variants[3].isCorrect); - document.removeEventListener('keydown', this.handlerKey); + document.removeEventListener('keyup', this.handlerKey); } }; } diff --git a/src/app/views/audiocall/AudiocallStartPage.scss b/src/app/views/audiocall/AudiocallStartPage.scss index 18697e3..11b1daf 100644 --- a/src/app/views/audiocall/AudiocallStartPage.scss +++ b/src/app/views/audiocall/AudiocallStartPage.scss @@ -5,19 +5,20 @@ $color-light-m: #f6f6f6; .audiocall-page { - font-family: "Roboto"; - font-weight: 100; - text-align: center; margin: 0 auto; - color: #767676; + max-width: 1000px; width: 100%; height: 100vh; - display: flex; + display: flex; + flex-direction: column; + flex-wrap: wrap; align-content: center; justify-content: center; gap: 50px; - flex-wrap: wrap; - max-width: 1000px; + font-family: "Roboto"; + font-weight: 100; + text-align: center; + color: #767676; @media screen and (max-width: 768px) { padding: 20px; @@ -105,14 +106,23 @@ $color-light-m: #f6f6f6; font-family: 'Roboto'; font-weight: 100; font-size: 48px; - line-height: 56px; + line-height: 50px; text-transform: uppercase; color: $color-dark-s; cursor: pointer; + &:hover { font-weight: 300; transition: all 0.1s; } + + &-active { + background: url('/src/assets/img/svg/corner-left-bottom.svg') bottom left no-repeat, + url('/src/assets/img/svg/corner-right-bottom.svg') bottom right no-repeat, + url('/src/assets/img/svg/corner-right-top.svg') top right no-repeat, + url('/src/assets/img/svg/corner-left-top.svg') top left no-repeat; + } + &:active { color: $color-dark-xl; font-weight: 300; diff --git a/src/app/views/audiocall/AudiocallStartPage.ts b/src/app/views/audiocall/AudiocallStartPage.ts index 792d193..fecd581 100644 --- a/src/app/views/audiocall/AudiocallStartPage.ts +++ b/src/app/views/audiocall/AudiocallStartPage.ts @@ -1,12 +1,8 @@ import { dch } from '../dch'; import Renderable from '../Renderable'; import { GlobalConstants } from '../../../GlobalConstants'; -import { AudiocallGameField } from './AudiocallGameField'; -import { WordService } from '../../services/WordService'; -import { IWord } from '../../interfaces/IWord'; -import { IAudiocallAnswer } from '../../interfaces/IAudiocallAnswer'; -import { IAudiocallQuestionArray } from '../../interfaces/IAudiocallQuestionArray'; -import { IAudiocallQuestion } from '../../interfaces/IAudiocallQuestion'; +import { IGameAnswer } from '../../interfaces/IGameAnswer'; + import './AudiocallStartPage.scss'; export class AudiocallStartPage extends Renderable { @@ -14,15 +10,11 @@ export class AudiocallStartPage extends Renderable { startButton: HTMLElement; - group: number | undefined; - - page: number | undefined; + group: number; - arrayAnswers: IAudiocallAnswer[]; + page: number; - answersCount: number; - - pages: number; + arrayAnswers: IGameAnswer[]; title: HTMLElement; @@ -36,13 +28,14 @@ export class AudiocallStartPage extends Renderable { levelBtnContainer: HTMLElement; - constructor(group?: number, page?: number) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onStartGame = (group: number, page: number) => {}; + + constructor(group: number, page: number) { super(); this.group = group; this.page = page; this.arrayAnswers = []; - this.answersCount = 4; - this.pages = GlobalConstants.NUMBER_OF_PAGES; this.title = dch('h2', ['audiocall-page--title'], 'audio decoding'); this.titleContainer = dch('div', ['audiocall-page--title-container'], '', this.title); this.gameDescription = dch( @@ -53,91 +46,49 @@ export class AudiocallStartPage extends Renderable { ); this.startButton = dch('button', ['audiocall-page--button'], 'Start decoding'); + this.startButton.addEventListener('click', () => { + this.onStartGame(this.group, this.page); + }); + + this.buttonsContainer = dch('div', ['button-container']); this.buttonContainerText = dch('p', ['button-container--text'], 'Choose your level of depth'); this.levelBtnContainer = dch('div', ['level-button-container']); - this.buttonsContainer = dch( - 'div', - ['button-container'], - '', - this.buttonContainerText, - this.levelBtnContainer, - this.startButton, - ); - this.mainContainer = dch( - 'div', - ['audiocall-page--main'], - '', - this.titleContainer, - this.gameDescription, - ); - this.rootNode = dch( - 'div', - ['audiocall-page'], - '', - this.mainContainer, - this.buttonsContainer, - ); - if (!this.group && !this.page) { - const countLevel = GlobalConstants.NUMBER_OF_GROUP_NO_AUTH_USER; - for (let i = 1; i <= countLevel; i++) { - const levelBtn = dch('button', ['level-button'], `${i}`); + const getRandomInt = (min: number, max: number) => { + const minAggregated = Math.ceil(min); + const maxAggregated = Math.floor(max); + return Math.floor(Math.random() * (maxAggregated - minAggregated + 1)) + minAggregated; + }; + + if (this.group < 0 && this.page < 0) { + this.group = 0; + this.page = 0; + + for (let i = 0; i < GlobalConstants.NUMBER_OF_GROUP_NO_AUTH_USER; i++) { + const levelBtn = dch('button', ['level-button'], `${i + 1}`); this.levelBtnContainer.append(levelBtn); + levelBtn.addEventListener('click', () => { - Array.from(Array(this.pages).keys()) - .map((pageCount) => WordService.getWordsByGroupAndPage((i - 1), pageCount) - .then((result) => this.createQuestionData(result.array)) - .catch((e) => console.error(e))); + this.group = i; + this.page = getRandomInt(0, GlobalConstants.NUMBER_OF_PAGES - 1); + + Array.from(this.levelBtnContainer.children).forEach((node) => node.classList.remove('level-button-active')); + + levelBtn.classList.add('level-button-active'); }); } - } else if (this.group && this.page) { - WordService.getWordsByGroupAndPage(this.group - 1, this.page - 1).then((wordData) => { - this.createQuestionData(wordData.array); - }).catch((e) => console.error(e)); + + this.buttonsContainer.append( + this.buttonContainerText, + this.levelBtnContainer, + ); } - // this.rootNode.append(new Menu().getElement()); - } - startGame(questionArrayData: IAudiocallQuestionArray) { - const gameField = new AudiocallGameField(questionArrayData); - this.rootNode.innerHTML = ''; - this.rootNode.append(gameField.getElement()); - } + this.buttonsContainer.append( + this.startButton, + ); - createAnswers = (array: IWord[], count: number, currentQuestion: IWord) => { - const shuffleArray = array.filter((item) => item.id !== currentQuestion.id); - shuffleArray.sort(() => Math.random() - 0.5); - const answersDataArray = [...shuffleArray.slice(0, count - 1)]; - const result = answersDataArray.map((item) => { - const answerData = { - wordData: item, - isCorrect: false, - } as IAudiocallAnswer; - return answerData; - }); - return result; - }; - - createQuestionData(array: IWord[]) { - const questionsArray = array.sort(() => Math.random() - 0.5).map((item) => { - this.arrayAnswers = this.createAnswers(array, this.answersCount, item); - const questionData = { - wordData: item, - isCorrect: true, - } as IAudiocallAnswer; - const question = { - wordData: item, - variants: [...this.arrayAnswers, questionData], - isCorrectVariant: true, - } as IAudiocallQuestion; - return question; - }); - const questionData = { - questions: questionsArray, - currentQuestion: 0, - } as IAudiocallQuestionArray; - this.startButton.addEventListener('click', () => { - this.startGame(questionData); - }); + this.mainContainer = dch('div', ['audiocall-page--main'], '', this.titleContainer, this.gameDescription); + this.rootNode = dch('div', ['audiocall-page'], '', this.mainContainer, this.buttonsContainer); } } diff --git a/src/app/views/audiocall/AudiocallStatisticPage.ts b/src/app/views/audiocall/AudiocallStatisticPage.ts index 185b8b1..bf2457a 100644 --- a/src/app/views/audiocall/AudiocallStatisticPage.ts +++ b/src/app/views/audiocall/AudiocallStatisticPage.ts @@ -4,6 +4,8 @@ import { IResultData } from '../../interfaces/IResultData'; import { GlobalConstants } from '../../../GlobalConstants'; import { musicPlayer } from '../../services/SingleMusicPlayer'; import './AudiocallStatisticPage.scss'; +import { TokenProvider } from '../../services/TokenProvider'; +import { UserWordService } from '../../services/UserWordService'; export class AudiocallStatisticPage extends Renderable { resultData: IResultData[]; @@ -35,6 +37,13 @@ export class AudiocallStatisticPage extends Renderable { this.correctWordsContainer = dch('div', ['result-words-container'], ''); this.inCorrectWordsContainer = dch('div', ['result-words-container'], ''); this.resultData.forEach((item) => { + const userId = TokenProvider.getUserId(); + + if (userId && !TokenProvider.checkIsExpired()) { + UserWordService.setWordStatistic(userId, item.questionData.id, item.isCorrect) + .catch((e) => console.error(e)); + } + const word = dch( 'p', ['button-word--text-item'], diff --git a/src/app/views/menu/Menu.scss b/src/app/views/menu/Menu.scss index ba13537..ca4ebca 100644 --- a/src/app/views/menu/Menu.scss +++ b/src/app/views/menu/Menu.scss @@ -164,3 +164,20 @@ $color-light-m: #f6f6f6; max-width: 1920px; margin: 0 auto; } + +@media screen and (max-width: 640px) { + .menu { + .close-button { + top: 25px; + right: 25px; + width: 25px; + height: 25px; + } + .burger-menu-button { + top: 25px; + left: 25px; + width: 25px; + height: 25px; + } + } +} \ No newline at end of file diff --git a/src/app/views/menu/Menu.ts b/src/app/views/menu/Menu.ts index d14cdf4..d2e3b1b 100644 --- a/src/app/views/menu/Menu.ts +++ b/src/app/views/menu/Menu.ts @@ -8,7 +8,7 @@ import './Menu.scss'; const menuData = [ { title: 'Their memories', - path: `${GlobalConstants.ROUTE_WORDBOOK}/1/1`, + path: GlobalConstants.ROUTE_WORDBOOK, isAuthNeeded: false, }, { @@ -66,15 +66,33 @@ export class Menu extends Renderable { this.logoContainer = dch('div', ['logo-container']); this.navigationList = dch('ul', ['nav-menu--list']); const currentPath = PathBus.getCurrentPath(); - const isUserAuth = Boolean(TokenProvider.getToken()); + const isUserAuth = Boolean(TokenProvider.getToken()) && !TokenProvider.checkIsExpired(); menuData.forEach((item) => { const link = dch('a', ['nav-menu--link'], `${item.title}`) as HTMLAnchorElement; link.href = `#${item.path}`; const list = dch('li', ['nav-menu--item'], '', link); + if (currentPath.indexOf(item.path) === 0) { list.classList.add('nav-menu--item-active'); - link.classList.add('nav-menu--link-active'); } + + if (currentPath.indexOf(GlobalConstants.ROUTE_WORDBOOK) === 0) { + const currentGroup = Number(currentPath.split('/')[2]); + const currentPage = Number(currentPath.split('/')[3]); + + const setNewHref = (path: string, group: number, page: number) => { + if (group && page) { + link.href = `#${path}/${group}/${page}`; + } + }; + + if (item.path === GlobalConstants.ROUTE_AUDIOCALL) { + setNewHref(item.path, currentGroup, currentPage); + } else if (item.path === GlobalConstants.ROUTE_SPRINT) { + setNewHref(item.path, currentGroup, currentPage); + } + } + if (item.isAuthNeeded) { if (isUserAuth) { this.navigationList.append(list); diff --git a/src/app/views/sprint/SprintGameField.ts b/src/app/views/sprint/SprintGameField.ts index 67fa744..66424f3 100644 --- a/src/app/views/sprint/SprintGameField.ts +++ b/src/app/views/sprint/SprintGameField.ts @@ -1,109 +1,105 @@ -import { IResultData } from '../../interfaces/IResultData'; -import { TokenProvider } from '../../services/TokenProvider'; -import { UserWordService } from '../../services/UserWordService'; import { dch } from '../dch'; import Renderable from '../Renderable'; +import { IResultData } from '../../interfaces/IResultData'; +import { IGameQuestionArray } from '../../interfaces/IGameQuestionArray'; +import { IGameQuestion } from '../../interfaces/IGameQuestion'; import { SprintQuestion } from './SprintQuestion'; -import { ISprintQuestionData } from '../../interfaces/ISprintQuestionData'; -import { SprintStatisticPage } from './SprintStatisticPage'; import { GlobalConstants } from '../../../GlobalConstants'; export class SprintGameField extends Renderable { - page: number | undefined; + private questionsArray: IGameQuestionArray | null = null; - data: ISprintQuestionData[]; + private result: Array = []; - result: IResultData[]; + private answerChain = 0; - answerChain: number; + private maxAnswerChain = 0; - maxAnswerChain: number; + private title: HTMLElement; - userId: string | null; + private timerText: HTMLElement; - startQuestion: number; + private currentTime = 0; - gameTime: number; + private gameTimer = 0; - currentTime: number; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onFinish = (result: IResultData[], answerChain: number) => {}; - timerContainer: HTMLElement; - - constructor(questionArrayData: ISprintQuestionData[]) { + constructor() { super(); - this.data = questionArrayData; + + this.title = dch('h2', ['sprint-page--title'], 'MEANING RESOLVING'); + const subTitle = dch('h3', ['sprint-page--sub-title'], 'In progress'); + this.timerText = dch('div', ['sprint-page--timer-text'], String(GlobalConstants.GAME_TIME)); + this.rootNode = dch('div', ['gamefield-container'], '', this.title, subTitle, this.timerText); + } + + setQuestionsArray(questionsArray: IGameQuestionArray) { + this.questionsArray = questionsArray; this.answerChain = 0; this.maxAnswerChain = 1; this.result = []; - this.userId = TokenProvider.getUserId(); - this.startQuestion = 0; - this.gameTime = GlobalConstants.GAME_TIME; - this.currentTime = this.gameTime; - this.timerContainer = dch('div', []); - this.rootNode = dch('div', ['gamefield-container'], ''); - this.showTime(); - this.gameCycle(this.data, this.startQuestion); } - gameCycle(questionArray: ISprintQuestionData[], index: number) { - this.rootNode.append(this.timerContainer); - if (index >= questionArray.length) { - this.currentTime = 0; - this.onFinish(this.result, this.maxAnswerChain); + startGame() { + if (!this.questionsArray) { return; } - const cardQuestion = new SprintQuestion(questionArray[index]); - this.rootNode.append(cardQuestion.getElement()); - cardQuestion.onAnswer = (questionData, answer) => { - if (questionData.isCorrect === answer) { - if (this.userId && !TokenProvider.checkIsExpired()) { - UserWordService.setWordStatistic(this.userId, questionData.word.id, questionData.isCorrect) - .catch((e) => console.error(e)); - } - this.answerChain += 1; - if (this.answerChain > this.maxAnswerChain) { - this.maxAnswerChain = this.answerChain; - } - this.result.push({ - questionData: questionData.word, - isCorrect: true, - } as IResultData); - } else if (questionData.isCorrect !== answer) { - if (this.userId && !TokenProvider.checkIsExpired()) { - UserWordService.setWordStatistic(this.userId, questionData.word.id, questionData.isCorrect) - .catch((e) => console.error(e)); - } + + this.answerChain = 0; + this.maxAnswerChain = 1; + this.currentTime = GlobalConstants.GAME_TIME; + this.result = []; + + this.startTimer(); + + this.renderCard(this.questionsArray.questions[this.questionsArray.currentQuestion]); + } + + nextQuestion() { + if (!this.questionsArray) { + return; + } + + if (this.questionsArray.questions.length > this.questionsArray.currentQuestion + 1) { + this.questionsArray.currentQuestion += 1; + this.renderCard(this.questionsArray.questions[this.questionsArray.currentQuestion]); + } else { + this.onFinish(this.result, this.maxAnswerChain); + clearInterval(this.gameTimer); + } + } + + renderCard(question: IGameQuestion) { + const cardQuestion = new SprintQuestion(question); + cardQuestion.onAnswer = (questionData, isCorrect) => { + this.result.push({ questionData, isCorrect }); + cardQuestion.destroy(); + this.nextQuestion(); + + if (!isCorrect) { this.answerChain = 0; - this.result.push({ - questionData: questionData.word, - isCorrect: false, - }); + return; + } + + this.answerChain += 1; + if (this.answerChain > this.maxAnswerChain) { + this.maxAnswerChain = this.answerChain; } - cardQuestion.destroy(); - this.gameCycle(questionArray, index + 1); }; + + this.rootNode.append(cardQuestion.getElement()); } - showTime() { - const changeTime = setInterval(() => { + startTimer() { + this.gameTimer = window.setInterval(() => { this.currentTime--; - this.timerContainer.innerText = `${this.currentTime}`; + this.timerText.innerText = `${this.currentTime}`; if (this.currentTime === 0) { - clearInterval(changeTime); + clearInterval(this.gameTimer); this.onFinish(this.result, this.maxAnswerChain); } }, 1000); } - - onFinish = (result: IResultData[], answerChain: number) => { - this.rootNode.innerHTML = ''; - if (!this.result.length) { - this.result.push({ - questionData: this.data[0].word, - isCorrect: false, - }); - } - const resultPage = new SprintStatisticPage(result, answerChain); - this.rootNode.append(resultPage.getElement()); - }; } diff --git a/src/app/views/sprint/SprintPage.ts b/src/app/views/sprint/SprintPage.ts new file mode 100644 index 0000000..ebe1b5d --- /dev/null +++ b/src/app/views/sprint/SprintPage.ts @@ -0,0 +1,62 @@ +import { IWord } from '../../interfaces/IWord'; +import { WordService } from '../../services/WordService'; +import Renderable from '../Renderable'; +import { IResultData } from '../../interfaces/IResultData'; +import { SprintStartPage } from './SprintStartPage'; +import { SprintGameField } from './SprintGameField'; +import { IGameQuestionArray } from '../../interfaces/IGameQuestionArray'; +import { SprintStatisticPage } from './SprintStatisticPage'; +import { IGameAnswer } from '../../interfaces/IGameAnswer'; +import { IGameQuestion } from '../../interfaces/IGameQuestion'; + +export class SprintPage extends Renderable { + constructor(group: number, page: number) { + super(); + const startPage = new SprintStartPage(group - 1, page - 1); + const gameField = new SprintGameField(); + startPage.onStartGame = (selectedGroup: number, selectedPage: number) => { + WordService.getWordsByGroupAndPage(selectedGroup, selectedPage) + .then((wordData) => { + const questionsData = { + questions: this.createQuestions(wordData.array), + currentQuestion: 0, + } as IGameQuestionArray; + + this.rootNode.innerHTML = ''; + gameField.setQuestionsArray(questionsData); + gameField.startGame(); + this.rootNode.append(gameField.getElement()); + }) + .catch((e) => console.error(e)); + }; + + gameField.onFinish = (result: IResultData[], answerChain: number) => { + this.rootNode.innerHTML = ''; + const resultPage = new SprintStatisticPage(result, answerChain); + this.rootNode.append(resultPage.getElement()); + }; + + this.rootNode = startPage.getElement(); + } + + createVariantForAnswer = (wordsArray: Array, currentWord: IWord) => { + const onlyDifferentWords = wordsArray.filter((word) => word.id !== currentWord.id); + const shuffledAndCutArray = [...onlyDifferentWords].sort(() => Math.random() - 0.5).slice(0, 1); + const variant = [currentWord, ...shuffledAndCutArray].sort(() => Math.random() - 0.5).slice(0, 1).map((word) => ({ + wordData: word, + isCorrect: word.id === currentWord.id, + } as IGameAnswer)); + return variant; + }; + + createQuestions = (wordsArray: Array) => { + const result = wordsArray.map( + (word) => ({ + wordData: word, + variants: this.createVariantForAnswer(wordsArray, word), + } as IGameQuestion), + ); + + return result.sort(() => Math.random() - 0.5); + }; +} diff --git a/src/app/views/sprint/SprintQuestion.ts b/src/app/views/sprint/SprintQuestion.ts index 2f80b44..44b189f 100644 --- a/src/app/views/sprint/SprintQuestion.ts +++ b/src/app/views/sprint/SprintQuestion.ts @@ -1,55 +1,57 @@ import { dch } from '../dch'; import Renderable from '../Renderable'; -import { ISprintQuestionData } from '../../interfaces/ISprintQuestionData'; +import { IWord } from '../../interfaces/IWord'; +import { IGameQuestion } from '../../interfaces/IGameQuestion'; export class SprintQuestion extends Renderable { - questionData: ISprintQuestionData; + private questionData: IGameQuestion; - question: HTMLElement; + private rightBtn: HTMLElement; - translate: HTMLElement; + private wrongBtn: HTMLElement; - questionContainer: HTMLElement; + constructor(questionData: IGameQuestion) { + super(); - rightBtn: HTMLElement; + this.questionData = questionData; + const questionText = dch('h3', ['game-question--question-text'], 'Is correct match?'); + const assumptionContainer = dch('div', ['assumption']); - wrongBtn: HTMLElement; + const wordOriginal = dch('div', ['assumption--original'], this.questionData.wordData.word); + const wordSeparator = dch('div', ['assumption--separator']); + const wordQuestion = dch('div', ['assumption--question'], this.questionData.variants[0].wordData.wordTranslate); - btnContainer: HTMLElement; + assumptionContainer.append(wordOriginal, wordSeparator, wordQuestion); - constructor(questionData: ISprintQuestionData) { - super(); - this.questionData = questionData; - this.question = dch('div', ['question'], `${questionData.word.word}`); - this.translate = dch('div', ['translate'], `${questionData.translate}`); - this.questionContainer = dch('div', ['question-container'], '', this.question, this.translate); this.rightBtn = dch('button', ['question-container_btn'], 'true'); this.rightBtn.onclick = () => { - this.onAnswer(this.questionData, true); + this.onAnswer(this.questionData.wordData, this.questionData.variants[0].isCorrect === true); }; this.wrongBtn = dch('button', ['question-container_btn'], 'false'); this.wrongBtn.onclick = () => { - this.onAnswer(this.questionData, false); + this.onAnswer(this.questionData.wordData, this.questionData.variants[0].isCorrect === false); }; - this.btnContainer = dch('div', ['button-container'], '', this.wrongBtn, this.rightBtn); - this.rootNode = dch('div', ['question-section'], '', this.questionContainer, this.btnContainer); - document.addEventListener('keydown', this.handlerKey); + const btnContainer = dch('div', ['game-btn-container'], '', this.rightBtn, this.wrongBtn); + + this.rootNode = dch('div', ['game-question'], '', assumptionContainer, questionText, btnContainer); + document.addEventListener('keyup', this.handlerKey); } destroy() { + document.removeEventListener('keyup', this.handlerKey); this.rootNode.remove(); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - onAnswer = (questionData: ISprintQuestionData, answer: boolean) => {}; + onAnswer = (questionData: IWord, isCorrect: boolean) => {}; handlerKey = (event: KeyboardEvent) => { - if (event.code === 'ArrowLeft') { - this.wrongBtn.click(); - document.removeEventListener('keydown', this.handlerKey); - } else if (event.code === 'ArrowRight') { + if (event.code === 'Digit1') { this.rightBtn.click(); - document.removeEventListener('keydown', this.handlerKey); + document.removeEventListener('keyup', this.handlerKey); + } else if (event.code === 'Digit2') { + this.wrongBtn.click(); + document.removeEventListener('keyup', this.handlerKey); } }; } diff --git a/src/app/views/sprint/SprintStartPage.ts b/src/app/views/sprint/SprintStartPage.ts index ff35eab..ed713ee 100644 --- a/src/app/views/sprint/SprintStartPage.ts +++ b/src/app/views/sprint/SprintStartPage.ts @@ -1,84 +1,92 @@ import { dch } from '../dch'; import Renderable from '../Renderable'; import { GlobalConstants } from '../../../GlobalConstants'; -import { WordService } from '../../services/WordService'; -import { IWord } from '../../interfaces/IWord'; -import { SprintGameField } from './SprintGameField'; -import { ISprintQuestionData } from '../../interfaces/ISprintQuestionData'; +import { IGameAnswer } from '../../interfaces/IGameAnswer'; export class SprintStartPage extends Renderable { - group: number | undefined; + gameDescription: HTMLElement; - page: number | undefined; + startButton: HTMLElement; - gameDescription: HTMLElement; + group: number; - pages: number; + page: number; - startButton: HTMLElement; + arrayAnswers: IGameAnswer[]; + + title: HTMLElement; + + mainContainer: HTMLElement; + + buttonsContainer: HTMLElement; + + titleContainer: HTMLElement; - constructor(group?: number, page?: number) { + buttonContainerText: HTMLElement; + + levelBtnContainer: HTMLElement; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onStartGame = (group: number, page: number) => {}; + + constructor(group: number, page: number) { super(); this.group = group; this.page = page; - this.pages = GlobalConstants.NUMBER_OF_PAGES; - this.startButton = dch('button', [], 'start'); - this.gameDescription = dch('div', [], 'About game'); - this.rootNode = dch('div', [], '', this.gameDescription, this.startButton); - - if (!this.group && !this.page) { - const countLevel = GlobalConstants.NUMBER_OF_GROUP_NO_AUTH_USER; - for (let i = 1; i <= countLevel; i++) { - const levelBtn = dch('button', ['level_button'], `${i}`); - this.rootNode.append(levelBtn); + this.arrayAnswers = []; + this.title = dch('h2', ['sprint-page--title'], 'MEANING RESOLVING'); + this.titleContainer = dch('div', ['sprint-page--title-container'], '', this.title); + this.gameDescription = dch( + 'div', + ['sprint-page--text'], + `Your time is limited. + Make decisions - is it correct match of word meaning.`, + ); + + this.startButton = dch('button', ['audiocall-page--button'], 'START RESOLVING'); + this.startButton.addEventListener('click', () => { + this.onStartGame(this.group, this.page); + }); + + this.buttonsContainer = dch('div', ['button-container']); + this.buttonContainerText = dch('p', ['button-container--text'], 'Choose your level of depth'); + this.levelBtnContainer = dch('div', ['level-button-container']); + + const getRandomInt = (min: number, max: number) => { + const minAggregated = Math.ceil(min); + const maxAggregated = Math.floor(max); + return Math.floor(Math.random() * (maxAggregated - minAggregated + 1)) + minAggregated; + }; + + if (this.group < 0 && this.page < 0) { + this.group = 0; + this.page = 0; + + for (let i = 0; i < GlobalConstants.NUMBER_OF_GROUP_NO_AUTH_USER; i++) { + const levelBtn = dch('button', ['level-button'], `${i + 1}`); + this.levelBtnContainer.append(levelBtn); + levelBtn.addEventListener('click', () => { - Array.from(Array(this.pages).keys()) - .map((pageCount) => WordService.getWordsByGroupAndPage((i - 1), pageCount) - .then((result) => { - this.createQuestionData(result.array); - }) - .catch((e) => console.error(e))); + this.group = i; + this.page = getRandomInt(0, GlobalConstants.NUMBER_OF_PAGES - 1); + + Array.from(this.levelBtnContainer.children).forEach((node) => node.classList.remove('level-button-active')); + + levelBtn.classList.add('level-button-active'); }); } - } else if (this.group && this.page) { - WordService.getWordsByGroupAndPage(this.group - 1, this.page - 1).then((wordData) => { - this.createQuestionData(wordData.array); - }).catch((e) => console.error(e)); - } - } - createAnswer = (array: IWord[], index: number, answerVariant: boolean) => { - if (answerVariant) { - return array[index].wordTranslate; + this.buttonsContainer.append( + this.buttonContainerText, + this.levelBtnContainer, + ); } - const randomInteger: number = Math.floor(Math.random() * array.length); - const indexTranslate = (randomInteger === index) - ? Math.floor(Math.random() * array.length) : randomInteger; - - return array[indexTranslate].wordTranslate; - }; - - createQuestionData = (array: IWord[]) => { - const shuffledArray = array.sort(() => Math.random() - 0.5); - const questionsArray = shuffledArray.map((item, index) => { - const answerVariant = !!Math.round(Math.random()); - - const translate = this.createAnswer(shuffledArray, index, answerVariant); - return { - word: item, - translate, - isCorrect: answerVariant, - } as ISprintQuestionData; - }); - this.startButton.addEventListener('click', () => { - this.startGame(questionsArray); - }); - }; + this.buttonsContainer.append( + this.startButton, + ); - startGame(questionArrayData: ISprintQuestionData[]) { - const gameField = new SprintGameField(questionArrayData); - this.rootNode.innerHTML = ''; - this.rootNode.append(gameField.getElement()); + this.mainContainer = dch('div', ['audiocall-page--main'], '', this.titleContainer, this.gameDescription); + this.rootNode = dch('div', ['audiocall-page'], '', this.mainContainer, this.buttonsContainer); } } diff --git a/src/app/views/sprint/SprintStatisticPage.ts b/src/app/views/sprint/SprintStatisticPage.ts index 88b6ac3..51b8f96 100644 --- a/src/app/views/sprint/SprintStatisticPage.ts +++ b/src/app/views/sprint/SprintStatisticPage.ts @@ -1,9 +1,10 @@ import Renderable from '../Renderable'; import { dch } from '../dch'; import { IResultData } from '../../interfaces/IResultData'; -import { PathBus } from '../../services/PathBus'; import { GlobalConstants } from '../../../GlobalConstants'; import { musicPlayer } from '../../services/SingleMusicPlayer'; +import { TokenProvider } from '../../services/TokenProvider'; +import { UserWordService } from '../../services/UserWordService'; export class SprintStatisticPage extends Renderable { resultData: IResultData[]; @@ -14,29 +15,38 @@ export class SprintStatisticPage extends Renderable { statisticContainer: HTMLElement; - playAgainButton: HTMLElement; + title: HTMLElement; + + score: HTMLElement; constructor(resultData: IResultData[], answerChain: number) { super(); this.resultData = resultData; - this.statisticContainer = dch('p', [], `The longest answer Chain - ${answerChain}`); + this.title = dch('h3', ['word-container--title'], 'results'); + const correctAnswers = this.resultData.filter((item) => item.isCorrect); + this.score = dch('div', ['word-container--subtitle'], `${correctAnswers.length}/${this.resultData.length}`); + this.statisticContainer = dch('p', ['result-text'], `The longest answer Chain - ${answerChain}`); this.resultContainer = dch('div', ['result-container']); - this.playAgainButton = dch('button', ['result-container_btn'], 'play again'); - this.playAgainButton.onclick = () => { - PathBus.setCurrentPath(PathBus.getCurrentPath()); - }; - this.rootNode = dch('div', ['result-page'], '', this.resultContainer, this.playAgainButton); + this.rootNode = dch('div', ['result-page'], '', this.title, this.score, this.resultContainer); + // this.rootNode.append(new Menu().getElement()); this.date = this.formatDate(new Date()); resultData.forEach((item) => { + const userId = TokenProvider.getUserId(); + + if (userId && !TokenProvider.checkIsExpired()) { + UserWordService.setWordStatistic(userId, item.questionData.id, item.isCorrect) + .catch((e) => console.error(e)); + } + const audioWordData = `${GlobalConstants.DEFAULT_API_URL}/${item.questionData.audio}`; - const playAudioBtn = dch('button', [], 'play'); + const playAudioBtn = dch('button', ['result-audio-btn']); playAudioBtn.onclick = () => { this.playAudio(audioWordData); }; - const word = dch('p', ['word-container_item'], `${item.questionData.word}`); + const word = dch('p', ['word-container--item'], `${item.questionData.word}`); const wordTranslate = dch('p', ['word-container_item'], `${item.questionData.wordTranslate}`); const wordResult = dch('p', ['word-container_item'], `${item.isCorrect ? 'true' : 'false'}`); - const wordContainer = dch('div', ['word-container']); + const wordContainer = dch('div', ['result-word-container']); wordContainer.append(playAudioBtn, word, wordTranslate, wordResult); this.resultContainer.append(wordContainer, this.statisticContainer); }); diff --git a/src/app/views/wordBook/Wordbook.ts b/src/app/views/wordBook/Wordbook.ts index 18bb7cf..7f57ceb 100644 --- a/src/app/views/wordBook/Wordbook.ts +++ b/src/app/views/wordBook/Wordbook.ts @@ -64,7 +64,6 @@ export class Wordbook extends Renderable { } changeLevel(btn: string) { - console.log('GG'); if (btn === 'prev') { if (this.data.currentGroup > 0) this.data.currentGroup--; PathBus.setCurrentPath( diff --git a/src/app/views/wordBook/cardsContainer/card/Card.scss b/src/app/views/wordBook/cardsContainer/card/Card.scss index 4ff7efb..56d25cb 100644 --- a/src/app/views/wordBook/cardsContainer/card/Card.scss +++ b/src/app/views/wordBook/cardsContainer/card/Card.scss @@ -25,6 +25,10 @@ filter: saturate(0) brightness(1.4); &_started { + filter: saturate(0.2) brightness(1.4); + } + + &_learned { filter: saturate(0.5) brightness(1.4); } } diff --git a/src/app/views/wordBook/cardsContainer/card/Card.ts b/src/app/views/wordBook/cardsContainer/card/Card.ts index 549454f..b8e1bec 100644 --- a/src/app/views/wordBook/cardsContainer/card/Card.ts +++ b/src/app/views/wordBook/cardsContainer/card/Card.ts @@ -8,25 +8,27 @@ import { UserWordService } from '../../../../services/UserWordService'; import './Card.scss'; export class Card extends Renderable { - data: IWordAdanced; + private data: IWordAdanced; - buttonSetDifficultyState: HTMLElement; + private buttonSetDifficultyState: HTMLElement; - buttonSetLearnedState: HTMLElement; + private buttonSetLearnedState: HTMLElement; private isWordDifficult = false; private isWordLearned = false; + private image = new Image(); + constructor(data: IWordAdanced) { super(); this.data = data; const userId = TokenProvider.getUserId(); this.rootNode = dch('div', ['word-card']); - const image = dch('img', ['word-card__image']); - image.setAttribute('src', `${GlobalConstants.DEFAULT_API_URL}/${this.data.word.image}`); - image.setAttribute('alt', this.data.word.word); + this.image = dch('img', ['word-card__image']) as HTMLImageElement; + this.image.setAttribute('src', `${GlobalConstants.DEFAULT_API_URL}/${this.data.word.image}`); + this.image.setAttribute('alt', this.data.word.word); const imageContainer = dch('div', ['word-card__image-container']); const buttonShowEngDescription = dch('button', ['word-card__button-vertical', 'word-card__button-vertical_show']); @@ -102,7 +104,7 @@ export class Card extends Renderable { ruDescriptionContainer.append(buttonHideRuDescription, textWordRu, textWordRuMeaning, textWordRuExample); textContainer.append(buttonShowEngDescription, textWordEnglish, textWordTranscription); - imageContainer.append(image); + imageContainer.append(this.image); this.rootNode.append( imageContainer, textContainer, @@ -117,26 +119,36 @@ export class Card extends Renderable { this.buttonSetLearnedState = dch('button', ['word-card__button-learned']); this.buttonSetLearnedState.onclick = () => this.buttonToggleLearnedHandler(); - if (userId) { - if (this.data.userData) { - if (this.data.userData.difficulty && this.data.userData.difficulty !== 'normal') { - this.isWordDifficult = true; - this.buttonSetDifficultyState.classList.add('word-card__button-difficulty_true'); - } - - if (this.data.userData.optional && this.data.userData.optional.isLearned) { - this.isWordLearned = true; - this.buttonSetLearnedState.classList.add('word-card__button-learned_true'); - - const resultText = `${this.data.userData.optional.successCounter}/ - ${this.data.userData.optional.successCounter + this.data.userData.optional.successCounter}`; - - const gamesResultContainer = dch('div', ['word-card__games-result'], resultText); - imageContainer.append(gamesResultContainer); - image.classList.add('word-card__image_started'); - } - } - imageContainer.append(this.buttonSetDifficultyState, this.buttonSetLearnedState); + if (!userId) { + return; + } + + imageContainer.append(this.buttonSetDifficultyState, this.buttonSetLearnedState); + + if (!this.data.userData) { + return; + } + + if (this.data.userData.difficulty && this.data.userData.difficulty !== 'normal') { + this.isWordDifficult = true; + this.buttonSetDifficultyState.classList.add('word-card__button-difficulty_true'); + } + + if (!this.data.userData.optional) { + return; + } + + const resultText = `${this.data.userData.optional.successCounter}/ + ${this.data.userData.optional.failCounter + this.data.userData.optional.successCounter}`; + + const gamesResultContainer = dch('div', ['word-card__games-result'], resultText); + imageContainer.append(gamesResultContainer); + this.image.classList.add('word-card__image_started'); + + if (this.data.userData.optional.isLearned) { + this.image.classList.add('word-card__image_learned'); + this.isWordLearned = true; + this.buttonSetLearnedState.classList.add('word-card__button-learned_true'); } } @@ -176,6 +188,7 @@ export class Card extends Renderable { .then((userWord) => { if (userWord) { this.isWordLearned = true; + this.image.classList.add('word-card__image_learned'); this.buttonSetLearnedState.classList.add('word-card__button-learned_true'); } }) @@ -185,6 +198,7 @@ export class Card extends Renderable { .then((userWord) => { if (userWord) { this.isWordLearned = false; + this.image.classList.remove('word-card__image_learned'); this.buttonSetLearnedState.classList.remove('word-card__button-learned_true'); } })