diff --git a/angular.json b/angular.json
index fbcae5a..9afefbb 100644
--- a/angular.json
+++ b/angular.json
@@ -23,6 +23,9 @@
"polyfills": [
"zone.js"
],
+ "allowedCommonJsDependencies": [
+ "crypto-js"
+ ],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "sass",
"assets": [
diff --git a/package-lock.json b/package-lock.json
index 5378f9a..1fa6364 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,6 +19,7 @@
"@types/crypto-js": "^4.1.1",
"angular-cli-ghpages": "^1.0.6",
"crypto-js": "^4.1.1",
+ "moment": "^2.29.4",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"xp.css": "^0.2.6",
@@ -8242,6 +8243,14 @@
"mkdirp": "bin/cmd.js"
}
},
+ "node_modules/moment": {
+ "version": "2.29.4",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/mrmime": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz",
diff --git a/package.json b/package.json
index 5e373fb..38cbec9 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"@types/crypto-js": "^4.1.1",
"angular-cli-ghpages": "^1.0.6",
"crypto-js": "^4.1.1",
+ "moment": "^2.29.4",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"xp.css": "^0.2.6",
diff --git a/src/app/about-window/about-window.component.html b/src/app/about-window/about-window.component.html
index ba4dff2..6203978 100644
--- a/src/app/about-window/about-window.component.html
+++ b/src/app/about-window/about-window.component.html
@@ -1,5 +1,5 @@
-
+
@@ -13,7 +13,7 @@
href="https://www.deviantart.com/marchmountain/art/Windows-XP-High-Resolution-Icon-Pack-916042853">marchmountain
at DeviantArt
-
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index e63830c..e9675d9 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -31,11 +31,11 @@ const routes: Routes = [
component: QuestionWindowComponent,
},
{
- path: PathsEnum.CORRECT_ANSWER,
+ path: `${PathsEnum.CORRECT_ANSWER}/:points`,
component: CorrectAnswerWindowComponent,
},
{
- path: PathsEnum.WRONG_ANSWER,
+ path: `${PathsEnum.WRONG_ANSWER}/:answer`,
component: WrongAnswerWindowComponent
}
]
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index dc46fb1..220cfc0 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -13,6 +13,7 @@ import {ScoreWindowComponent} from './score-window/score-window.component';
import { CorrectAnswerWindowComponent } from './correct-answer-window/correct-answer-window.component';
import { WrongAnswerWindowComponent } from './wrong-answer-window/wrong-answer-window.component';
import { AboutWindowComponent } from './about-window/about-window.component';
+import {CopyClipboardDirective} from "./directives/CopyClipboardDirective";
@NgModule({
declarations: [
@@ -25,6 +26,7 @@ import { AboutWindowComponent } from './about-window/about-window.component';
CorrectAnswerWindowComponent,
WrongAnswerWindowComponent,
AboutWindowComponent,
+ CopyClipboardDirective
],
imports: [
BrowserModule,
diff --git a/src/app/common/icon-button/icon-button.component.html b/src/app/common/icon-button/icon-button.component.html
index 7d0c3bc..5bc91f2 100644
--- a/src/app/common/icon-button/icon-button.component.html
+++ b/src/app/common/icon-button/icon-button.component.html
@@ -1,6 +1,6 @@
diff --git a/src/app/common/window-title-bar/window-title-bar.component.html b/src/app/common/window-title-bar/window-title-bar.component.html
index 99daec4..3e91e8a 100644
--- a/src/app/common/window-title-bar/window-title-bar.component.html
+++ b/src/app/common/window-title-bar/window-title-bar.component.html
@@ -1,7 +1,7 @@
![]()
{{this.title}}
diff --git a/src/app/correct-answer-window/correct-answer-window.component.html b/src/app/correct-answer-window/correct-answer-window.component.html
index aab3ecb..1c629d4 100644
--- a/src/app/correct-answer-window/correct-answer-window.component.html
+++ b/src/app/correct-answer-window/correct-answer-window.component.html
@@ -1,5 +1,5 @@
-
+
@@ -7,7 +7,7 @@
-
diff --git a/src/app/correct-answer-window/correct-answer-window.component.sass b/src/app/correct-answer-window/correct-answer-window.component.sass
index a30ccbd..5ed149c 100644
--- a/src/app/correct-answer-window/correct-answer-window.component.sass
+++ b/src/app/correct-answer-window/correct-answer-window.component.sass
@@ -13,5 +13,4 @@
label
text-align: center
-
-
+ margin: 5px
diff --git a/src/app/correct-answer-window/correct-answer-window.component.ts b/src/app/correct-answer-window/correct-answer-window.component.ts
index 6bf2bd6..bb3e6ed 100644
--- a/src/app/correct-answer-window/correct-answer-window.component.ts
+++ b/src/app/correct-answer-window/correct-answer-window.component.ts
@@ -1,8 +1,9 @@
import {Component, OnInit} from '@angular/core';
-import {Router} from "@angular/router";
+import {ActivatedRoute, Router} from "@angular/router";
import {PathsEnum} from "../../model/PathsEnum";
import {StorageService} from "../../service/storage.service";
-import {StorageKeyEnum} from "../../model/StorageKeyEnum";
+import {AppStorage, WeekScore} from "../../model/AppStorage";
+import * as moment from "moment";
@Component({
selector: 'app-correct-answer-window',
@@ -11,14 +12,16 @@ import {StorageKeyEnum} from "../../model/StorageKeyEnum";
})
export class CorrectAnswerWindowComponent implements OnInit {
- public questionScore: number = 10;
+ public questionScore: number = 0;
private correctAnswerSound: HTMLAudioElement = new Audio('assets/sounds/tada.wav');
constructor(
private readonly router: Router,
+ private readonly route: ActivatedRoute,
private readonly storageService: StorageService
) {
+ this.questionScore = parseInt(this.route.snapshot.paramMap.get('points') ?? '0');
}
public async ngOnInit(): Promise
{
@@ -31,12 +34,26 @@ export class CorrectAnswerWindowComponent implements OnInit {
}
private saveCurrentScore(): void {
- const currentScore: string | null = this.storageService.get(StorageKeyEnum.CURRENT_SCORE);
- let currentScoreNumber: number = (currentScore === null || currentScore === '') ? 0 : parseInt(currentScore);
-
- currentScoreNumber += this.questionScore;
-
- this.storageService.save(StorageKeyEnum.CURRENT_SCORE, currentScoreNumber.toString());
- this.storageService.save(StorageKeyEnum.LAST_QUIZ_RESPONSE_DATE, new Date().getTime().toString());
+ const appStorage: AppStorage = this.storageService.get();
+ const currentWeek: number = moment().isoWeek();
+ const newCurrentScoreMap: Map = new Map([[currentWeek, {
+ score: 0,
+ rightAnswers: 0,
+ wrongAnswers: 0,
+ }]]);
+ const currentWeekScoreMap: Map = appStorage.weekScoreMap ?? newCurrentScoreMap;
+ const currentWeekScore: WeekScore = currentWeekScoreMap.get(currentWeek)!;
+
+ currentWeekScore.rightAnswers += 1;
+ currentWeekScore.score += this.questionScore;
+ currentWeekScoreMap.set(currentWeek, currentWeekScore!);
+
+ this.storageService.save(
+ {
+ ...appStorage,
+ weekScoreMap: currentWeekScoreMap,
+ lastQuizResponseDate: moment().toISOString()
+ }
+ )
}
}
diff --git a/src/app/directives/CopyClipboardDirective.ts b/src/app/directives/CopyClipboardDirective.ts
new file mode 100644
index 0000000..938b7f8
--- /dev/null
+++ b/src/app/directives/CopyClipboardDirective.ts
@@ -0,0 +1,32 @@
+import {Directive, Input, Output, EventEmitter, HostListener} from "@angular/core";
+
+@Directive({selector: '[copy-clipboard]'})
+export class CopyClipboardDirective {
+
+ @Input("copy-clipboard")
+ public payload: string = '';
+
+ @Output("copied")
+ public copied: EventEmitter = new EventEmitter();
+
+ @HostListener("click", ["$event"])
+ public onClick(event: MouseEvent): void {
+
+ event.preventDefault();
+ if (!this.payload)
+ return;
+
+ let listener = (e: ClipboardEvent) => {
+ // @ts-ignore
+ let clipboard = e.clipboardData || window["clipboardData"];
+ clipboard.setData("text", this.payload.toString());
+ e.preventDefault();
+
+ this.copied.emit(this.payload);
+ };
+
+ document.addEventListener("copy", listener, false)
+ document.execCommand("copy");
+ document.removeEventListener("copy", listener, false);
+ }
+}
diff --git a/src/app/main-window/main-window.component.html b/src/app/main-window/main-window.component.html
index 163175b..0dcd1a5 100644
--- a/src/app/main-window/main-window.component.html
+++ b/src/app/main-window/main-window.component.html
@@ -1,22 +1,22 @@
-
+
-
+
diff --git a/src/app/main-window/main-window.component.ts b/src/app/main-window/main-window.component.ts
index b0aff4f..3983359 100644
--- a/src/app/main-window/main-window.component.ts
+++ b/src/app/main-window/main-window.component.ts
@@ -1,8 +1,10 @@
import {Component, OnInit} from '@angular/core';
import {StorageService} from "../../service/storage.service";
-import {StorageKeyEnum} from "../../model/StorageKeyEnum";
import {Router} from "@angular/router";
import {PathsEnum} from "../../model/PathsEnum";
+import {AppStorage} from "../../model/AppStorage";
+import * as moment from "moment";
+import {Duration, Moment} from "moment";
@Component({
selector: 'app-main-window',
@@ -12,7 +14,7 @@ import {PathsEnum} from "../../model/PathsEnum";
export class MainWindowComponent implements OnInit {
public quizCanBeAnswered: boolean = true;
- public countdown: string = '';
+ public remainingTime: string = '';
protected readonly PathsEnum = PathsEnum;
@@ -23,7 +25,8 @@ export class MainWindowComponent implements OnInit {
}
public async ngOnInit(): Promise
{
- const lastQuizResponseDate: string | null = this.storageService.get(StorageKeyEnum.LAST_QUIZ_RESPONSE_DATE);
+ const appStorage: AppStorage = this.storageService.get();
+ const lastQuizResponseDate: string | null = appStorage.lastQuizResponseDate;
this.quizCanBeAnswered = this.checkIfQuizCanBeAnswered(lastQuizResponseDate);
@@ -31,57 +34,52 @@ export class MainWindowComponent implements OnInit {
this.startCountdown(lastQuizResponseDate);
}
- private checkIfQuizCanBeAnswered(lastQuizResponseDate: string | null): boolean {
- if (lastQuizResponseDate === null || lastQuizResponseDate === '') return true;
+ public async redirectTo(route: PathsEnum): Promise {
+ await this.router.navigateByUrl(route);
+ }
- const now: Date = new Date();
- const lastAnsweredDate: Date = new Date();
- const threeHoursInMs: number = 1000 * 60 * 60 * 3;
+ private checkIfQuizCanBeAnswered(lastQuizResponseDate: string | null): boolean {
+ if (lastQuizResponseDate === null) return true;
- lastAnsweredDate.setTime(parseInt(lastQuizResponseDate) + threeHoursInMs);
+ const now: Moment = moment();
+ const nextResponseMinimumDate: Moment = moment(lastQuizResponseDate).add(3, "hours");
- return now.getTime() >= lastAnsweredDate.getTime();
+ return now.isSame(nextResponseMinimumDate) || now.isAfter(nextResponseMinimumDate);
}
private startCountdown(lastQuizResponseDate: string | null): void {
- if (lastQuizResponseDate === null || lastQuizResponseDate === '') return;
-
- const nextAnswerDate: Date = new Date();
- const threeHoursInMs: number = 1000 * 60 * 60 * 3;
+ if (lastQuizResponseDate === null) return;
- nextAnswerDate.setTime(parseInt(lastQuizResponseDate) + threeHoursInMs);
+ const nextResponseMinimumDate: Moment = moment(lastQuizResponseDate).add(3, "hours");
- new Promise(async (resolve, reject): Promise => {
+ new Promise(async (resolve): Promise => {
while (true) {
- const now: Date = new Date();
-
- const sameHour: boolean = now.getUTCHours() === nextAnswerDate.getUTCHours();
- const sameMinute: boolean = now.getUTCMinutes() === nextAnswerDate.getUTCMinutes();
- const sameSecond: boolean = now.getUTCSeconds() === nextAnswerDate.getUTCSeconds();
+ const now: Moment = moment();
- if (sameHour && sameMinute && sameSecond) {
+ if (now.isSame(nextResponseMinimumDate) || now.isAfter(nextResponseMinimumDate)) {
this.quizCanBeAnswered = true;
- this.storageService.clear(StorageKeyEnum.LAST_QUIZ_RESPONSE_DATE);
+ this.clearLastAnsweredDate();
resolve();
break;
}
- const newTime: Date = new Date();
+ const timeLeft: Duration = moment.duration(nextResponseMinimumDate.valueOf() - now.valueOf());
- newTime.setTime(nextAnswerDate.getTime() - now.getTime());
-
- const hours: number = newTime.getUTCHours();
- const minutes: number = newTime.getUTCMinutes();
- const seconds: number = newTime.getUTCSeconds();
-
- this.countdown = `${hours} h, ${minutes} min, ${seconds} s`
+ this.remainingTime = `${timeLeft.hours()} hours, ${timeLeft.minutes()} minutes, ${timeLeft.seconds()} seconds`
await new Promise(f => setTimeout(f, 1000));
}
});
}
- public async redirectTo(route: PathsEnum): Promise {
- await this.router.navigateByUrl(route);
+ private clearLastAnsweredDate(): void {
+ const appStorage: AppStorage = this.storageService.get();
+
+ this.storageService.save(
+ {
+ ...appStorage,
+ lastQuizResponseDate: null
+ }
+ );
}
}
diff --git a/src/app/question-window/question-window.component.html b/src/app/question-window/question-window.component.html
index 5a0b515..228c080 100644
--- a/src/app/question-window/question-window.component.html
+++ b/src/app/question-window/question-window.component.html
@@ -1,6 +1,6 @@
-
+
@@ -12,6 +12,7 @@
+