diff --git a/src/app/theater-overlay/theater-overlay.component.html b/src/app/theater-overlay/theater-overlay.component.html index 315e341..f2d7a36 100644 --- a/src/app/theater-overlay/theater-overlay.component.html +++ b/src/app/theater-overlay/theater-overlay.component.html @@ -13,7 +13,7 @@ } @else { - + } diff --git a/src/app/theater-overlay/theater-overlay.component.ts b/src/app/theater-overlay/theater-overlay.component.ts index 9050e31..5a3e55c 100644 --- a/src/app/theater-overlay/theater-overlay.component.ts +++ b/src/app/theater-overlay/theater-overlay.component.ts @@ -1,11 +1,14 @@ -import {Component, inject, OnInit} from '@angular/core'; +import {Component, DestroyRef, inject, OnDestroy, OnInit, signal} from '@angular/core'; import {HttpService} from '../http.service'; import {LoadingService} from '../loading.service'; -import {catchError, forkJoin, of, tap} from 'rxjs'; +import {catchError, filter, forkJoin, interval, of, startWith, switchMap, tap} from 'rxjs'; import {Sitzkategorie, Sitzplatz, Vorstellung} from '@infinimotion/model-frontend'; import {TheaterSeatState} from '../model/theater-seat-state.model'; import {ActivatedRoute} from '@angular/router'; import {SelectedSeatsService} from '../selected-seats.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + +const POLLING_INTERVAL_MS = 5000; @Component({ selector: 'app-theater-overlay', @@ -13,43 +16,78 @@ import {SelectedSeatsService} from '../selected-seats.service'; templateUrl: './theater-overlay.component.html', styleUrl: './theater-overlay.component.css' }) -export class TheaterOverlayComponent implements OnInit { +export class TheaterOverlayComponent implements OnInit, OnDestroy { private http = inject(HttpService); - loading = inject(LoadingService) + private route = inject(ActivatedRoute); + private destroyRef = inject(DestroyRef); + private selectedSeatService = inject(SelectedSeatsService); + + readonly loading = inject(LoadingService); showId!: number; - seatsPerRow: { seat: Sitzplatz, state: TheaterSeatState }[][] = [] + seatsPerRow = signal<{ seat: Sitzplatz, state: TheaterSeatState }[][]>([]); performance: Vorstellung | undefined; seatCategories: Sitzkategorie[] = []; - constructor(private route: ActivatedRoute, private selectedSeatService : SelectedSeatsService) {} + private isPollingEnabled = signal(true); + private isInitialLoad = signal(true); -ngOnInit() { - this.showId = Number(this.route.snapshot.paramMap.get('id')!); - this.selectedSeatService.clearSelection(); - this.selectedSeatService.setSeatSelectable(true); - this.loadPerformanceAndSeats(); -} + ngOnInit() { + this.showId = Number(this.route.snapshot.paramMap.get('id')!); + this.selectedSeatService.clearSelection(); + this.selectedSeatService.setSeatSelectable(true); -loadPerformanceAndSeats() { - this.loading.show(); + this.startAutoRefresh(); + } - forkJoin({ - performance: this.http.getPerformaceById(this.showId), - seats: this.http.getSeatsByShowId(this.showId) - }).pipe( - tap(({ performance, seats }) => { - this.performance = performance; - this.seatsPerRow = this.converter(seats); - this.loading.hide(); - }), - catchError(err => { - this.loading.showError(err); - console.error('Fehler beim Laden', err); - return of({ performance: null, seats: [] }); - }) - ).subscribe(); -} + ngOnDestroy() { + this.isPollingEnabled.set(false); + } + + private startAutoRefresh() { + interval(POLLING_INTERVAL_MS).pipe( + startWith(0), + filter(() => this.isPollingEnabled()), + filter(() => !this.selectedSeatService.committed()), + switchMap(() => this.loadPerformanceAndSeats()), + takeUntilDestroyed(this.destroyRef) + ).subscribe(); + } + + private loadPerformanceAndSeats() { + if (this.isInitialLoad()) { + this.loading.show(); + } + + return forkJoin({ + performance: this.http.getPerformaceById(this.showId), + seats: this.http.getSeatsByShowId(this.showId) + }).pipe( + tap(({ performance, seats }) => { + this.performance = performance; + this.seatsPerRow.set(this.converter(seats)); + + if (this.isInitialLoad()) { + this.loading.hide(); + this.isInitialLoad.set(false); + } + }), + catchError(err => { + if (this.isInitialLoad()) { + this.loading.showError(err); + } else { + console.warn('Fehler beim Aktualisieren der Sitze:', err); + } + + if (this.isInitialLoad()) { + this.loading.hide(); + this.isInitialLoad.set(false); + } + + return of({ performance: null, seats: { seats: [], reserved: [], booked: [] } }); + }) + ); + } converter(resp: { seats: Sitzplatz[], reserved: Sitzplatz[], booked: Sitzplatz[] }): { seat: Sitzplatz, @@ -80,6 +118,18 @@ loadPerformanceAndSeats() { rows.forEach(row => row.sort((a, b) => a.seat.position - b.seat.position)); return rows; } + + refreshSeats(): void { + this.loadPerformanceAndSeats().subscribe(); + } + + pausePolling(): void { //TODO: Ab Stepper Schritt 2 Polling pausieren + this.isPollingEnabled.set(false); + } + + resumePolling(): void { + this.isPollingEnabled.set(true); + } }