import {Component, CUSTOM_ELEMENTS_SCHEMA, inject} from '@angular/core'; import {AsyncPipe, DatePipe} from '@angular/common'; import {GuestShowDataService} from './guest-show-data.service'; import {ActivatedRoute} from '@angular/router'; import {catchError, map, switchMap} from 'rxjs/operators'; import {Song} from '../songs/services/song'; import {SongTextComponent} from '../../widget-modules/components/song-text/song-text.component'; import {ShowTypePipe} from '../../widget-modules/pipes/show-type-translater/show-type.pipe'; import {concat, from, Observable, of} from 'rxjs'; import {GuestShow} from './guest-show'; import {ensureSwiperElement} from '../../services/swiper-element'; @Component({ selector: 'app-guest', templateUrl: './guest.component.html', styleUrls: ['./guest.component.less'], schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [SongTextComponent, AsyncPipe, DatePipe, ShowTypePipe], }) export class GuestComponent { private currentRoute = inject(ActivatedRoute); private service = inject(GuestShowDataService); public constructor() { void ensureSwiperElement(); } public showState$: Observable = this.currentRoute.params.pipe( map(param => param.id as string), switchMap(id => concat( of({status: 'loading'}), from(this.service.read(id)).pipe( switchMap(show => { const normalizedShow = this.normalizeShow(show); if (!normalizedShow) { return of({status: 'not-found'}); } return concat( of({status: 'loaded', show: normalizedShow}), this.service.read$(id).pipe( map(liveShow => this.normalizeShow(liveShow)), map(liveShow => (liveShow ? ({status: 'loaded', show: liveShow} as GuestShowState) : ({status: 'not-found'} as GuestShowState))), catchError(() => of({status: 'error', message: 'Live-Aktualisierung fehlgeschlagen.'})) ) ); }), catchError(() => of({status: 'error', message: 'Gastansicht konnte nicht geladen werden.'})) ) ) ) ); public trackBy = (index: number, show: Song) => show.id; private normalizeShow(show: GuestShow | null): GuestShowView | null { if (!show) { return null; } return { ...show, date: this.toDate(show.date), updatedAt: this.toDate(show.updatedAt ?? null), songs: Array.isArray(show.songs) ? show.songs : [], }; } private toDate(value: unknown): Date | null { if (value instanceof Date) { return value; } if (typeof value === 'object' && value !== null) { if (this.hasToDate(value)) { return value.toDate(); } if ('seconds' in value && typeof value.seconds === 'number') { return new Date(value.seconds * 1000); } } if (typeof value === 'string' || typeof value === 'number') { const parsedDate = new Date(value); return Number.isNaN(parsedDate.getTime()) ? null : parsedDate; } return null; } private hasToDate(value: object): value is FirestoreDateLike { return 'toDate' in value && typeof value.toDate === 'function'; } } interface GuestShowView extends Omit { date: Date | null; updatedAt: Date | null; } type GuestShowState = {status: 'loading'} | {status: 'not-found'} | {status: 'error'; message: string} | {status: 'loaded'; show: GuestShowView}; type FirestoreDateLike = { toDate: () => Date; };