diff --git a/package-lock.json b/package-lock.json index 5d672fc..564c59c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "@angular-eslint/eslint-plugin-template": "^18.0.0", "@angular-eslint/schematics": "^18.0.0", "@angular-eslint/template-parser": "^18.0.0", - "@angular/cli": "^18.0.0", + "@angular/cli": "18.2.10", "@angular/compiler-cli": "^18.0.0", "@angular/language-service": "^18.0.0", "@capacitor/cli": "6.1.2", @@ -265,12 +265,12 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.7.tgz", - "integrity": "sha512-j7198lpkOXMG+Gyfln/5aDgBZV7m4pWMzHFhkO3+w3cbCNUN1TVZW0SyJcF+CYaxANzTbuumfvpsYc/fTeAGLw==", + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.10.tgz", + "integrity": "sha512-EIm/yCYg3ZYPsPYJxXRX5F6PofJCbNQ5rZEuQEY09vy+ZRTqGezH0qoUP5WxlYeJrjiRLYqADI9WtVNzDyaD4w==", "dev": true, "dependencies": { - "@angular-devkit/core": "18.2.7", + "@angular-devkit/core": "18.2.10", "jsonc-parser": "3.3.1", "magic-string": "0.30.11", "ora": "5.4.1", @@ -282,6 +282,33 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@angular-devkit/schematics/node_modules/@angular-devkit/core": { + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.10.tgz", + "integrity": "sha512-LFqiNdraBujg8e1lhuB0bkFVAoIbVbeXXwfoeROKH60OPbP8tHdgV6sFTqU7UGBKA+b+bYye70KFTG2Ys8QzKQ==", + "dev": true, + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, "node_modules/@angular-eslint/builder": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-18.3.1.tgz", @@ -458,17 +485,17 @@ } }, "node_modules/@angular/cli": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.7.tgz", - "integrity": "sha512-KoWgSvhRsU05A2m6B7jw1kdpyoS+Ce5GGLW6xcnX7VF2AckW54vYd/8ZkgpzQrKfvIpVblYd4KJGizKoaLZ5jA==", + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.10.tgz", + "integrity": "sha512-qW/F3XVZMzzenFzbn+7FGpw8GOt9qW8UxBtYya7gUNdWlcsgGUk+ZaGC2OLbfI5gX6pchW4TOPMsDSMeaCEI2Q==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1802.7", - "@angular-devkit/core": "18.2.7", - "@angular-devkit/schematics": "18.2.7", + "@angular-devkit/architect": "0.1802.10", + "@angular-devkit/core": "18.2.10", + "@angular-devkit/schematics": "18.2.10", "@inquirer/prompts": "5.3.8", "@listr2/prompt-adapter-inquirer": "2.0.15", - "@schematics/angular": "18.2.7", + "@schematics/angular": "18.2.10", "@yarnpkg/lockfile": "1.1.0", "ini": "4.1.3", "jsonc-parser": "3.3.1", @@ -490,6 +517,48 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@angular/cli/node_modules/@angular-devkit/architect": { + "version": "0.1802.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.10.tgz", + "integrity": "sha512-/xudcHK2s4J/GcL6qyobmGaWMHQcYLSMqCaWMT+nK6I6tu9VEAj/p3R83Tzx8B/eKi31Pz499uHw9pmqdtbafg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "18.2.10", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/cli/node_modules/@angular-devkit/core": { + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.10.tgz", + "integrity": "sha512-LFqiNdraBujg8e1lhuB0bkFVAoIbVbeXXwfoeROKH60OPbP8tHdgV6sFTqU7UGBKA+b+bYye70KFTG2Ys8QzKQ==", + "dev": true, + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, "node_modules/@angular/common": { "version": "18.2.7", "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.7.tgz", @@ -5056,13 +5125,13 @@ "dev": true }, "node_modules/@schematics/angular": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.7.tgz", - "integrity": "sha512-WOBzO11qstznHbC9tZXQf6/8+PqmaRI6QYcdTspqXNh9q9nNglvi43Xn4tSIpEhW8aSHea9hgWZV8sG+i/4W9Q==", + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.10.tgz", + "integrity": "sha512-2pDHT4aSzfs8Up4RQmHHuFd5FeuUebS1ZJwyt46MfXzRMFtzUZV/JKsIvDqyMwnkvFfLvgJyTCkl8JGw5jQObg==", "dev": true, "dependencies": { - "@angular-devkit/core": "18.2.7", - "@angular-devkit/schematics": "18.2.7", + "@angular-devkit/core": "18.2.10", + "@angular-devkit/schematics": "18.2.10", "jsonc-parser": "3.3.1" }, "engines": { @@ -5071,6 +5140,33 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.10.tgz", + "integrity": "sha512-LFqiNdraBujg8e1lhuB0bkFVAoIbVbeXXwfoeROKH60OPbP8tHdgV6sFTqU7UGBKA+b+bYye70KFTG2Ys8QzKQ==", + "dev": true, + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, "node_modules/@sigstore/bundle": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", diff --git a/package.json b/package.json index 04ac038..8f3da1c 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@angular-eslint/eslint-plugin-template": "^18.0.0", "@angular-eslint/schematics": "^18.0.0", "@angular-eslint/template-parser": "^18.0.0", - "@angular/cli": "^18.0.0", + "@angular/cli": "18.2.10", "@angular/compiler-cli": "^18.0.0", "@angular/language-service": "^18.0.0", "@capacitor/cli": "6.1.2", diff --git a/src/app/app.component.html b/src/app/app.component.html index 8304967..95f98b8 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -5,12 +5,14 @@ Edu App - - - - {{ p.title }} - - + @for (p of appPages; track p; let i = $index) { + + + + {{ p.title }} + + + } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c687d89..baadc02 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,4 @@ -import {CommonModule} from '@angular/common'; + import {Component} from '@angular/core'; import {RouterLink, RouterLinkActive} from '@angular/router'; import { @@ -39,19 +39,19 @@ import { templateUrl: 'app.component.html', styleUrls: ['app.component.scss'], standalone: true, - imports: [RouterLink, RouterLinkActive, CommonModule, IonApp, IonSplitPane, IonMenu, IonContent, IonList, IonListHeader, IonNote, IonMenuToggle, IonItem, IonIcon, IonLabel, IonRouterLink, IonRouterOutlet], + imports: [RouterLink, RouterLinkActive, IonApp, IonSplitPane, IonMenu, IonContent, IonList, IonListHeader, IonNote, IonMenuToggle, IonItem, IonIcon, IonLabel, IonRouterLink, IonRouterOutlet], }) export class AppComponent { public appPages = [ {title: 'Home', url: '/home', icon: 'mail'}, {title: 'Inbox', url: '/folder/inbox', icon: 'mail'}, {title: 'Outbox', url: '/folder/outbox', icon: 'paper-plane'}, - {title: 'Favorites', url: '/folder/favorites', icon: 'heart'}, + {title: 'Achievments', url: '/achievements', icon: 'paper-plane' }, + { title: 'Favorites', url: '/folder/favorites', icon: 'heart'}, {title: 'Archived', url: '/folder/archived', icon: 'archive'}, {title: 'Trash', url: '/folder/trash', icon: 'trash'}, {title: 'Avatar Settings', url: '/avatar-settings', icon: 'warning'}, {title: 'Login', url: '/login-screen', icon: 'warning'}, - ]; constructor() { diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 8139f94..cd24426 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -25,6 +25,10 @@ export const routes: Routes = [ path: 'home', loadComponent: () => import('./pages/home/home.page').then(m => m.HomePage) }, + { + path: 'achievements', + loadComponent: () => import('./pages/achievements/achievements.page').then(m => m.AchievementsPage) + }, { path: 'game', loadComponent: () => import('./pages/game/game.page').then(m => m.GamePage) diff --git a/src/app/models/achievemets.model.ts b/src/app/models/achievemets.model.ts new file mode 100644 index 0000000..e1a62c4 --- /dev/null +++ b/src/app/models/achievemets.model.ts @@ -0,0 +1,8 @@ +export interface Achievement { + title: string, // Name of Achievement + svg: string, // Icon or image path + text: string, // Description of success + expanded: boolean, // Unpacking status (true = unpacked) + unlocked: boolean, // Unlock status (true = unlocked) +} + diff --git a/src/app/pages/achievements/achievements.page.html b/src/app/pages/achievements/achievements.page.html new file mode 100644 index 0000000..0acd854 --- /dev/null +++ b/src/app/pages/achievements/achievements.page.html @@ -0,0 +1,38 @@ + + + Achievements + + + + + + + + Complete (-%) + + + + + @for (square of squares; track square; let index = $index) { + + @if (!square.expanded) { + + + + + } + + @if (square.expanded) { + + {{ square.title }} + {{ square.text }} + + } + + } + + + diff --git a/src/app/pages/achievements/achievements.page.scss b/src/app/pages/achievements/achievements.page.scss new file mode 100644 index 0000000..355cc9b --- /dev/null +++ b/src/app/pages/achievements/achievements.page.scss @@ -0,0 +1,106 @@ +/* Container for centering content */ +.container { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 5px; +} + +/* Completion banner for achievement progress */ +.completion-banner { + width: 324px; + height: 86px; + background-color: var(--ion-color-secondary); + border-radius: 10px; + margin-bottom: 16px; + display: flex; + align-items: center; + justify-content: center; + color: var(--ion-color-secondary-contrast); +} + +.completion-banner .banner-text { + font-size: 38px; +} + +/* Grid layout for achievement squares */ +.grid-container { + display: grid; + grid-template-columns: repeat(5, 164px); + grid-template-rows: repeat(4, 121px); + gap: 8px; + justify-content: center; +} + +/* Styles for each square in the grid */ +.square { + position: relative; /* For the pseudo-element */ + background-color: var(--ion-color-primary); + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + width: 164px; + height: 121px; + cursor: pointer; + transition: transform 0.3s, background-color 0.3s; + z-index: 1; /* Normal square has basic priority */ +} + +/* Styles when a square is expanded */ +.square.expanded { + transform: scale(1.5); /* Enlarges the square on click */ + background-color: var(--ion-color-secondary); /* Outer color (fill) */ + z-index: 10; /* Ensures the expanded square is above the others */ +} + +.square.expanded::before { + content: ''; + position: absolute; + top: 5%; + left: 5%; + width: 90%; + height: 90%; + background-color: var(--ion-color-primary); /* Inner color (path) */ + border-radius: 5px; /* Maintains rounding */ + z-index: 0; /* The pseudo-element is below text and icons */ +} + +/* Container for the SVG icons inside squares */ +.svg-slot { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.svg-slot img { + width: 50px; + height: 50px; +} + +/* Title inside the expanded square */ +.square-title { + font-size: 18px; + font-weight: bold; + color: var(--ion-color-secondary-contrast); + text-align: center; + margin-bottom: 5px; + position: relative; /* Ensures the text stays above the pseudo-element */ + z-index: 2; /* Text will be above the pseudo-element */ +} +.square.unlocked { + background-color: var(--ion-color-success) !important; /* This green color is used for completed achievements */ + border: 2px solid var(--ion-color-primary); + +} + +/* Text inside the expanded square */ +.square-text { + color: var(--ion-color-secondary-contrast); + font-size: 15px; + text-align: center; + position: relative; /* Ensures visibility above the pseudo-element */ + z-index: 2; /* Text will be above the pseudo-element */ +} diff --git a/src/app/pages/achievements/achievements.page.spec.ts b/src/app/pages/achievements/achievements.page.spec.ts new file mode 100644 index 0000000..081086c --- /dev/null +++ b/src/app/pages/achievements/achievements.page.spec.ts @@ -0,0 +1,17 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AchievementsPage } from './achievements.page'; + +describe('AchievementsPage', () => { + let component: AchievementsPage; + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.createComponent(AchievementsPage); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/achievements/achievements.page.ts b/src/app/pages/achievements/achievements.page.ts new file mode 100644 index 0000000..4ead730 --- /dev/null +++ b/src/app/pages/achievements/achievements.page.ts @@ -0,0 +1,43 @@ +import { Component, OnInit } from '@angular/core'; +import { AchievementsService } from './service/achievements.service'; // Fixed import service +import { + IonContent, + IonHeader, + IonTitle, + IonToolbar +} from '@ionic/angular/standalone'; +import {Achievement} from "../../models/achievemets.model"; + + +@Component({ + selector: 'app-achievements', + templateUrl: './achievements.page.html', + styleUrls: ['./achievements.page.scss'], + standalone: true, + imports: [ + IonContent, + IonHeader, + IonTitle, + IonToolbar + ] +}) +export class AchievementsPage implements OnInit { + squares:Achievement[] = []; + + + clickedSquare: number | null = null; // To track which square was clicked + + constructor(private achievementsService: AchievementsService) {} // Dependence on the service + + ngOnInit() { + // Retrieve a list of achievements from the service + this.squares = this.achievementsService.getAchievements(); + } + + toggleText(index: number) { + // Call the service to toggle the 'expanded' state for the selected achievement + this.achievementsService.toggleAchievementExpansion(index); + this.squares = this.achievementsService.getAchievements(); // Aktualizujeme stav po změně + } +} + diff --git a/src/app/pages/achievements/service/achievements.service.ts b/src/app/pages/achievements/service/achievements.service.ts new file mode 100644 index 0000000..ebf8d38 --- /dev/null +++ b/src/app/pages/achievements/service/achievements.service.ts @@ -0,0 +1,117 @@ +import { Injectable } from '@angular/core'; +import { Achievement } from "../../../models/achievemets.model"; + +@Injectable({ + providedIn: 'root', // This service will be available globally +}) +export class AchievementsService { + private achievements: Achievement [] = [ + { svg: '/assets/AchievementsIcon/GameWinner.svg', title: 'Game Winner', text: 'Win the game', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/FieldStealer.svg', title: 'Field Stealer', text: 'Steal your opponent\'s field', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/FieldConqueror.svg', title: 'Field Conqueror', text: 'Acquire blank field', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/Master.svg', title: 'Master', text: 'Win 5 games in a row', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/Destroyer.svg', title: 'Destroyer', text: 'Steal from your opponents 50 times', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/RowMaster.svg', title: 'Row Master', text: 'Acquire 10 fields in a row', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/VeteranWinner.svg', title: 'Veteran Winner', text: 'Win 25 games', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/TopAnswerer.svg', title: 'Top Answerer', text: 'Most questions answered', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/CenterConqueror.svg', title: 'Center Conqueror', text: 'Acquire center of the map', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/FieldCollector.svg', title: 'Field Collector', text: 'Acquire 500 fields', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/Champion.svg', title: 'Champion', text: 'Became best player 10 times', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/MysteryGift.svg', title: 'Mystery Gift', text: 'Acquire mystery gift', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/UltimateWinner.svg', title: 'Ultimate Winner', text: 'Win 100 games', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/SilentGenius.svg', title: 'Silent Genius', text: 'All questions answered', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/MountainConqueror.svg', title: 'Mountain Conqueror', text: 'Acquire all mountains', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/Secret.svg', title: '???', text: 'Nobody knows how to achieve it', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/QuestionMaster.svg', title: 'Question Master', text: 'Create 100 questions', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/FieldProtector.svg', title: 'Field Protector', text: 'Protect your field', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/Defender.svg', title: 'Defender', text: 'Protect your field 500 times', expanded: false, unlocked: false }, + { svg: 'assets/AchievementsIcon/Complete.svg', title: 'Complete', text: 'Complete all achievements', expanded: false, unlocked: false }, + ]; + + constructor() { } + + // Method to get a list of all achievements + getAchievements(): Achievement[] { + return [...this.achievements]; + } + + // Method to toggle the 'expanded' state for a specific achievement + toggleAchievementExpansion(index: number) { + this.achievements.forEach((achievement, i) => { + if (i === index) { + achievement.expanded = !achievement.expanded; + } else { + achievement.expanded = false; + } + }); + } + + // Method for unlocking success + unlockAchievement(title: string) { + const achievement = this.achievements.find(a => a.title === title); + if (achievement && !achievement.unlocked) { + achievement.unlocked = true; + } + } + + // Method for checking success and unlocking + checkAndUnlockAchievements(gameResult: { won: boolean, stolenField: boolean, winStreak: number, answeredQuestions: number, createdQuestions: number }) { + if (gameResult.won && !this.achievements[0].unlocked) { + this.unlockAchievement('Game Winner'); // Unlocked "Game Winner" + } + if (gameResult.stolenField && !this.achievements[1].unlocked) { + this.unlockAchievement('Field Stealer'); // Unlocked "Field Stealer" + } + if (gameResult.winStreak >= 5 && !this.achievements[3].unlocked) { + this.unlockAchievement('Master'); // Unlocked "Master" + } + if (gameResult.winStreak >= 50 && !this.achievements[4].unlocked) { + this.unlockAchievement('Destroyer'); // Unlocked "Destroyer" + } + if (gameResult.winStreak >= 10 && !this.achievements[5].unlocked) { + this.unlockAchievement('Row Master'); // Unlocked "Row Master" + } + if (gameResult.won && !this.achievements[6].unlocked) { + this.unlockAchievement('Veteran Winner'); // Unlocked "Veteran Winner" + } + if (gameResult.answeredQuestions >= 100 && !this.achievements[7].unlocked) { + this.unlockAchievement('Top Answerer'); // Unlocked "Top Answerer" + } + if (gameResult.stolenField && !this.achievements[8].unlocked) { + this.unlockAchievement('Center Conqueror'); // Unlocked "Center Conqueror" + } + if (gameResult.winStreak >= 500 && !this.achievements[9].unlocked) { + this.unlockAchievement('Field Collector'); // Unlocked "Field Collector" + } + if (gameResult.winStreak >= 10 && !this.achievements[10].unlocked) { + this.unlockAchievement('Champion'); // Unlocked "Champion" + } + if (gameResult.stolenField && !this.achievements[11].unlocked) { + this.unlockAchievement('Mystery Gift'); // Unlocked "Mystery Gift" + } + if (gameResult.won && gameResult.winStreak >= 100 && !this.achievements[12].unlocked) { + this.unlockAchievement('Ultimate Winner'); // Unlocked "Ultimate Winner" + } + if (gameResult.answeredQuestions >= 1000 && !this.achievements[13].unlocked) { + this.unlockAchievement('Silent Genius'); // Unlocked "Silent Genius" + } + if (gameResult.winStreak >= 500 && !this.achievements[14].unlocked) { + this.unlockAchievement('Mountain Conqueror'); // Unlocked "Mountain Conqueror" + } + if (gameResult.stolenField && !this.achievements[15].unlocked) { + this.unlockAchievement('Secret'); // Unlocked "???" + } + if (gameResult.createdQuestions >= 100 && !this.achievements[16].unlocked) { + this.unlockAchievement('Question Master'); // Unlocked "Question Master" + } + if (gameResult.winStreak >= 500 && !this.achievements[17].unlocked) { + this.unlockAchievement('Field Protector'); // Unlocked "Field Protector" + } + if (gameResult.winStreak >= 500 && !this.achievements[18].unlocked) { + this.unlockAchievement('Defender'); // Unlocked "Defender" + } + if (this.achievements.every(a => a.unlocked) && !this.achievements[19].unlocked) { + this.unlockAchievement('Complete'); // Unlocked "Complete" + } + } +} diff --git a/src/app/pages/achievements/service/achievments.service.spec.ts b/src/app/pages/achievements/service/achievments.service.spec.ts new file mode 100644 index 0000000..d270c28 --- /dev/null +++ b/src/app/pages/achievements/service/achievments.service.spec.ts @@ -0,0 +1,43 @@ + +import { TestBed } from '@angular/core/testing'; +import { AchievementsService } from './achievements.service'; + +describe('AchievementsService', () => { + let service: AchievementsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AchievementsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should return a list of achievements', () => { + const achievements = service.getAchievements(); + expect(achievements.length).toBeGreaterThan(0); + }); + + it('should toggle expanded state of an achievement', () => { + const index = 0; + service.toggleAchievementExpansion(index); + const achievements = service.getAchievements(); + expect(achievements[index].expanded).toBeTrue(); + + service.toggleAchievementExpansion(index); + expect(achievements[index].expanded).toBeFalse(); + }); + + it('should close all achievements when toggling a new one', () => { + const index1 = 0; + const index2 = 1; + + service.toggleAchievementExpansion(index1); + service.toggleAchievementExpansion(index2); + + const achievements = service.getAchievements(); + expect(achievements[index1].expanded).toBeFalse(); + expect(achievements[index2].expanded).toBeTrue(); + }); +}); diff --git a/src/app/pages/avatar-settings/avatar-settings.page.ts b/src/app/pages/avatar-settings/avatar-settings.page.ts index 262dd9e..f1fa9c8 100644 --- a/src/app/pages/avatar-settings/avatar-settings.page.ts +++ b/src/app/pages/avatar-settings/avatar-settings.page.ts @@ -1,5 +1,5 @@ import {Component, OnInit} from '@angular/core'; -import {CommonModule} from '@angular/common'; + import {FormControl, FormsModule, ReactiveFormsModule, Validators} from '@angular/forms'; import {Avatar, User} from "../../models/user.model"; import { @@ -15,6 +15,7 @@ import { import {debounceTime} from "rxjs"; import {addIcons} from "ionicons"; import {createOutline} from "ionicons/icons"; +import {CommonModule} from "@angular/common"; @Component({ selector: 'app-avatar-settings', diff --git a/src/app/pages/game/game.page.ts b/src/app/pages/game/game.page.ts index 421bc05..5cabb33 100644 --- a/src/app/pages/game/game.page.ts +++ b/src/app/pages/game/game.page.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { CommonModule } from '@angular/common'; + import { FormsModule } from '@angular/forms'; import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/standalone'; @@ -8,7 +8,7 @@ import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/stan templateUrl: './game.page.html', styleUrls: ['./game.page.scss'], standalone: true, - imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule] + imports: [IonContent, IonHeader, IonTitle, IonToolbar, FormsModule] }) export class GamePage implements OnInit { diff --git a/src/app/pages/home/home.page.ts b/src/app/pages/home/home.page.ts index e886d0c..e08b7b7 100644 --- a/src/app/pages/home/home.page.ts +++ b/src/app/pages/home/home.page.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { CommonModule } from '@angular/common'; + import { FormsModule } from '@angular/forms'; import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/standalone'; @@ -8,7 +8,7 @@ import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/stan templateUrl: './home.page.html', styleUrls: ['./home.page.scss'], standalone: true, - imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule] + imports: [IonContent, IonHeader, IonTitle, IonToolbar, FormsModule] }) export class HomePage implements OnInit { diff --git a/src/app/pages/login-screen/login-screen.page.ts b/src/app/pages/login-screen/login-screen.page.ts index 0aa8e9d..5d01acc 100644 --- a/src/app/pages/login-screen/login-screen.page.ts +++ b/src/app/pages/login-screen/login-screen.page.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { CommonModule } from '@angular/common'; + import { FormsModule } from '@angular/forms'; import { IonAlert, @@ -18,7 +18,7 @@ import { templateUrl: './login-screen.page.html', styleUrls: ['./login-screen.page.scss'], standalone: true, - imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule, IonButton, IonGrid, IonCol, IonRow, IonAlert] + imports: [IonContent, IonHeader, IonTitle, IonToolbar, FormsModule, IonButton, IonGrid, IonCol, IonRow, IonAlert] }) export class LoginScreenPage implements OnInit { public alertButtons = ['Action']; diff --git a/src/assets/AchievementsIcon/CenterConqueror.svg b/src/assets/AchievementsIcon/CenterConqueror.svg new file mode 100644 index 0000000..2af68dd --- /dev/null +++ b/src/assets/AchievementsIcon/CenterConqueror.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/Champion.svg b/src/assets/AchievementsIcon/Champion.svg new file mode 100644 index 0000000..fa11020 --- /dev/null +++ b/src/assets/AchievementsIcon/Champion.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/Complete.svg b/src/assets/AchievementsIcon/Complete.svg new file mode 100644 index 0000000..dc8440e --- /dev/null +++ b/src/assets/AchievementsIcon/Complete.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/Defender.svg b/src/assets/AchievementsIcon/Defender.svg new file mode 100644 index 0000000..cc2a854 --- /dev/null +++ b/src/assets/AchievementsIcon/Defender.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/Destroyer.svg b/src/assets/AchievementsIcon/Destroyer.svg new file mode 100644 index 0000000..94b503d --- /dev/null +++ b/src/assets/AchievementsIcon/Destroyer.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/FieldCollector.svg b/src/assets/AchievementsIcon/FieldCollector.svg new file mode 100644 index 0000000..26e0468 --- /dev/null +++ b/src/assets/AchievementsIcon/FieldCollector.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/FieldConqueror.svg b/src/assets/AchievementsIcon/FieldConqueror.svg new file mode 100644 index 0000000..737d4c3 --- /dev/null +++ b/src/assets/AchievementsIcon/FieldConqueror.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/FieldProtector.svg b/src/assets/AchievementsIcon/FieldProtector.svg new file mode 100644 index 0000000..166e10a --- /dev/null +++ b/src/assets/AchievementsIcon/FieldProtector.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/FieldStealer.svg b/src/assets/AchievementsIcon/FieldStealer.svg new file mode 100644 index 0000000..1a9dfcb --- /dev/null +++ b/src/assets/AchievementsIcon/FieldStealer.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/GameWinner.svg b/src/assets/AchievementsIcon/GameWinner.svg new file mode 100644 index 0000000..4ade7a8 --- /dev/null +++ b/src/assets/AchievementsIcon/GameWinner.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/Master.svg b/src/assets/AchievementsIcon/Master.svg new file mode 100644 index 0000000..3ceddf5 --- /dev/null +++ b/src/assets/AchievementsIcon/Master.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/MountainConqueror.svg b/src/assets/AchievementsIcon/MountainConqueror.svg new file mode 100644 index 0000000..4db3020 --- /dev/null +++ b/src/assets/AchievementsIcon/MountainConqueror.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/MysteryGift.svg b/src/assets/AchievementsIcon/MysteryGift.svg new file mode 100644 index 0000000..660a46a --- /dev/null +++ b/src/assets/AchievementsIcon/MysteryGift.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/QuestionMaster.svg b/src/assets/AchievementsIcon/QuestionMaster.svg new file mode 100644 index 0000000..765bcbe --- /dev/null +++ b/src/assets/AchievementsIcon/QuestionMaster.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/RowMaster.svg b/src/assets/AchievementsIcon/RowMaster.svg new file mode 100644 index 0000000..43456a1 --- /dev/null +++ b/src/assets/AchievementsIcon/RowMaster.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/Secret.svg b/src/assets/AchievementsIcon/Secret.svg new file mode 100644 index 0000000..0bbac7a --- /dev/null +++ b/src/assets/AchievementsIcon/Secret.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/SilentGenius.svg b/src/assets/AchievementsIcon/SilentGenius.svg new file mode 100644 index 0000000..899b87e --- /dev/null +++ b/src/assets/AchievementsIcon/SilentGenius.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/TopAnswerer.svg b/src/assets/AchievementsIcon/TopAnswerer.svg new file mode 100644 index 0000000..767248d --- /dev/null +++ b/src/assets/AchievementsIcon/TopAnswerer.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/UltimateWinner.svg b/src/assets/AchievementsIcon/UltimateWinner.svg new file mode 100644 index 0000000..e1b2e96 --- /dev/null +++ b/src/assets/AchievementsIcon/UltimateWinner.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/AchievementsIcon/VeteranWinner.svg b/src/assets/AchievementsIcon/VeteranWinner.svg new file mode 100644 index 0000000..0293dea --- /dev/null +++ b/src/assets/AchievementsIcon/VeteranWinner.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file