fix: NCWF-4 Token Issues (#8)
Co-authored-by: Lala, Shahd <Shahd.Lala@sybit.de> Co-authored-by: shahdlala66 <shahd.lala66@gmail.com> Reviewed-on: #8
This commit was merged in pull request #8.
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
import { Component, DestroyRef, OnInit, inject } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { TournamentService } from '../../services/tournament.service';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { BotService } from '../../services/bot.service';
|
||||
import { Bot } from '../../models/bot.models';
|
||||
import { Tournament, TournamentResult, RoundPairings } from '../../models/tournament.models';
|
||||
import { CurrentUser } from '../../models/auth.models';
|
||||
|
||||
type StatusTab = 'started' | 'created' | 'finished';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tournaments',
|
||||
standalone: true,
|
||||
imports: [CommonModule, RouterLink, ReactiveFormsModule],
|
||||
templateUrl: './tournaments.component.html',
|
||||
styleUrl: './tournaments.component.css'
|
||||
})
|
||||
export class TournamentsComponent implements OnInit {
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly tournamentService = inject(TournamentService);
|
||||
private readonly authService = inject(AuthService);
|
||||
private readonly fb = inject(FormBuilder);
|
||||
private readonly botService = inject(BotService);
|
||||
private readonly router = inject(Router);
|
||||
|
||||
loading = true;
|
||||
tab: StatusTab = 'started';
|
||||
currentUser: CurrentUser | null = null;
|
||||
|
||||
started: Tournament[] = [];
|
||||
created: Tournament[] = [];
|
||||
finished: Tournament[] = [];
|
||||
|
||||
selectedTournament: Tournament | null = null;
|
||||
pairings: RoundPairings | null = null;
|
||||
pairingsLoading = false;
|
||||
|
||||
showCreateDialog = false;
|
||||
createForm: FormGroup = this.fb.group({
|
||||
name: ['', [Validators.required, Validators.minLength(3)]],
|
||||
nbRounds: [4, [Validators.required, Validators.min(1), Validators.max(20)]],
|
||||
clockLimitMinutes: [3, [Validators.required, Validators.min(1), Validators.max(60)]],
|
||||
clockIncrement: [0, [Validators.required, Validators.min(0), Validators.max(60)]],
|
||||
rated: [false]
|
||||
});
|
||||
createLoading = false;
|
||||
createError: string | null = null;
|
||||
|
||||
startingId: string | null = null;
|
||||
|
||||
joinDialogTournamentId: string | null = null;
|
||||
userBots: Bot[] = [];
|
||||
botsLoading = false;
|
||||
joiningBotId: string | null = null;
|
||||
joinError: string | null = null;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.authService.currentUser$
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe(u => { this.currentUser = u; });
|
||||
this.loadTournaments();
|
||||
}
|
||||
|
||||
openCreateDialog(): void {
|
||||
this.createForm.reset({ name: '', nbRounds: 4, clockLimitMinutes: 3, clockIncrement: 0, rated: false });
|
||||
this.createError = null;
|
||||
this.showCreateDialog = true;
|
||||
}
|
||||
|
||||
closeCreateDialog(): void {
|
||||
this.showCreateDialog = false;
|
||||
}
|
||||
|
||||
submitCreate(): void {
|
||||
if (this.createForm.invalid) return;
|
||||
this.createLoading = true;
|
||||
this.createError = null;
|
||||
this.tournamentService.create(this.createForm.value).subscribe({
|
||||
next: t => {
|
||||
this.createLoading = false;
|
||||
this.showCreateDialog = false;
|
||||
this.created = [t, ...this.created];
|
||||
this.tab = 'created';
|
||||
this.selectedTournament = null;
|
||||
},
|
||||
error: err => {
|
||||
this.createLoading = false;
|
||||
this.createError = err.error?.message ?? err.error?.error ?? 'Failed to create tournament.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setTab(tab: StatusTab): void {
|
||||
this.tab = tab;
|
||||
this.selectedTournament = null;
|
||||
this.pairings = null;
|
||||
}
|
||||
|
||||
selectTournament(t: Tournament): void {
|
||||
if (this.selectedTournament?.id === t.id) {
|
||||
this.selectedTournament = null;
|
||||
this.pairings = null;
|
||||
return;
|
||||
}
|
||||
this.selectedTournament = t;
|
||||
this.pairings = null;
|
||||
if (t.round > 0) {
|
||||
this.loadPairings(t.id, t.round);
|
||||
}
|
||||
}
|
||||
|
||||
get activeList(): Tournament[] {
|
||||
return this[this.tab];
|
||||
}
|
||||
|
||||
clockDisplay(t: Tournament): string {
|
||||
const min = Math.floor(t.clock.limit / 60);
|
||||
return `${min}+${t.clock.increment}`;
|
||||
}
|
||||
|
||||
rankMedal(rank: number): string {
|
||||
if (rank === 1) return '🥇';
|
||||
if (rank === 2) return '🥈';
|
||||
if (rank === 3) return '🥉';
|
||||
return `${rank}.`;
|
||||
}
|
||||
|
||||
scoreDisplay(r: TournamentResult): string {
|
||||
return r.points % 1 === 0 ? `${r.points}` : `${r.points}`;
|
||||
}
|
||||
|
||||
startTournament(event: MouseEvent, t: Tournament): void {
|
||||
event.stopPropagation();
|
||||
this.startingId = t.id;
|
||||
this.tournamentService.start(t.id).subscribe({
|
||||
next: updated => {
|
||||
this.startingId = null;
|
||||
const list = this.created.map(x => x.id === t.id ? updated : x);
|
||||
this.created = list.filter(x => x.status === 'created');
|
||||
if (!this.started.find(x => x.id === updated.id)) this.started = [updated, ...this.started];
|
||||
this.selectedTournament = updated;
|
||||
this.tab = 'started';
|
||||
},
|
||||
error: () => { this.startingId = null; }
|
||||
});
|
||||
}
|
||||
|
||||
watchGame(gameId: string): void {
|
||||
void this.router.navigate(['/game', gameId]);
|
||||
}
|
||||
|
||||
openJoinDialog(event: MouseEvent, tournamentId: string): void {
|
||||
event.stopPropagation();
|
||||
this.joinDialogTournamentId = tournamentId;
|
||||
this.joinError = null;
|
||||
this.botsLoading = true;
|
||||
this.botService.list()
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe({
|
||||
next: bots => { this.userBots = bots; this.botsLoading = false; },
|
||||
error: () => { this.botsLoading = false; }
|
||||
});
|
||||
}
|
||||
|
||||
closeJoinDialog(): void {
|
||||
this.joinDialogTournamentId = null;
|
||||
this.joiningBotId = null;
|
||||
this.joinError = null;
|
||||
}
|
||||
|
||||
joinWithBot(bot: Bot): void {
|
||||
if (!this.joinDialogTournamentId || this.joiningBotId) return;
|
||||
this.joiningBotId = bot.id;
|
||||
this.joinError = null;
|
||||
this.botService.rotateToken(bot.id).subscribe({
|
||||
next: token => {
|
||||
this.tournamentService.joinWithBotToken(this.joinDialogTournamentId!, token).subscribe({
|
||||
next: () => {
|
||||
this.joiningBotId = null;
|
||||
const tid = this.joinDialogTournamentId!;
|
||||
this.closeJoinDialog();
|
||||
this.tournamentService.get(tid)
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe(updated => {
|
||||
this.created = this.created.map(x => x.id === tid ? updated : x);
|
||||
this.started = this.started.map(x => x.id === tid ? updated : x);
|
||||
if (this.selectedTournament?.id === tid) this.selectedTournament = updated;
|
||||
});
|
||||
},
|
||||
error: err => {
|
||||
this.joiningBotId = null;
|
||||
this.joinError = err.error?.message ?? err.error?.error ?? 'Failed to join tournament.';
|
||||
}
|
||||
});
|
||||
},
|
||||
error: () => {
|
||||
this.joiningBotId = null;
|
||||
this.joinError = 'Failed to get bot token.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private loadTournaments(): void {
|
||||
this.tournamentService.list()
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe({
|
||||
next: list => {
|
||||
this.started = list.started;
|
||||
this.created = list.created;
|
||||
this.finished = list.finished;
|
||||
this.loading = false;
|
||||
if (this.started.length === 0 && this.created.length > 0) this.tab = 'created';
|
||||
else if (this.started.length === 0 && this.finished.length > 0) this.tab = 'finished';
|
||||
},
|
||||
error: () => { this.loading = false; }
|
||||
});
|
||||
}
|
||||
|
||||
private loadPairings(id: string, round: number): void {
|
||||
this.pairingsLoading = true;
|
||||
this.tournamentService.roundPairings(id, round)
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe({
|
||||
next: p => { this.pairings = p; this.pairingsLoading = false; },
|
||||
error: () => { this.pairingsLoading = false; }
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user