replace alerts und confirms with modals #15

This commit is contained in:
Your Name 2025-01-28 16:33:34 +01:00
parent 47b393e134
commit 2f35648264
8 changed files with 274 additions and 188 deletions

View File

@ -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: `
<div class="fixed inset-0 bg-gray-500 bg-opacity-50 flex items-center justify-center z-50" *ngIf="visible">
<div class="bg-white rounded-lg p-6 max-w-xs w-full shadow-lg border border-gray-200">
<h3 class="text-lg font-semibold mb-4">{{ title }}</h3>
<p *ngIf="message" class="mb-4">{{ message }}</p>
<input *ngIf="showInput" type="text" class="w-full p-2 border rounded mb-4 focus:ring-2 focus:ring-blue-500 focus:border-transparent" [(ngModel)]="inputValue" (keyup.enter)="onConfirm()" autofocus />
<div class="flex justify-end space-x-2">
<button (click)="onCancel()" class="px-4 py-2 rounded bg-gray-100 text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400">Cancel</button>
<button (click)="onConfirm()" class="px-4 py-2 rounded bg-blue-500 text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500">
{{ confirmText }}
</button>
</div>
</div>
</div>
`,
})
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<string>();
@Output() canceled = new EventEmitter<void>();
onConfirm() {
this.confirmed.emit(this.inputValue);
//this.reset();
}
onCancel() {
this.canceled.emit();
//this.reset();
}
private reset() {
this.visible = false;
this.inputValue = '';
}
}

View File

@ -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<void>();
@ -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,10 +43,13 @@ 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.',
});
},
});
}

View File

@ -18,7 +18,7 @@
<div class="flex flex-col space-y-1">
<div class="flex items-center space-x-2 whitespace-nowrap">
<h3 class="font-medium">{{ deck.name }}</h3>
<span class="text-gray-600">({{ deck.images.length }})</span>
<span class="text-gray-600">({{ deck.images.length }} Pics)</span>
</div>
<div class="text-sm text-gray-600">
<div>Next training: {{ getNextTrainingString(deck) }}</div>
@ -123,20 +123,40 @@
<app-move-image-modal *ngIf="imageToMove" [image]="imageToMove.image" [sourceDeck]="imageToMove.sourceDeck" [decks]="decks" (moveCompleted)="onImageMoved()" (closed)="imageToMove = null"> </app-move-image-modal>
</div>
<!-- Popover für die Löschbestätigung -->
<div id="deletePopover" popover="manual" class="popover">
<!-- <div id="deletePopover" popover="manual" class="popover">
<p>Are you sure you want to delete this deck?</p>
<div class="flex justify-end space-x-2">
<button (click)="confirmDelete()" class="bg-red-500 text-white px-4 py-2 rounded">Delete</button>
<button (click)="cancelDelete()" class="bg-gray-500 text-white px-4 py-2 rounded">Cancel</button>
</div>
</div>
</div> -->
<!-- Popover für die Eingabe des neuen Decknamens -->
<div id="renamePopover" popover="manual" class="popover">
<!-- <div id="renamePopover" popover="manual" class="popover">
<p>Enter the new name for the deck:</p>
<input type="text" id="newDeckNameInput" class="w-full p-2 border rounded mb-2" [value]="deckToRename?.name" />
<div class="flex justify-end space-x-2">
<button (click)="confirmRename()" class="bg-blue-500 text-white px-4 py-2 rounded">Rename</button>
<button (click)="cancelRename()" class="bg-gray-500 text-white px-4 py-2 rounded">Cancel</button>
</div>
</div>
</div> -->
<!-- Statt der alten Popover-Divs -->
<!-- <app-popover [visible]="showDeletePopover" title="Delete Deck" message="Are you sure you want to delete this deck?" confirmText="Delete" (confirmed)="confirmDelete()" (canceled)="cancelDelete()"></app-popover>
<app-popover
[visible]="showRenamePopover"
title="Rename Deck"
[showInput]="true"
[inputValue]="deckToRename?.name || ''"
confirmText="Rename"
(confirmed)="confirmRename($event)"
(canceled)="cancelRename()"
></app-popover>
<app-popover
[visible]="showDeleteImagePopover"
title="Delete Image"
message="Are you sure you want to delete this image?"
confirmText="Delete"
(confirmed)="confirmImageDelete()"
(canceled)="cancelImageDelete()"
></app-popover> -->

View File

@ -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({
confirmDelete(deckName: string): void {
this.deckService.deleteDeck(deckName).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);
}
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(): 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({
confirmRename(deck: Deck, newName?: string): void {
if (newName && newName.trim() !== '' && newName !== deck.name) {
this.deckService.renameDeck(deck.name, newName).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;
if (this.activeDeck?.name === deck.name) {
this.activeDeck.name = newName;
}
this.loadDecks(); // Lade die Decks neu, um die Liste zu aktualisieren
this.closeRenamePopover();
this.loadDecks();
},
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;
}
this.deckService.deleteDeck(deckName).subscribe({
next: () => this.loadDecks(),
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(),
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;
}
}

View File

@ -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<void>();
@Output() closed = new EventEmitter<void>();
@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();
}
},
});
}
}

View File

@ -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,10 +31,13 @@ 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.',
});
},
});
}

View File

@ -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,
});
}
}

View File

@ -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)
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
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<T>(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,21 +277,24 @@ 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)
GRADUATION: 1440, // 1 day (1440 minutes)
};
const EASY_BONUS = 1.3; // Zusätzlicher Easy-Multiplikator
/* const FACTOR_CHANGES = {
/* const FACTOR_CHANGES = {
MIN: 1.3,
MAX: 2.5,
AGAIN: 0.85,
@ -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(),
});
}
/**