// src/app/components/cued-title/cued-title.component.ts import { Component, input, computed, signal, effect } from '@angular/core'; import { DatePipe } from '@angular/common'; import { TitleCasePipe } from '@angular/common'; import { TitleSheet, TitleSheetListStatus } from '../../../models/title-sheet.model'; @Component({ selector: 'app-cued-title', standalone: true, imports: [TitleCasePipe], templateUrl: './cued-title.component.html', styleUrl: './cued-title.component.scss' }) export class CuedTitleComponent { // === INPUT SIGNALS (ANGULAR 19) === unrealStatus = input(null); // === INTERNAL SIGNALS === private playStartTime = signal(null); private elapsedSeconds = signal(0); private intervalId : any = signal(null); // Keep track of the last attempted title (even if it failed) private lastAttemptedTitle = signal(null); // === COMPUTED SIGNALS === statusClass = computed(() => { const status = this.unrealStatus(); if (!status) return 'stopped'; return status.isPlaying ? 'playing' : 'stopped'; }); hasCurrentTitle = computed(() => { const status = this.unrealStatus(); const lastAttempted = this.lastAttemptedTitle(); // Show title if: // 1. There's a current title, OR // 2. There's an error/stopped state but we have a last attempted title return status?.currentTitleSheet !== null || (lastAttempted !== null && (status?.errorMessage)); }); // Get the title to display (current or last attempted) displayedTitle = computed(() => { const status = this.unrealStatus(); // Prefer current title, fallback to last attempted title return status?.currentTitleSheet || this.lastAttemptedTitle(); }); isPlaying = computed(() => { const status = this.unrealStatus(); return status?.isPlaying || false; }); // Format du timer : MM:SS formattedTimer = computed(() => { const seconds = this.elapsedSeconds(); const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`; }); // Couleur du timer selon les règles timerColorClass = computed(() => { if (!this.isPlaying()) return 'timer-stopped'; const seconds = this.elapsedSeconds(); if (seconds < 10) return 'timer-white'; return 'timer-red'; }); constructor() { // Track the last attempted title for error display effect(() => { const status = this.unrealStatus(); if (status?.currentTitleSheet) { // Update last attempted title when a new title is set this.lastAttemptedTitle.set(status.currentTitleSheet); } else if (!status?.isPlaying && !status?.errorMessage) { // Clear last attempted title when voluntarily stopped (no error) this.lastAttemptedTitle.set(null); } }); // Effect pour gérer le démarrage/arrêt du timer effect(() => { const isPlaying = this.isPlaying(); if (isPlaying) { this.startTimer(); } else { this.stopTimer(); this.resetTimer(); } }); } ngOnDestroy(): void { this.stopTimer(); } private startTimer(): void { this.stopTimer(); this.playStartTime.set(new Date()); this.elapsedSeconds.set(0); this.intervalId = setInterval(() => { const startTime = this.playStartTime(); if (startTime) { const now = new Date(); const elapsed = Math.floor((now.getTime() - startTime.getTime()) / 1000); this.elapsedSeconds.set(elapsed); } }, 1000); } private stopTimer(): void { if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = null; } } private resetTimer(): void { this.playStartTime.set(null); this.elapsedSeconds.set(0); } }