From 2f35648264f16f03a97c7afa01a6b523fc1e4ba9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 28 Jan 2025 16:33:34 +0100 Subject: [PATCH] replace alerts und confirms with modals #15 --- src/app/components/popover.component.ts | 53 ++++++ .../create-deck-modal.component.ts | 27 +-- src/app/deck-list.component.html | 30 +++- src/app/deck-list.component.ts | 155 ++++++------------ .../edit-image-modal.component.ts | 32 ++-- .../move-image-modal.component.ts | 20 ++- src/app/services/popover.service.ts | 34 ++++ src/app/training/training.component.ts | 111 ++++++++----- 8 files changed, 274 insertions(+), 188 deletions(-) create mode 100644 src/app/components/popover.component.ts create mode 100644 src/app/services/popover.service.ts diff --git a/src/app/components/popover.component.ts b/src/app/components/popover.component.ts new file mode 100644 index 0000000..d318645 --- /dev/null +++ b/src/app/components/popover.component.ts @@ -0,0 +1,53 @@ +// popover.component.ts +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-popover', + standalone: true, + imports: [CommonModule, FormsModule], + template: ` +
+
+

{{ title }}

+ +

{{ message }}

+ + + +
+ + +
+
+
+ `, +}) +export class PopoverComponent { + @Input() title: string = ''; + @Input() message: string = ''; + @Input() showInput: boolean = false; + @Input() confirmText: string = 'Confirm'; + @Input() inputValue: string = ''; + @Input() visible: boolean = false; + @Output() confirmed = new EventEmitter(); + @Output() canceled = new EventEmitter(); + + onConfirm() { + this.confirmed.emit(this.inputValue); + //this.reset(); + } + + onCancel() { + this.canceled.emit(); + //this.reset(); + } + + private reset() { + this.visible = false; + this.inputValue = ''; + } +} diff --git a/src/app/create-deck-modal/create-deck-modal.component.ts b/src/app/create-deck-modal/create-deck-modal.component.ts index 31ded9f..fc4470a 100644 --- a/src/app/create-deck-modal/create-deck-modal.component.ts +++ b/src/app/create-deck-modal/create-deck-modal.component.ts @@ -1,14 +1,15 @@ -import { Component, Output, EventEmitter, AfterViewInit, ViewChild, ElementRef } from '@angular/core'; -import { DeckService } from '../deck.service'; import { CommonModule } from '@angular/common'; -import { Modal } from 'flowbite'; +import { AfterViewInit, Component, ElementRef, EventEmitter, Output, ViewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { Modal } from 'flowbite'; +import { DeckService } from '../deck.service'; +import { PopoverService } from '../services/popover.service'; @Component({ selector: 'app-create-deck-modal', templateUrl: './create-deck-modal.component.html', standalone: true, - imports: [CommonModule, FormsModule] + imports: [CommonModule, FormsModule], }) export class CreateDeckModalComponent implements AfterViewInit { @Output() deckCreated = new EventEmitter(); @@ -16,7 +17,7 @@ export class CreateDeckModalComponent implements AfterViewInit { deckName: string = ''; modal!: Modal; - constructor(private deckService: DeckService) { } + constructor(private deckService: DeckService, private popoverService: PopoverService) {} ngAfterViewInit(): void { this.modal = new Modal(this.modalElement.nativeElement); @@ -29,7 +30,10 @@ export class CreateDeckModalComponent implements AfterViewInit { createDeck(event: Event): void { event.preventDefault(); if (this.deckName.trim() === '') { - alert('Please enter a deck name.'); + this.popoverService.show({ + title: 'Information', + message: 'Please enter a deck name !', + }); return; } @@ -39,14 +43,17 @@ export class CreateDeckModalComponent implements AfterViewInit { this.deckCreated.emit(); this.modal.hide(); }, - error: (err) => { + error: err => { console.error('Error creating deck', err); - alert('Error creating deck.'); - } + this.popoverService.show({ + title: 'Error', + message: 'Error creating deck.', + }); + }, }); } closeModal(): void { this.modal.hide(); } -} \ No newline at end of file +} diff --git a/src/app/deck-list.component.html b/src/app/deck-list.component.html index 8ee4bef..172f2d2 100644 --- a/src/app/deck-list.component.html +++ b/src/app/deck-list.component.html @@ -18,7 +18,7 @@

{{ deck.name }}

- ({{ deck.images.length }}) + ({{ deck.images.length }} Pics)
Next training: {{ getNextTrainingString(deck) }}
@@ -123,20 +123,40 @@
-
+ -
+ + + diff --git a/src/app/deck-list.component.ts b/src/app/deck-list.component.ts index 67d12b6..9921f69 100644 --- a/src/app/deck-list.component.ts +++ b/src/app/deck-list.component.ts @@ -1,11 +1,13 @@ import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { firstValueFrom } from 'rxjs'; import { CreateDeckModalComponent } from './create-deck-modal/create-deck-modal.component'; import { Box, Deck, DeckImage, DeckService, OcrResult } from './deck.service'; import { EditImageModalComponent } from './edit-image-modal/edit-image-modal.component'; import { MoveImageModalComponent } from './move-image-modal/move-image-modal.component'; +import { PopoverService } from './services/popover.service'; import { TrainingComponent } from './training/training.component'; @Component({ @@ -28,14 +30,13 @@ import { TrainingComponent } from './training/training.component'; TrainingComponent, EditImageModalComponent, MoveImageModalComponent, // Adding the new component + FormsModule, ], }) export class DeckListComponent implements OnInit { decks: Deck[] = []; trainingsDeck: Deck | null = null; activeDeck: Deck | null = null; - private deckToDelete: string | null = null; - deckToRename: Deck | null = null; loading: boolean = false; @ViewChild(CreateDeckModalComponent) @@ -54,7 +55,7 @@ export class DeckListComponent implements OnInit { // State for moving images imageToMove: { image: DeckImage; sourceDeck: Deck } | null = null; - constructor(private deckService: DeckService, private cdr: ChangeDetectorRef, private http: HttpClient) {} + constructor(private deckService: DeckService, private cdr: ChangeDetectorRef, private http: HttpClient, private popoverService: PopoverService) {} ngOnInit(): void { this.loadExpandedDecks(); @@ -84,11 +85,12 @@ export class DeckListComponent implements OnInit { // Method to open the delete confirmation popover openDeletePopover(deckName: string): void { - this.deckToDelete = deckName; - const deletePopover = document.getElementById('deletePopover'); - if (deletePopover) { - deletePopover.showPopover(); - } + this.popoverService.show({ + title: 'Delete Deck', + message: 'Are you sure you want to delete this deck?', + confirmText: 'Delete', + onConfirm: () => this.confirmDelete(deckName), + }); } // Method to check if a deck is active @@ -97,113 +99,56 @@ export class DeckListComponent implements OnInit { } // Method to confirm the deletion of a deck - confirmDelete(): void { - if (this.deckToDelete) { - this.deckService.deleteDeck(this.deckToDelete).subscribe({ - next: () => { - this.loadDecks(); - this.closeDeletePopover(); - this.activeDeck = this.decks.length > 0 ? this.decks[0] : null; - }, - error: err => console.error('Error deleting deck', err), - }); - } - } - - // Method to cancel the deletion of a deck - cancelDelete(): void { - this.closeDeletePopover(); - } - - // Method to close the delete confirmation popover - closeDeletePopover(): void { - const deletePopover = document.getElementById('deletePopover'); - if (deletePopover) { - deletePopover.hidePopover(); - } - this.deckToDelete = null; - } - - // Method to open the rename popover - openRenamePopover(deck: Deck): void { - this.deckToRename = deck; - const renamePopover = document.getElementById('renamePopover'); - if (renamePopover) { - renamePopover.showPopover(); - // Set the input value to the current deck name and select it - setTimeout(() => { - const newDeckNameInput = document.getElementById('newDeckNameInput') as HTMLInputElement; - if (newDeckNameInput) { - newDeckNameInput.value = deck.name; - newDeckNameInput.select(); - } - }, 0); - } - } - - // Method to confirm the renaming of a deck - confirmRename(): void { - const newDeckNameInput = document.getElementById('newDeckNameInput') as HTMLInputElement; - if (newDeckNameInput && this.deckToRename) { - const newDeckName = newDeckNameInput.value.trim(); - if (newDeckName && newDeckName !== this.deckToRename.name) { - this.deckService.renameDeck(this.deckToRename.name, newDeckName).subscribe({ - next: () => { - // Aktualisiere den Namen im activeDeck, falls dieser der umbenannte Deck ist - if (this.activeDeck && this.activeDeck.name === this.deckToRename?.name) { - this.activeDeck.name = newDeckName; - } - this.loadDecks(); // Lade die Decks neu, um die Liste zu aktualisieren - this.closeRenamePopover(); - }, - error: err => console.error('Error renaming deck', err), - }); - } - } - } - - // Method to cancel the renaming of a deck - cancelRename(): void { - this.closeRenamePopover(); - } - - // Method to close the rename popover - closeRenamePopover(): void { - const renamePopover = document.getElementById('renamePopover'); - if (renamePopover) { - renamePopover.hidePopover(); - } - this.deckToRename = null; - } - - // Method to delete a deck - deleteDeck(deckName: string): void { - if (!confirm(`Are you sure you want to delete the deck "${deckName}"?`)) { - return; - } + confirmDelete(deckName: string): void { this.deckService.deleteDeck(deckName).subscribe({ - next: () => this.loadDecks(), + next: () => { + this.loadDecks(); + this.activeDeck = this.decks.length > 0 ? this.decks[0] : null; + }, error: err => console.error('Error deleting deck', err), }); } - // Method to rename a deck - renameDeck(deck: Deck): void { - const newDeckName = prompt('Enter the new name for the deck:', deck.name); - if (newDeckName && newDeckName !== deck.name) { - this.deckService.renameDeck(deck.name, newDeckName).subscribe({ - next: () => this.loadDecks(), + // Method to open the rename popover + openRenamePopover(deck: Deck): void { + this.popoverService.showWithInput({ + title: 'Rename Deck', + message: 'Enter the new name for the deck:', + confirmText: 'Rename', + inputValue: deck.name, + onConfirm: (inputValue?: string) => this.confirmRename(deck, inputValue), + }); + } + + // Method to confirm the renaming of a deck + confirmRename(deck: Deck, newName?: string): void { + if (newName && newName.trim() !== '' && newName !== deck.name) { + this.deckService.renameDeck(deck.name, newName).subscribe({ + next: () => { + if (this.activeDeck?.name === deck.name) { + this.activeDeck.name = newName; + } + this.loadDecks(); + }, error: err => console.error('Error renaming deck', err), }); + } else { + // Optional: Handle ungültigen neuen Namen + console.warn('Invalid new deck name.'); } } - // Method to delete an image from a deck + // Delete-Image Methoden ersetzen deleteImage(deck: Deck, image: DeckImage): void { - if (!confirm(`Are you sure you want to delete the image "${image.name}"?`)) { - return; - } + this.popoverService.show({ + title: 'Delete Image', + message: `Are you sure you want to delete the image ${image.name}?`, + confirmText: 'Delete', + onConfirm: () => this.confirmImageDelete(deck, image), + }); + } + confirmImageDelete(deck: Deck, image: DeckImage): void { const imageId = image.id; this.deckService.deleteImage(imageId).subscribe({ @@ -211,7 +156,7 @@ export class DeckListComponent implements OnInit { this.loadDecks(); if (this.activeDeck) { this.activeDeck.images = this.activeDeck.images.filter(img => img.id !== imageId); - this.cdr.detectChanges(); // Erzwingt einen UI-Update + this.cdr.detectChanges(); } }, error: err => console.error('Error deleting image', err), @@ -416,6 +361,6 @@ export class DeckListComponent implements OnInit { // Methode zur Berechnung der Anzahl der zu bearbeitenden Wörter getWordsToReview(deck: Deck): number { const nextTraining = this.getNextTrainingDate(deck); - return deck.images.flatMap(image => image.boxes.filter(box => box.due && new Date(box.due * 86400000) <= new Date(nextTraining))).length; + return deck.images.flatMap(image => image.boxes.filter(box => (box.due && new Date(box.due * 86400000) <= new Date(nextTraining)) || !box.due)).length; } } diff --git a/src/app/edit-image-modal/edit-image-modal.component.ts b/src/app/edit-image-modal/edit-image-modal.component.ts index 321dbb3..adc0ca4 100644 --- a/src/app/edit-image-modal/edit-image-modal.component.ts +++ b/src/app/edit-image-modal/edit-image-modal.component.ts @@ -1,22 +1,23 @@ // src/app/edit-image-modal.component.ts -import { Component, Input, Output, EventEmitter, AfterViewInit, ViewChild, ElementRef, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnDestroy, Output, ViewChild } from '@angular/core'; import { fabric } from 'fabric'; import { Modal } from 'flowbite'; -import { DeckImage, DeckService, OcrResult } from '../deck.service'; +import { DeckImage, DeckService } from '../deck.service'; +import { PopoverService } from '../services/popover.service'; @Component({ selector: 'app-edit-image-modal', templateUrl: './edit-image-modal.component.html', standalone: true, - imports: [CommonModule] + imports: [CommonModule], }) export class EditImageModalComponent implements AfterViewInit, OnDestroy { // Constant for box color private readonly BOX_COLOR = 'rgba(255, 0, 0, 0.3)'; // Red with transparency @Input() deckName: string = ''; - @Input() imageData: { imageSrc: string | ArrayBuffer | null, deckImage: DeckImage | null } = { imageSrc: null, deckImage: null }; + @Input() imageData: { imageSrc: string | ArrayBuffer | null; deckImage: DeckImage | null } = { imageSrc: null, deckImage: null }; @Output() imageSaved = new EventEmitter(); @Output() closed = new EventEmitter(); @ViewChild('editImageModal') modalElement!: ElementRef; @@ -32,13 +33,13 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy { private keyDownHandler!: (e: KeyboardEvent) => void; modal: any; - constructor(private deckService: DeckService) { } + constructor(private deckService: DeckService, private popoverService: PopoverService) {} async ngAfterViewInit() { this.modal = new Modal(this.modalElement.nativeElement, { onHide: () => { this.closed.emit(); - } + }, }); this.maxCanvasWidth = window.innerWidth * 0.6; @@ -75,14 +76,14 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy { return new Promise((resolve, reject) => { fabric.Image.fromURL( url, - (img) => { + img => { resolve(img); }, { crossOrigin: 'anonymous', originX: 'left', originY: 'top', - } + }, ); }); } @@ -122,7 +123,6 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy { // Add boxes this.imageData.deckImage?.boxes.forEach(box => { - const rect = new fabric.Rect({ left: box.x1 * scaleFactor, top: box.y1 * scaleFactor, @@ -157,7 +157,6 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy { this.updateBoxCoordinates(); // this.detectedText = ocrResults.map(result => result.text).join('\n'); - } catch (error) { console.error('Error processing image:', error); } @@ -198,7 +197,7 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy { x1: Math.round(x1), x2: Math.round(x2), y1: Math.round(y1), - y2: Math.round(y2) + y2: Math.round(y2), }); }); @@ -265,11 +264,14 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy { this.imageSaved.emit(); this.closeModal(); }, - error: (err) => { + error: err => { console.error('Error saving image:', err); - alert('Error saving image.'); + this.popoverService.show({ + title: 'Error', + message: 'Error saving image.', + }); this.closeModal(); - } + }, }); } -} \ No newline at end of file +} diff --git a/src/app/move-image-modal/move-image-modal.component.ts b/src/app/move-image-modal/move-image-modal.component.ts index 2af29f1..c7ea156 100644 --- a/src/app/move-image-modal/move-image-modal.component.ts +++ b/src/app/move-image-modal/move-image-modal.component.ts @@ -1,13 +1,14 @@ -import { Component, Input, Output, EventEmitter } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { DeckImage, Deck, DeckService } from '../deck.service'; +import { Deck, DeckImage, DeckService } from '../deck.service'; +import { PopoverService } from '../services/popover.service'; @Component({ selector: 'app-move-image-modal', templateUrl: './move-image-modal.component.html', standalone: true, - imports: [CommonModule, FormsModule] + imports: [CommonModule, FormsModule], }) export class MoveImageModalComponent { @Input() image!: DeckImage; @@ -18,7 +19,7 @@ export class MoveImageModalComponent { selectedDeckId: number | null = null; - constructor(private deckService: DeckService) { } + constructor(private deckService: DeckService, private popoverService: PopoverService) {} moveImage(): void { if (this.selectedDeckId === null) { @@ -30,14 +31,17 @@ export class MoveImageModalComponent { this.moveCompleted.emit(); this.close(); }, - error: (err) => { + error: err => { console.error('Error moving image:', err); - alert('Error moving image.'); - } + this.popoverService.show({ + title: 'Error', + message: 'Error moving image.', + }); + }, }); } close(): void { this.closed.emit(); } -} \ No newline at end of file +} diff --git a/src/app/services/popover.service.ts b/src/app/services/popover.service.ts new file mode 100644 index 0000000..49700a7 --- /dev/null +++ b/src/app/services/popover.service.ts @@ -0,0 +1,34 @@ +// popover.service.ts +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Injectable({ providedIn: 'root' }) +export class PopoverService { + private showPopoverSource = new Subject<{ + title: string; + message: string; + showInput: boolean; + inputValue: string; + confirmText: string; + onConfirm: () => void; + onCancel?: () => void; + }>(); + + popoverState$ = this.showPopoverSource.asObservable(); + + show(options: { title: string; message: string; confirmText?: string; onConfirm?: (inputValue?: string) => void; onCancel?: () => void }) { + this.showPopoverSource.next({ + showInput: false, + inputValue: null, + confirmText: 'Ok', + onConfirm: (inputValue?: string) => {}, + ...options, + }); + } + showWithInput(options: { title: string; message: string; confirmText: string; inputValue: string; onConfirm: (inputValue?: string) => void; onCancel?: () => void }) { + this.showPopoverSource.next({ + showInput: true, + ...options, + }); + } +} diff --git a/src/app/training/training.component.ts b/src/app/training/training.component.ts index 0888223..ced51f8 100644 --- a/src/app/training/training.component.ts +++ b/src/app/training/training.component.ts @@ -2,25 +2,26 @@ import { CommonModule } from '@angular/common'; import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { lastValueFrom } from 'rxjs'; import { Box, Deck, DeckImage, DeckService } from '../deck.service'; +import { PopoverService } from '../services/popover.service'; const LEARNING_STEPS = { - AGAIN: 1, // 1 minute - GOOD: 10, // 10 minutes - GRADUATION: 1440 // 1 day (in minutes) + AGAIN: 1, // 1 minute + GOOD: 10, // 10 minutes + GRADUATION: 1440, // 1 day (in minutes) }; const FACTOR_CHANGES = { - AGAIN: 0.85, // Reduce factor by 15% - EASY: 1.15, // Increase factor by 15% - MIN: 1.3, // Minimum factor allowed - MAX: 2.9 // Maximum factor allowed + AGAIN: 0.85, // Reduce factor by 15% + EASY: 1.15, // Increase factor by 15% + MIN: 1.3, // Minimum factor allowed + MAX: 2.9, // Maximum factor allowed }; const EASY_INTERVAL = 4 * 1440; // 4 days in minutes @Component({ selector: 'app-training', templateUrl: './training.component.html', standalone: true, - imports: [CommonModule] + imports: [CommonModule], }) export class TrainingComponent implements OnInit { @Input() deck!: Deck; @@ -37,7 +38,7 @@ export class TrainingComponent implements OnInit { isShowingBox: boolean = false; isTrainingFinished: boolean = false; - constructor(private deckService: DeckService) { } + constructor(private deckService: DeckService, private popoverService: PopoverService) {} ngOnInit(): void { // Initialization was done in ngAfterViewInit @@ -48,7 +49,10 @@ export class TrainingComponent implements OnInit { if (this.deck && this.deck.images.length > 0) { this.loadImage(this.currentImageIndex); } else { - alert('No deck or images available.'); + this.popoverService.show({ + title: 'Information', + message: 'No deck or images available.', + }); this.close.emit(); } } @@ -135,7 +139,7 @@ export class TrainingComponent implements OnInit { this.boxesToReview.forEach((box, index) => { ctx.beginPath(); ctx.rect(box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1); - if (this.currentBoxIndex === index && this.isShowingBox || (box.due && box.due - this.getTodayInDays() > 0)) { + if ((this.currentBoxIndex === index && this.isShowingBox) || (box.due && box.due - this.getTodayInDays() > 0)) { // Box is currently revealed, no overlay return; } else if (this.currentBoxIndex === index && !this.isShowingBox) { @@ -154,7 +158,10 @@ export class TrainingComponent implements OnInit { img.onerror = () => { console.error('Error loading image for canvas.'); - alert('Error loading image for canvas.'); + this.popoverService.show({ + title: 'Error', + message: 'Error loading image for canvas.', + }); this.close.emit(); }; } @@ -165,7 +172,8 @@ export class TrainingComponent implements OnInit { * @returns The shuffled array */ shuffleArray(array: T[]): T[] { - let currentIndex = array.length, randomIndex; + let currentIndex = array.length, + randomIndex; // While there are elements to shuffle while (currentIndex !== 0) { @@ -174,8 +182,7 @@ export class TrainingComponent implements OnInit { currentIndex--; // Swap it with the current element - [array[currentIndex], array[randomIndex]] = [ - array[randomIndex], array[currentIndex]]; + [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]]; } return array; @@ -257,7 +264,10 @@ export class TrainingComponent implements OnInit { await lastValueFrom(this.deckService.updateBox(box)); } catch (error) { console.error('Error updating box:', error); - alert('Error updating box.'); + this.popoverService.show({ + title: 'Error', + message: 'Error updating box.', + }); } } @@ -267,33 +277,36 @@ export class TrainingComponent implements OnInit { * @param action The action ('again', 'good', 'easy') * @returns An object with the new values for ivl, factor, reps, and lapses */ - calculateNewInterval(box: Box, action: 'again' | 'good' | 'easy'): { - newIvl: number, - newFactor: number, - newReps: number, - newLapses: number, - newIsGraduated: boolean + calculateNewInterval( + box: Box, + action: 'again' | 'good' | 'easy', + ): { + newIvl: number; + newFactor: number; + newReps: number; + newLapses: number; + newIsGraduated: boolean; } { const LEARNING_STEPS = { - AGAIN: 1, // 1 minute - GOOD: 10, // 10 minutes - GRADUATION: 1440 // 1 day (1440 minutes) + AGAIN: 1, // 1 minute + GOOD: 10, // 10 minutes + GRADUATION: 1440, // 1 day (1440 minutes) }; - - const EASY_BONUS = 1.3; // Zusätzlicher Easy-Multiplikator -/* const FACTOR_CHANGES = { + + const EASY_BONUS = 1.3; // Zusätzlicher Easy-Multiplikator + /* const FACTOR_CHANGES = { MIN: 1.3, MAX: 2.5, AGAIN: 0.85, EASY: 1.15 }; */ - + let newIvl = box.ivl || 0; let newFactor = box.factor || 2.5; let newReps = box.reps || 0; let newLapses = box.lapses || 0; let newIsGraduated = box.isGraduated || false; - + if (action === 'again') { newLapses++; newReps = 0; @@ -302,13 +315,13 @@ export class TrainingComponent implements OnInit { newFactor = Math.max(FACTOR_CHANGES.MIN, newFactor * FACTOR_CHANGES.AGAIN); return { newIvl, newFactor, newReps, newLapses, newIsGraduated }; } - + if (action === 'easy') { newReps++; - + // Faktor zuerst aktualisieren const updatedFactor = Math.min(FACTOR_CHANGES.MAX, newFactor * FACTOR_CHANGES.EASY); - + if (!newIsGraduated) { // Direkte Graduierung mit Easy newIsGraduated = true; @@ -317,14 +330,14 @@ export class TrainingComponent implements OnInit { // Anki-Formel für Easy in der Review-Phase newIvl = Math.round((newIvl + LEARNING_STEPS.GRADUATION / 2) * updatedFactor * EASY_BONUS); } - + newFactor = updatedFactor; return { newIvl, newFactor, newReps, newLapses, newIsGraduated }; } - + // Handle 'good' action newReps++; - + if (!newIsGraduated) { if (newReps === 1) { newIvl = LEARNING_STEPS.GOOD; // 10 Minuten @@ -336,7 +349,7 @@ export class TrainingComponent implements OnInit { // Standard-SR-Formel newIvl = Math.round(newIvl * newFactor); } - + return { newIvl, newFactor, newReps, newLapses, newIsGraduated }; } @@ -347,8 +360,7 @@ export class TrainingComponent implements OnInit { * @returns The next interval as a string (e.g., "10 min", "2 d") */ getNextInterval(box: Box | null, action: 'again' | 'good' | 'easy'): string { - if (!box) - return ''; + if (!box) return ''; const { newIvl } = this.calculateNewInterval(box, action); @@ -407,7 +419,10 @@ export class TrainingComponent implements OnInit { this.currentImageIndex++; this.loadImage(this.currentImageIndex); } else { - alert('This is the last image in the deck.'); + this.popoverService.show({ + title: 'Information', + message: 'This is the last image in the deck.', + }); } } @@ -416,7 +431,10 @@ export class TrainingComponent implements OnInit { */ endTraining(): void { this.isTrainingFinished = true; - alert(`Training completed!`); + this.popoverService.show({ + title: 'Information', + message: 'Training completed!', + }); this.close.emit(); } @@ -424,9 +442,12 @@ export class TrainingComponent implements OnInit { * Asks the user if they want to end the training and closes it if confirmed. */ closeTraining(): void { - if (confirm('Do you really want to end the training?')) { - this.close.emit(); - } + this.popoverService.show({ + title: 'End Training', + message: 'Do you really want to end the training?', + confirmText: 'End Training', + onConfirm: (inputValue?: string) => this.close.emit(), + }); } /** @@ -435,4 +456,4 @@ export class TrainingComponent implements OnInit { get progress(): string { return `Image ${this.currentImageIndex + 1} of ${this.deck.images.length}`; } -} \ No newline at end of file +}