c02414ea40
Co-authored-by: Lala, Shahd <Shahd.Lala@sybit.de> Reviewed-on: #7
180 lines
6.0 KiB
TypeScript
180 lines
6.0 KiB
TypeScript
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`;
|
|
}
|
|
}
|