From fc2b0ff2c23e2237015c3cfbcb17ae0024c0c9f7 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Tue, 22 Feb 2022 16:37:27 +0400 Subject: [PATCH 01/24] fix: temporary change main route --- src/app/App.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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()); } From 4ae14dcd8a77c1b7999bd89aea051a84f313460a Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Tue, 22 Feb 2022 16:37:50 +0400 Subject: [PATCH 02/24] fix: change even type to keyup --- src/app/views/audiocall/AudiocallQuestion.ts | 10 +++++----- src/app/views/sprint/SprintQuestion.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/views/audiocall/AudiocallQuestion.ts b/src/app/views/audiocall/AudiocallQuestion.ts index 9274d0a..ca9dbe2 100644 --- a/src/app/views/audiocall/AudiocallQuestion.ts +++ b/src/app/views/audiocall/AudiocallQuestion.ts @@ -37,7 +37,7 @@ 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) => { @@ -56,16 +56,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/sprint/SprintQuestion.ts b/src/app/views/sprint/SprintQuestion.ts index 2f80b44..765fee6 100644 --- a/src/app/views/sprint/SprintQuestion.ts +++ b/src/app/views/sprint/SprintQuestion.ts @@ -33,7 +33,7 @@ export class SprintQuestion extends Renderable { }; 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); + document.addEventListener('keyup', this.handlerKey); } destroy() { @@ -46,7 +46,7 @@ export class SprintQuestion extends Renderable { handlerKey = (event: KeyboardEvent) => { if (event.code === 'ArrowLeft') { this.wrongBtn.click(); - document.removeEventListener('keydown', this.handlerKey); + document.removeEventListener('keyup', this.handlerKey); } else if (event.code === 'ArrowRight') { this.rightBtn.click(); document.removeEventListener('keydown', this.handlerKey); From f9c2e81dd212526f6f13f282a1a32dafeb568487 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Tue, 22 Feb 2022 16:45:00 +0400 Subject: [PATCH 03/24] feat: change keys to digits --- src/app/views/sprint/SprintQuestion.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/views/sprint/SprintQuestion.ts b/src/app/views/sprint/SprintQuestion.ts index 765fee6..3b94450 100644 --- a/src/app/views/sprint/SprintQuestion.ts +++ b/src/app/views/sprint/SprintQuestion.ts @@ -44,10 +44,10 @@ export class SprintQuestion extends Renderable { onAnswer = (questionData: ISprintQuestionData, answer: boolean) => {}; handlerKey = (event: KeyboardEvent) => { - if (event.code === 'ArrowLeft') { + if (event.code === 'Digit1') { this.wrongBtn.click(); document.removeEventListener('keyup', this.handlerKey); - } else if (event.code === 'ArrowRight') { + } else if (event.code === 'Digit2') { this.rightBtn.click(); document.removeEventListener('keydown', this.handlerKey); } From a4a602eb1277fa5c01419c4fc82be6bb61272f01 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Tue, 22 Feb 2022 21:39:16 +0400 Subject: [PATCH 04/24] fix: setWordLearnedState --- src/app/services/UserWordService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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; From b30d3f98e18179ed5a7f20b91327187f312e7769 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Tue, 22 Feb 2022 21:39:54 +0400 Subject: [PATCH 05/24] refactor: simplify nested if --- .../wordBook/cardsContainer/card/Card.ts | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/app/views/wordBook/cardsContainer/card/Card.ts b/src/app/views/wordBook/cardsContainer/card/Card.ts index 549454f..53a0439 100644 --- a/src/app/views/wordBook/cardsContainer/card/Card.ts +++ b/src/app/views/wordBook/cardsContainer/card/Card.ts @@ -117,26 +117,37 @@ 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); + image.classList.add('word-card__image_started'); + + console.log(this.data.userData.optional.isLearned); + + if (this.data.userData.optional.isLearned) { + this.isWordLearned = true; + this.buttonSetLearnedState.classList.add('word-card__button-learned_true'); } } @@ -172,6 +183,7 @@ export class Card extends Renderable { return; } if (!this.isWordLearned) { + console.log('gg'); UserWordService.addWordLearnedById(userId, this.data.word.id) .then((userWord) => { if (userWord) { From bff52592e0624824c5929fbb8156342c07fb53a8 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Tue, 22 Feb 2022 21:51:15 +0400 Subject: [PATCH 06/24] feat: add different style when word learned --- .../wordBook/cardsContainer/card/Card.scss | 4 ++++ .../wordBook/cardsContainer/card/Card.ts | 22 +++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) 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 53a0439..39ed6dd 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, @@ -141,11 +143,12 @@ export class Card extends Renderable { const gamesResultContainer = dch('div', ['word-card__games-result'], resultText); imageContainer.append(gamesResultContainer); - image.classList.add('word-card__image_started'); + this.image.classList.add('word-card__image_started'); console.log(this.data.userData.optional.isLearned); 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'); } @@ -183,11 +186,11 @@ export class Card extends Renderable { return; } if (!this.isWordLearned) { - console.log('gg'); UserWordService.addWordLearnedById(userId, this.data.word.id) .then((userWord) => { if (userWord) { this.isWordLearned = true; + this.image.classList.add('word-card__image_learned'); this.buttonSetLearnedState.classList.add('word-card__button-learned_true'); } }) @@ -197,6 +200,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'); } }) From faf711573ad223da0bd3872f9b6c9685379fdc7d Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Tue, 22 Feb 2022 22:07:09 +0400 Subject: [PATCH 07/24] fix: remove console.log --- src/app/views/wordBook/Wordbook.ts | 1 - src/app/views/wordBook/cardsContainer/card/Card.ts | 2 -- 2 files changed, 3 deletions(-) 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.ts b/src/app/views/wordBook/cardsContainer/card/Card.ts index 39ed6dd..b8e1bec 100644 --- a/src/app/views/wordBook/cardsContainer/card/Card.ts +++ b/src/app/views/wordBook/cardsContainer/card/Card.ts @@ -145,8 +145,6 @@ export class Card extends Renderable { imageContainer.append(gamesResultContainer); this.image.classList.add('word-card__image_started'); - console.log(this.data.userData.optional.isLearned); - if (this.data.userData.optional.isLearned) { this.image.classList.add('word-card__image_learned'); this.isWordLearned = true; From 763d68c73cdc2180794875d8fd8da1e7196bf79f Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 01:01:38 +0400 Subject: [PATCH 08/24] feat: add AudiocallPage --- src/app/views/audiocall/AudiocallPage.ts | 71 ++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/app/views/audiocall/AudiocallPage.ts diff --git a/src/app/views/audiocall/AudiocallPage.ts b/src/app/views/audiocall/AudiocallPage.ts new file mode 100644 index 0000000..cfdb9d0 --- /dev/null +++ b/src/app/views/audiocall/AudiocallPage.ts @@ -0,0 +1,71 @@ +import { AudiocallStatisticPage } from './AudiocallStatisticPage'; +import { IAudiocallAnswer } from '../../interfaces/IAudiocallAnswer'; +import { IAudiocallQuestion } from '../../interfaces/IAudiocallQuestion'; +import { IAudiocallQuestionArray } from '../../interfaces/IAudiocallQuestionArray'; +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'; + +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 IAudiocallQuestionArray; + + 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 IAudiocallAnswer)); + + const correctVariant = { + wordData: currentWord, + isCorrect: true, + } as IAudiocallAnswer; + + 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 IAudiocallQuestion), + ); + + return result.sort(() => Math.random() - 0.5); + }; +} From 768c5926122387607f54b18df1a9ac1ca53ae261 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 01:02:00 +0400 Subject: [PATCH 09/24] feat: add AudiocallPage in controller --- src/app/controllers/AudiocallController.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) 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()); } } From 4905355530cf976f8c7467d12c04e44dfd4e5052 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 01:04:23 +0400 Subject: [PATCH 10/24] refactor: cleanup code --- src/app/views/audiocall/AudiocallGameField.ts | 99 ++++++++-------- src/app/views/audiocall/AudiocallQuestion.ts | 16 ++- .../views/audiocall/AudiocallStartPage.scss | 11 +- src/app/views/audiocall/AudiocallStartPage.ts | 109 +++++------------- .../views/audiocall/AudiocallStatisticPage.ts | 9 ++ 5 files changed, 109 insertions(+), 135 deletions(-) diff --git a/src/app/views/audiocall/AudiocallGameField.ts b/src/app/views/audiocall/AudiocallGameField.ts index 5292a52..3cfeaf9 100644 --- a/src/app/views/audiocall/AudiocallGameField.ts +++ b/src/app/views/audiocall/AudiocallGameField.ts @@ -5,73 +5,78 @@ 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'; export class AudiocallGameField extends Renderable { - page: number | undefined; + private questionsArray: IAudiocallQuestionArray | 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: IAudiocallQuestionArray) { + 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: IAudiocallQuestion) { + 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(resultPage.getElement()); - }; + this.rootNode.append(cardQuestion.getElement()); + } } diff --git a/src/app/views/audiocall/AudiocallQuestion.ts b/src/app/views/audiocall/AudiocallQuestion.ts index ca9dbe2..0b23387 100644 --- a/src/app/views/audiocall/AudiocallQuestion.ts +++ b/src/app/views/audiocall/AudiocallQuestion.ts @@ -7,28 +7,32 @@ import { IWord } from '../../interfaces/IWord'; import './AudiocallQuestion.scss'; export class AudiocallQuestion extends Renderable { - questionData: IAudiocallQuestion; + private questionData: IAudiocallQuestion; - audioWord: string; + private audioWord: string; - playAudioButton: HTMLElement; + private playAudioButton: HTMLElement; - title: HTMLElement; + private title: HTMLElement; - answersContainer: HTMLElement; + private answersContainer: HTMLElement; constructor(questionData: IAudiocallQuestion) { 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']); + 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.questionData.variants.sort(() => Math.random() - 0.5) .forEach((answer) => { const answerBtn = dch('button', ['game-btn'], answer.wordData.wordTranslate); @@ -40,7 +44,7 @@ export class AudiocallQuestion extends Renderable { document.addEventListener('keyup', this.handlerKey); } - playAudio = (audio: string) => { + private playAudio = (audio: string) => { musicPlayer.setPlayList([audio]); musicPlayer.play() .catch((e) => console.error(e)); diff --git a/src/app/views/audiocall/AudiocallStartPage.scss b/src/app/views/audiocall/AudiocallStartPage.scss index f7e259c..ca62dd8 100644 --- a/src/app/views/audiocall/AudiocallStartPage.scss +++ b/src/app/views/audiocall/AudiocallStartPage.scss @@ -91,15 +91,24 @@ $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: 200; color: $color-dark-xl; 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..99b0cc4 100644 --- a/src/app/views/audiocall/AudiocallStartPage.ts +++ b/src/app/views/audiocall/AudiocallStartPage.ts @@ -1,12 +1,7 @@ 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 './AudiocallStartPage.scss'; export class AudiocallStartPage extends Renderable { @@ -14,16 +9,12 @@ export class AudiocallStartPage extends Renderable { startButton: HTMLElement; - group: number | undefined; + group: number; - page: number | undefined; + page: number; arrayAnswers: IAudiocallAnswer[]; - answersCount: number; - - pages: number; - title: HTMLElement; mainContainer: HTMLElement; @@ -36,13 +27,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( @@ -63,81 +55,36 @@ export class AudiocallStartPage extends Renderable { 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, - ); + this.mainContainer = dch('div', ['audiocall-page--main'], '', this.titleContainer, this.gameDescription); + this.rootNode = dch('div', ['audiocall-page'], '', this.mainContainer, this.buttonsContainer); + + 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 && !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}`); + 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.rootNode.append(new Menu().getElement()); - } - startGame(questionArrayData: IAudiocallQuestionArray) { - const gameField = new AudiocallGameField(questionArrayData); - this.rootNode.innerHTML = ''; - this.rootNode.append(gameField.getElement()); - } - - 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.onStartGame(this.group, this.page); }); } } diff --git a/src/app/views/audiocall/AudiocallStatisticPage.ts b/src/app/views/audiocall/AudiocallStatisticPage.ts index 69aaed2..82583cb 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[]; @@ -30,6 +32,13 @@ export class AudiocallStatisticPage extends Renderable { // 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', ['result-audio-btn']); playAudioBtn.onclick = () => { From 844ed2673dec0ebc96cf50d6db0b0c271b1bee69 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 01:29:05 +0400 Subject: [PATCH 11/24] feat: add redirect if no "path" --- src/app/controllers/WordbookController.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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; From 65a7bdf18ca8207065aa0e65fee64ae33cc5057c Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 01:29:46 +0400 Subject: [PATCH 12/24] fix: hide when token expired --- src/app/views/menu/Menu.scss | 17 +++++++++++++++++ src/app/views/menu/Menu.ts | 6 +++--- 2 files changed, 20 insertions(+), 3 deletions(-) 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..baf1e27 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,14 +66,14 @@ 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 (item.isAuthNeeded) { if (isUserAuth) { From 43962dfa71d5834434f3113b33dcc172b7b0b973 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 13:55:00 +0400 Subject: [PATCH 13/24] feat: impl interface unification --- src/app/interfaces/IAudiocallQuestion.ts | 7 ------- src/app/interfaces/IAudiocallQuestionArray.ts | 6 ------ src/app/interfaces/{IAudiocallAnswer.ts => IGameAnswer.ts} | 2 +- src/app/interfaces/IGameQuestion.ts | 7 +++++++ src/app/interfaces/IGameQuestionArray.ts | 6 ++++++ src/app/interfaces/ISprintQuestionData.ts | 7 ------- 6 files changed, 14 insertions(+), 21 deletions(-) delete mode 100644 src/app/interfaces/IAudiocallQuestion.ts delete mode 100644 src/app/interfaces/IAudiocallQuestionArray.ts rename src/app/interfaces/{IAudiocallAnswer.ts => IGameAnswer.ts} (68%) create mode 100644 src/app/interfaces/IGameQuestion.ts create mode 100644 src/app/interfaces/IGameQuestionArray.ts delete mode 100644 src/app/interfaces/ISprintQuestionData.ts 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, -} From c8536ead99d8000065223d30fe929c3167d221a3 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 13:55:46 +0400 Subject: [PATCH 14/24] feat: change interfaces --- src/app/views/audiocall/AudiocallGameField.ts | 13 +++---- src/app/views/audiocall/AudiocallPage.ts | 15 ++++---- src/app/views/audiocall/AudiocallQuestion.ts | 7 ++-- src/app/views/audiocall/AudiocallStartPage.ts | 34 +++++++++++-------- 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/app/views/audiocall/AudiocallGameField.ts b/src/app/views/audiocall/AudiocallGameField.ts index 3cfeaf9..c197b5a 100644 --- a/src/app/views/audiocall/AudiocallGameField.ts +++ b/src/app/views/audiocall/AudiocallGameField.ts @@ -1,13 +1,14 @@ 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 { IGameQuestionArray } from '../../interfaces/IGameQuestionArray'; +import { IGameQuestion } from '../../interfaces/IGameQuestion'; + +import './AudiocallGameField.scss'; export class AudiocallGameField extends Renderable { - private questionsArray: IAudiocallQuestionArray | null = null; + private questionsArray: IGameQuestionArray | null = null; private result: Array = []; @@ -27,7 +28,7 @@ export class AudiocallGameField extends Renderable { this.rootNode = dch('div', ['gamefield-container'], '', this.title); } - setQuestionsArray(questionsArray: IAudiocallQuestionArray) { + setQuestionsArray(questionsArray: IGameQuestionArray) { this.questionsArray = questionsArray; this.answerChain = 0; this.maxAnswerChain = 1; @@ -59,7 +60,7 @@ export class AudiocallGameField extends Renderable { } } - renderCard(question: IAudiocallQuestion) { + renderCard(question: IGameQuestion) { const cardQuestion = new AudiocallQuestion(question); cardQuestion.onAnswer = (questionData, isCorrect) => { this.result.push({ questionData, isCorrect }); diff --git a/src/app/views/audiocall/AudiocallPage.ts b/src/app/views/audiocall/AudiocallPage.ts index cfdb9d0..830a2b6 100644 --- a/src/app/views/audiocall/AudiocallPage.ts +++ b/src/app/views/audiocall/AudiocallPage.ts @@ -1,13 +1,14 @@ import { AudiocallStatisticPage } from './AudiocallStatisticPage'; -import { IAudiocallAnswer } from '../../interfaces/IAudiocallAnswer'; -import { IAudiocallQuestion } from '../../interfaces/IAudiocallQuestion'; -import { IAudiocallQuestionArray } from '../../interfaces/IAudiocallQuestionArray'; + 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) { @@ -21,7 +22,7 @@ export class AudiocallPage extends Renderable { const questionsData = { questions: this.createQuestions(wordData.array), currentQuestion: 0, - } as IAudiocallQuestionArray; + } as IGameQuestionArray; this.rootNode.innerHTML = ''; gameField.setQuestionsArray(questionsData); @@ -48,12 +49,12 @@ export class AudiocallPage extends Renderable { const incorrectVariants = shuffledAndCutArray.map((word) => ({ wordData: word, isCorrect: false, - } as IAudiocallAnswer)); + } as IGameAnswer)); const correctVariant = { wordData: currentWord, isCorrect: true, - } as IAudiocallAnswer; + } as IGameAnswer; return incorrectVariants.concat(correctVariant).sort(() => Math.random() - 0.5); }; @@ -63,7 +64,7 @@ export class AudiocallPage extends Renderable { (word) => ({ wordData: word, variants: this.createVariantsForAnswer(wordsArray, 4, word), - } as IAudiocallQuestion), + } as IGameQuestion), ); return result.sort(() => Math.random() - 0.5); diff --git a/src/app/views/audiocall/AudiocallQuestion.ts b/src/app/views/audiocall/AudiocallQuestion.ts index 0b23387..344276f 100644 --- a/src/app/views/audiocall/AudiocallQuestion.ts +++ b/src/app/views/audiocall/AudiocallQuestion.ts @@ -1,13 +1,13 @@ 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 { - private questionData: IAudiocallQuestion; + private questionData: IGameQuestion; private audioWord: string; @@ -17,7 +17,7 @@ export class AudiocallQuestion extends Renderable { private answersContainer: HTMLElement; - constructor(questionData: IAudiocallQuestion) { + constructor(questionData: IGameQuestion) { super(); this.questionData = questionData; @@ -51,6 +51,7 @@ export class AudiocallQuestion extends Renderable { }; destroy() { + document.removeEventListener('keyup', this.handlerKey); this.rootNode.remove(); } diff --git a/src/app/views/audiocall/AudiocallStartPage.ts b/src/app/views/audiocall/AudiocallStartPage.ts index 99b0cc4..fecd581 100644 --- a/src/app/views/audiocall/AudiocallStartPage.ts +++ b/src/app/views/audiocall/AudiocallStartPage.ts @@ -1,7 +1,8 @@ import { dch } from '../dch'; import Renderable from '../Renderable'; import { GlobalConstants } from '../../../GlobalConstants'; -import { IAudiocallAnswer } from '../../interfaces/IAudiocallAnswer'; +import { IGameAnswer } from '../../interfaces/IGameAnswer'; + import './AudiocallStartPage.scss'; export class AudiocallStartPage extends Renderable { @@ -13,7 +14,7 @@ export class AudiocallStartPage extends Renderable { page: number; - arrayAnswers: IAudiocallAnswer[]; + arrayAnswers: IGameAnswer[]; title: HTMLElement; @@ -45,18 +46,13 @@ 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); const getRandomInt = (min: number, max: number) => { const minAggregated = Math.ceil(min); @@ -81,10 +77,18 @@ export class AudiocallStartPage extends Renderable { levelBtn.classList.add('level-button-active'); }); } + + this.buttonsContainer.append( + this.buttonContainerText, + this.levelBtnContainer, + ); } - this.startButton.addEventListener('click', () => { - this.onStartGame(this.group, this.page); - }); + this.buttonsContainer.append( + this.startButton, + ); + + this.mainContainer = dch('div', ['audiocall-page--main'], '', this.titleContainer, this.gameDescription); + this.rootNode = dch('div', ['audiocall-page'], '', this.mainContainer, this.buttonsContainer); } } From 9eae169210e691cbddcd97776f0f55a32c7ac397 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 13:55:58 +0400 Subject: [PATCH 15/24] feat: add SprintPage --- src/app/views/sprint/SprintPage.ts | 64 ++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/app/views/sprint/SprintPage.ts diff --git a/src/app/views/sprint/SprintPage.ts b/src/app/views/sprint/SprintPage.ts new file mode 100644 index 0000000..882d9fd --- /dev/null +++ b/src/app/views/sprint/SprintPage.ts @@ -0,0 +1,64 @@ +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; + + console.log(questionsData); + + 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); + }; +} From c740374eb153b9869f495270c6793d83fe0ff9a5 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 13:56:06 +0400 Subject: [PATCH 16/24] feat: add SprintPage --- src/app/controllers/SprintController.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) 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()); } } From afa4281734c9243fdb5d76dfe046f0dc9f450c3f Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 13:56:36 +0400 Subject: [PATCH 17/24] refactor: full refactor of sprint game --- src/app/views/sprint/SprintGameField.ts | 144 +++++++------ src/app/views/sprint/SprintQuestion.ts | 46 +++-- src/app/views/sprint/SprintStartPage.ts | 214 ++++++++++++++------ src/app/views/sprint/SprintStatisticPage.ts | 93 ++++++++- 4 files changed, 330 insertions(+), 167 deletions(-) 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/SprintQuestion.ts b/src/app/views/sprint/SprintQuestion.ts index 3b94450..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); + 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 === 'Digit1') { - this.wrongBtn.click(); + this.rightBtn.click(); document.removeEventListener('keyup', this.handlerKey); } else if (event.code === 'Digit2') { - this.rightBtn.click(); - document.removeEventListener('keydown', this.handlerKey); + 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..0bc92df 100644 --- a/src/app/views/sprint/SprintStartPage.ts +++ b/src/app/views/sprint/SprintStartPage.ts @@ -1,84 +1,178 @@ 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; + + buttonContainerText: HTMLElement; + + 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.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)); + + this.buttonsContainer.append( + this.buttonContainerText, + this.levelBtnContainer, + ); } + + this.buttonsContainer.append( + this.startButton, + ); + + this.mainContainer = dch('div', ['audiocall-page--main'], '', this.titleContainer, this.gameDescription); + this.rootNode = dch('div', ['audiocall-page'], '', this.mainContainer, this.buttonsContainer); } +} - createAnswer = (array: IWord[], index: number, answerVariant: boolean) => { - if (answerVariant) { - return array[index].wordTranslate; - } - const randomInteger: number = Math.floor(Math.random() * array.length); - const indexTranslate = (randomInteger === index) - ? Math.floor(Math.random() * array.length) : randomInteger; +// +// 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'; - return array[indexTranslate].wordTranslate; - }; +// export class SprintStartPage extends Renderable { +// group: number | undefined; - createQuestionData = (array: IWord[]) => { - const shuffledArray = array.sort(() => Math.random() - 0.5); - const questionsArray = shuffledArray.map((item, index) => { - const answerVariant = !!Math.round(Math.random()); +// page: number | undefined; - const translate = this.createAnswer(shuffledArray, index, answerVariant); +// gameDescription: HTMLElement; - return { - word: item, - translate, - isCorrect: answerVariant, - } as ISprintQuestionData; - }); - this.startButton.addEventListener('click', () => { - this.startGame(questionsArray); - }); - }; +// pages: number; - startGame(questionArrayData: ISprintQuestionData[]) { - const gameField = new SprintGameField(questionArrayData); - this.rootNode.innerHTML = ''; - this.rootNode.append(gameField.getElement()); - } -} +// startButton: HTMLElement; + +// 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); +// 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))); +// }); +// } +// } 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; +// } +// 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); +// }); +// }; + +// startGame(questionArrayData: ISprintQuestionData[]) { +// const gameField = new SprintGameField(questionArrayData); +// this.rootNode.innerHTML = ''; +// this.rootNode.append(gameField.getElement()); +// } +// } diff --git a/src/app/views/sprint/SprintStatisticPage.ts b/src/app/views/sprint/SprintStatisticPage.ts index 88b6ac3..28de55b 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); }); @@ -54,3 +64,64 @@ export class SprintStatisticPage extends Renderable { year: 'numeric', }); } + +// +// +// +// +// 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'; + +// export class SprintStatisticPage extends Renderable { +// resultData: IResultData[]; + +// resultContainer: HTMLElement; + +// date: string; + +// statisticContainer: HTMLElement; + +// playAgainButton: HTMLElement; + +// constructor(resultData: IResultData[], answerChain: number) { +// super(); +// this.resultData = resultData; +// this.statisticContainer = dch('p', [], `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.date = this.formatDate(new Date()); +// resultData.forEach((item) => { +// const audioWordData = `${GlobalConstants.DEFAULT_API_URL}/${item.questionData.audio}`; +// const playAudioBtn = dch('button', [], 'play'); +// playAudioBtn.onclick = () => { +// this.playAudio(audioWordData); +// }; +// 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']); +// wordContainer.append(playAudioBtn, word, wordTranslate, wordResult); +// this.resultContainer.append(wordContainer, this.statisticContainer); +// }); +// } + +// playAudio = (audio: string) => { +// musicPlayer.setPlayList([audio]); +// musicPlayer.play() +// .catch((e) => console.error(e)); +// }; + +// formatDate = (date: Date) => date.toLocaleDateString('ru-RU', { +// day: 'numeric', +// month: 'numeric', +// year: 'numeric', +// }); +// } From 5b104821ac951dab076d2e210f3b24cd9672b086 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 13:58:50 +0400 Subject: [PATCH 18/24] feat: remove comments --- src/app/views/sprint/SprintStartPage.ts | 86 --------------------- src/app/views/sprint/SprintStatisticPage.ts | 61 --------------- 2 files changed, 147 deletions(-) diff --git a/src/app/views/sprint/SprintStartPage.ts b/src/app/views/sprint/SprintStartPage.ts index 0bc92df..ed713ee 100644 --- a/src/app/views/sprint/SprintStartPage.ts +++ b/src/app/views/sprint/SprintStartPage.ts @@ -90,89 +90,3 @@ export class SprintStartPage extends Renderable { this.rootNode = dch('div', ['audiocall-page'], '', this.mainContainer, this.buttonsContainer); } } - -// -// 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'; - -// export class SprintStartPage extends Renderable { -// group: number | undefined; - -// page: number | undefined; - -// gameDescription: HTMLElement; - -// pages: number; - -// startButton: HTMLElement; - -// 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); -// 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))); -// }); -// } -// } 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; -// } -// 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); -// }); -// }; - -// startGame(questionArrayData: ISprintQuestionData[]) { -// const gameField = new SprintGameField(questionArrayData); -// this.rootNode.innerHTML = ''; -// this.rootNode.append(gameField.getElement()); -// } -// } diff --git a/src/app/views/sprint/SprintStatisticPage.ts b/src/app/views/sprint/SprintStatisticPage.ts index 28de55b..51b8f96 100644 --- a/src/app/views/sprint/SprintStatisticPage.ts +++ b/src/app/views/sprint/SprintStatisticPage.ts @@ -64,64 +64,3 @@ export class SprintStatisticPage extends Renderable { year: 'numeric', }); } - -// -// -// -// -// 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'; - -// export class SprintStatisticPage extends Renderable { -// resultData: IResultData[]; - -// resultContainer: HTMLElement; - -// date: string; - -// statisticContainer: HTMLElement; - -// playAgainButton: HTMLElement; - -// constructor(resultData: IResultData[], answerChain: number) { -// super(); -// this.resultData = resultData; -// this.statisticContainer = dch('p', [], `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.date = this.formatDate(new Date()); -// resultData.forEach((item) => { -// const audioWordData = `${GlobalConstants.DEFAULT_API_URL}/${item.questionData.audio}`; -// const playAudioBtn = dch('button', [], 'play'); -// playAudioBtn.onclick = () => { -// this.playAudio(audioWordData); -// }; -// 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']); -// wordContainer.append(playAudioBtn, word, wordTranslate, wordResult); -// this.resultContainer.append(wordContainer, this.statisticContainer); -// }); -// } - -// playAudio = (audio: string) => { -// musicPlayer.setPlayList([audio]); -// musicPlayer.play() -// .catch((e) => console.error(e)); -// }; - -// formatDate = (date: Date) => date.toLocaleDateString('ru-RU', { -// day: 'numeric', -// month: 'numeric', -// year: 'numeric', -// }); -// } From 3fab25f67ffdf621010ec1f285c66322b3f3182e Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 14:15:22 +0400 Subject: [PATCH 19/24] feat: add route from wordbook to games --- src/app/views/menu/Menu.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/app/views/menu/Menu.ts b/src/app/views/menu/Menu.ts index baf1e27..d2e3b1b 100644 --- a/src/app/views/menu/Menu.ts +++ b/src/app/views/menu/Menu.ts @@ -75,6 +75,24 @@ export class Menu extends Renderable { if (currentPath.indexOf(item.path) === 0) { list.classList.add('nav-menu--item-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); From 6d7be31790f68b7ba9119690718d0138ab10ec18 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 15:05:29 +0400 Subject: [PATCH 20/24] fix: remove console.log --- src/app/views/sprint/SprintPage.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app/views/sprint/SprintPage.ts b/src/app/views/sprint/SprintPage.ts index 882d9fd..ebe1b5d 100644 --- a/src/app/views/sprint/SprintPage.ts +++ b/src/app/views/sprint/SprintPage.ts @@ -22,8 +22,6 @@ export class SprintPage extends Renderable { currentQuestion: 0, } as IGameQuestionArray; - console.log(questionsData); - this.rootNode.innerHTML = ''; gameField.setQuestionsArray(questionsData); gameField.startGame(); From 64d9fb2acf98fd5b70bee19aee3b4d0f757e3f64 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 15:17:49 +0400 Subject: [PATCH 21/24] fix: trailing spaces --- src/app/views/audiocall/AudiocallGameField.ts | 2 +- src/app/views/audiocall/AudiocallStatisticPage.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/views/audiocall/AudiocallGameField.ts b/src/app/views/audiocall/AudiocallGameField.ts index 5313d08..c197b5a 100644 --- a/src/app/views/audiocall/AudiocallGameField.ts +++ b/src/app/views/audiocall/AudiocallGameField.ts @@ -77,7 +77,7 @@ export class AudiocallGameField extends Renderable { this.maxAnswerChain = this.answerChain; } }; - + this.rootNode.append(cardQuestion.getElement()); } } diff --git a/src/app/views/audiocall/AudiocallStatisticPage.ts b/src/app/views/audiocall/AudiocallStatisticPage.ts index a8d1e8e..bf2457a 100644 --- a/src/app/views/audiocall/AudiocallStatisticPage.ts +++ b/src/app/views/audiocall/AudiocallStatisticPage.ts @@ -43,7 +43,7 @@ export class AudiocallStatisticPage extends Renderable { UserWordService.setWordStatistic(userId, item.questionData.id, item.isCorrect) .catch((e) => console.error(e)); } - + const word = dch( 'p', ['button-word--text-item'], From 3ce117711587f074bc920592586ed1cbce4ef671 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 15:22:12 +0400 Subject: [PATCH 22/24] fix: number on button --- src/app/views/audiocall/AudiocallQuestion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/views/audiocall/AudiocallQuestion.ts b/src/app/views/audiocall/AudiocallQuestion.ts index 255f656..599fc37 100644 --- a/src/app/views/audiocall/AudiocallQuestion.ts +++ b/src/app/views/audiocall/AudiocallQuestion.ts @@ -35,7 +35,7 @@ export class AudiocallQuestion extends Renderable { 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); From 06d5635294a7afda9dbec34287593c41449e13ef Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 15:38:30 +0400 Subject: [PATCH 23/24] fix: layout --- src/app/views/audiocall/AudiocallStartPage.scss | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/app/views/audiocall/AudiocallStartPage.scss b/src/app/views/audiocall/AudiocallStartPage.scss index 200bcf4..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; From d5c682055a1f7be614309ec98a3383d7860bb5d3 Mon Sep 17 00:00:00 2001 From: thirdmadman Date: Wed, 23 Feb 2022 15:39:42 +0400 Subject: [PATCH 24/24] feat: add questionText --- src/app/views/audiocall/AudiocallQuestion.scss | 13 +++++++++++-- src/app/views/audiocall/AudiocallQuestion.ts | 12 +++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) 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 599fc37..9d20c9c 100644 --- a/src/app/views/audiocall/AudiocallQuestion.ts +++ b/src/app/views/audiocall/AudiocallQuestion.ts @@ -26,12 +26,22 @@ export class AudiocallQuestion extends Renderable { 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) => {