import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
interface SessionDetail {
session: { code: string; status: string; current_round: number };
round_question: { id: number; prompt: string; answers: Array<{ text: string }> } | null;
}
@Component({
selector: 'app-player-shell',
standalone: true,
imports: [CommonModule, FormsModule],
template: `
Player SPA gameplay flow
Status: {{ session.session.status }}
Prompt: {{ session.round_question.prompt }}
{{ error }}
{{ submitError.message }}
`,
})
export class PlayerShellComponent {
sessionCode = '';
nickname = '';
playerId = 0;
sessionToken = '';
lieText = '';
selectedGuess = '';
loading = false;
error = '';
submitError: { kind: 'lie' | 'guess'; message: string } | null = null;
session: SessionDetail | null = null;
private normalizeCode(value: string): string {
return value.trim().toUpperCase();
}
private async request(path: string, method: 'GET' | 'POST', payload?: unknown): Promise {
const response = await fetch(path, {
method,
headers: {
Accept: 'application/json',
...(payload === undefined ? {} : { 'Content-Type': 'application/json' }),
},
...(payload === undefined ? {} : { body: JSON.stringify(payload) }),
credentials: 'same-origin',
});
const body = await response.json().catch(() => ({}));
if (!response.ok) {
throw new Error((body as { error?: string }).error ?? `HTTP ${response.status}`);
}
return body as T;
}
async refreshSession(): Promise {
this.loading = true;
this.error = '';
try {
const code = this.normalizeCode(this.sessionCode);
this.session = await this.request(`/lobby/sessions/${encodeURIComponent(code)}`, 'GET');
this.sessionCode = this.session.session.code;
if (this.session.session.status !== 'guess') {
this.selectedGuess = '';
}
} catch (error) {
this.error = `Session refresh failed: ${(error as Error).message}`;
} finally {
this.loading = false;
}
}
async joinSession(): Promise {
this.loading = true;
this.error = '';
try {
const payload = await this.request<{
player: { id: number; session_token: string };
session: { code: string };
}>('/lobby/sessions/join', 'POST', {
code: this.normalizeCode(this.sessionCode),
nickname: this.nickname.trim(),
});
this.playerId = payload.player.id;
this.sessionToken = payload.player.session_token;
this.sessionCode = payload.session.code;
await this.refreshSession();
} catch (error) {
this.error = `Join failed: ${(error as Error).message}`;
} finally {
this.loading = false;
}
}
async submitLie(): Promise {
if (!this.session?.round_question?.id) {
return;
}
this.loading = true;
this.submitError = null;
try {
await this.request(
`/lobby/sessions/${encodeURIComponent(this.normalizeCode(this.sessionCode))}/questions/${this.session.round_question.id}/lies/submit`,
'POST',
{
player_id: this.playerId,
session_token: this.sessionToken,
text: this.lieText,
}
);
await this.refreshSession();
} catch (error) {
this.submitError = { kind: 'lie', message: `Lie submit failed: ${(error as Error).message}` };
} finally {
this.loading = false;
}
}
async submitGuess(): Promise {
if (!this.session?.round_question?.id || !this.selectedGuess) {
return;
}
this.loading = true;
this.submitError = null;
try {
await this.request(
`/lobby/sessions/${encodeURIComponent(this.normalizeCode(this.sessionCode))}/questions/${this.session.round_question.id}/guesses/submit`,
'POST',
{
player_id: this.playerId,
session_token: this.sessionToken,
selected_text: this.selectedGuess,
}
);
await this.refreshSession();
} catch (error) {
this.submitError = { kind: 'guess', message: `Guess submit failed: ${(error as Error).message}` };
} finally {
this.loading = false;
}
}
}