Files
NowChess-Frontend/src/app/components/challenge-create-dialog/challenge-create-dialog.component.ts
T
2026-05-14 20:16:36 +00:00

160 lines
5.9 KiB
TypeScript

import { Component, inject, OnInit, OnDestroy, DestroyRef, Output, EventEmitter } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { finalize } from 'rxjs';
import { ChallengeService } from '../../services/challenge.service';
import { Router } from '@angular/router';
import { getErrorMessage } from '../../core/http/error-message.util';
import { PlayerColor } from '../../models/challenge.models';
type TimeMode = 'blitz' | 'rapid' | 'classical' | 'unlimited';
interface TimePreset {
label: string;
limitSeconds: number;
incrementSeconds: number;
}
@Component({
selector: 'app-challenge-create-dialog',
standalone: true,
imports: [CommonModule, FormsModule, ReactiveFormsModule],
templateUrl: './challenge-create-dialog.component.html',
styleUrls: ['./challenge-create-dialog.component.css']
})
export class ChallengeCreateDialogComponent implements OnInit, OnDestroy {
private readonly challengeService = inject(ChallengeService);
private readonly router = inject(Router);
private readonly fb = inject(FormBuilder);
private readonly destroyRef = inject(DestroyRef);
@Output() closeChallengeDialog = new EventEmitter<void>();
form!: FormGroup;
loading = false;
errorMessage = '';
selectedTimeMode: TimeMode = 'rapid';
timePresets: Record<TimeMode, TimePreset[]> = {
blitz: [
{ label: '1+0', limitSeconds: 60, incrementSeconds: 0 },
{ label: '2+1', limitSeconds: 120, incrementSeconds: 1 },
{ label: '3+0', limitSeconds: 180, incrementSeconds: 0 },
{ label: '3+2', limitSeconds: 180, incrementSeconds: 2 },
{ label: '5+0', limitSeconds: 300, incrementSeconds: 0 }
],
rapid: [
{ label: '10+0', limitSeconds: 600, incrementSeconds: 0 },
{ label: '10+5', limitSeconds: 600, incrementSeconds: 5 },
{ label: '15+10', limitSeconds: 900, incrementSeconds: 10 },
{ label: '25+10', limitSeconds: 1500, incrementSeconds: 10 }
],
classical: [
{ label: '30+0', limitSeconds: 1800, incrementSeconds: 0 },
{ label: '30+20', limitSeconds: 1800, incrementSeconds: 20 },
{ label: '60+30', limitSeconds: 3600, incrementSeconds: 30 },
{ label: '90+30', limitSeconds: 5400, incrementSeconds: 30 }
],
unlimited: []
};
ttlOptions = [
{ label: '5 minutes', seconds: 300 },
{ label: '1 hour', seconds: 3600 },
{ label: '1 day', seconds: 86400 },
{ label: 'No expiry', seconds: 0 }
];
ngOnInit(): void {
this.initializeForm();
}
ngOnDestroy(): void {
}
private initializeForm(): void {
this.form = this.fb.group({
targetUsername: ['', [Validators.required, Validators.minLength(1)]],
color: ['random', Validators.required],
timeMode: ['rapid'],
limitMinutes: [10, [Validators.required, Validators.min(1), Validators.max(1000)]],
incrementSeconds: [5, [Validators.required, Validators.min(0), Validators.max(300)]],
ttlSeconds: [3600, Validators.required]
});
this.form.get('timeMode')?.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((mode: unknown) => {
const timeMode = mode as TimeMode;
this.selectedTimeMode = timeMode;
if (timeMode !== 'unlimited') {
const firstPreset = this.timePresets[timeMode][0];
if (firstPreset) {
this.form.patchValue({
limitMinutes: firstPreset.limitSeconds / 60,
incrementSeconds: firstPreset.incrementSeconds
});
}
}
});
}
selectPreset(preset: TimePreset): void {
this.form.patchValue({
limitMinutes: preset.limitSeconds / 60,
incrementSeconds: preset.incrementSeconds
});
}
getAvailablePresets(): TimePreset[] {
return this.timePresets[this.selectedTimeMode] || [];
}
submit(): void {
if (this.form.invalid || this.loading) {
return;
}
const targetUsername = this.form.get('targetUsername')?.value?.trim();
if (!targetUsername) {
this.errorMessage = 'Please enter a valid username';
return;
}
this.errorMessage = '';
this.loading = true;
this.form.disable();
const limitSeconds = Math.round((this.form.getRawValue().limitMinutes || 0) * 60);
const { incrementSeconds, ttlSeconds, color: rawColor } = this.form.getRawValue();
const color = (rawColor || 'random') as PlayerColor;
this.challengeService.sendChallenge(targetUsername, {
timeControl: {
limitSeconds,
incrementSeconds
},
color,
ttlSeconds: ttlSeconds > 0 ? ttlSeconds : undefined
})
.pipe(finalize(() => { this.loading = false; this.form.enable(); }))
.subscribe({
next: (challenge) => {
// Challenge sent successfully - navigate to challenges page to view status
this.form.reset();
this.closeChallengeDialog.emit();
void this.router.navigate(['/challenges']);
},
error: (error) => {
this.errorMessage = getErrorMessage(error, 'Failed to send challenge. Please try again.');
}
});
}
cancel(): void {
this.form.reset();
this.closeChallengeDialog.emit();
}
}