160 lines
5.9 KiB
TypeScript
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();
|
|
}
|
|
}
|