133 lines
3.8 KiB
TypeScript
133 lines
3.8 KiB
TypeScript
|
|
// 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<TitleSheetListStatus | null>(null);
|
||
|
|
|
||
|
|
// === INTERNAL SIGNALS ===
|
||
|
|
private playStartTime = signal<Date | null>(null);
|
||
|
|
private elapsedSeconds = signal<number>(0);
|
||
|
|
private intervalId : any = signal<number | null>(null);
|
||
|
|
|
||
|
|
// Keep track of the last attempted title (even if it failed)
|
||
|
|
private lastAttemptedTitle = signal<TitleSheet | null>(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);
|
||
|
|
}
|
||
|
|
}
|