import { Component, inject, OnInit, OnDestroy, DestroyRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Router } from '@angular/router'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ChallengeService } from '../../services/challenge.service'; import { ChallengeEventService } from '../../services/challenge-event.service'; import { Challenge } from '../../models/challenge.models'; import { getErrorMessage } from '../../core/http/error-message.util'; @Component({ selector: 'app-challenges', standalone: true, imports: [CommonModule], templateUrl: './challenges.component.html', styleUrls: ['./challenges.component.css'] }) export class ChallengesComponent implements OnInit, OnDestroy { private readonly challengeService = inject(ChallengeService); private readonly challengeEventService = inject(ChallengeEventService); private readonly router = inject(Router); private readonly destroyRef = inject(DestroyRef); incomingChallenges: Challenge[] = []; outgoingChallenges: Challenge[] = []; loading = false; errorMessage = ''; private pollInterval: any = null; private readonly pollIntervalMs = 5000; // Poll every 5 seconds ngOnInit(): void { this.loadChallenges(true); // Subscribe to challenge events this.challengeEventService.getChallengeReceived$() .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.loadChallenges(); }); // Start polling for challenge updates this.startPolling(); } ngOnDestroy(): void { this.stopPolling(); } private startPolling(): void { this.pollInterval = setInterval(() => { this.loadChallenges(false); }, this.pollIntervalMs); } private stopPolling(): void { if (this.pollInterval) { clearInterval(this.pollInterval); this.pollInterval = null; } } loadChallenges(showLoader = false): void { if (showLoader) { this.loading = true; this.errorMessage = ''; } this.challengeService.listChallenges() .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: (response) => { this.incomingChallenges = response.in || response.incoming || []; this.outgoingChallenges = response.out || response.outgoing || []; if (showLoader) { this.loading = false; } }, error: (error) => { this.errorMessage = getErrorMessage(error, 'Failed to load challenges'); if (showLoader) { this.loading = false; } } }); } acceptChallenge(challenge: Challenge): void { this.challengeService.acceptChallenge(challenge.id) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: (acceptedChallenge) => { this.challengeEventService.onChallengeAccepted(acceptedChallenge); this.loadChallenges(); if (acceptedChallenge.gameId) { void this.router.navigate(['/game', acceptedChallenge.gameId]); } else { this.errorMessage = 'Challenge accepted, but no game was created.'; } }, error: (error) => { this.errorMessage = getErrorMessage(error, 'Failed to accept challenge'); } }); } declineChallenge(challenge: Challenge): void { this.challengeService.declineChallenge(challenge.id, { reason: 'generic' }) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: () => { this.challengeEventService.removeChallenge(challenge.id); this.loadChallenges(); }, error: (error) => { this.errorMessage = getErrorMessage(error, 'Failed to decline challenge'); } }); } cancelChallenge(challenge: Challenge): void { this.challengeService.cancelChallenge(challenge.id) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: () => { this.loadChallenges(); }, error: (error) => { this.errorMessage = getErrorMessage(error, 'Failed to cancel challenge'); } }); } goBack(): void { void this.router.navigate(['/']); } openGame(challenge: Challenge): void { if (!challenge.gameId) { this.errorMessage = 'Missing game id for this challenge.'; return; } void this.router.navigate(['/game', challenge.gameId]); } getTimeControlDisplay(challenge: Challenge): string { const { limit, increment } = challenge.timeControl; if (!limit || !increment) { return 'Unlimited'; } const minutes = Math.floor(limit / 60); return `${minutes}+${increment}`; } getChallengerDisplay(challenge: Challenge): string { return challenge.challenger.name; } getOpponentDisplay(challenge: Challenge): string { return challenge.destUser.name; } getExpirationInfo(challenge: Challenge): string { const expiresAt = new Date(challenge.expiresAt); const now = new Date(); const diffMs = expiresAt.getTime() - now.getTime(); if (diffMs <= 0 || challenge.status === 'expired') { return 'Expired'; } const minutes = Math.floor(diffMs / 60000); if (minutes > 60) { const hours = Math.floor(minutes / 60); return `${hours}h`; } return `${minutes}m`; } }