88 lines
2.6 KiB
TypeScript
88 lines
2.6 KiB
TypeScript
import type { ApiClient } from '../api/client';
|
|
import type { SessionDetailResponse } from '../api/types';
|
|
|
|
export type AsyncState = 'idle' | 'loading' | 'success' | 'error';
|
|
|
|
export interface VerticalSliceState {
|
|
sessionCode: string;
|
|
session: SessionDetailResponse | null;
|
|
joinState: AsyncState;
|
|
startRoundState: AsyncState;
|
|
loadingSession: boolean;
|
|
errorMessage: string | null;
|
|
}
|
|
|
|
export interface VerticalSliceController {
|
|
getState(): VerticalSliceState;
|
|
hydrateLobby(sessionCode: string): Promise<VerticalSliceState>;
|
|
joinLobby(sessionCode: string, nickname: string): Promise<VerticalSliceState>;
|
|
startRound(sessionCode: string, categorySlug: string): Promise<VerticalSliceState>;
|
|
}
|
|
|
|
export function createVerticalSliceController(api: ApiClient): VerticalSliceController {
|
|
const state: VerticalSliceState = {
|
|
sessionCode: '',
|
|
session: null,
|
|
joinState: 'idle',
|
|
startRoundState: 'idle',
|
|
loadingSession: false,
|
|
errorMessage: null
|
|
};
|
|
|
|
const normalizeCode = (value: string): string => value.trim().toUpperCase();
|
|
|
|
async function hydrateLobby(sessionCode: string): Promise<VerticalSliceState> {
|
|
state.loadingSession = true;
|
|
state.errorMessage = null;
|
|
state.sessionCode = normalizeCode(sessionCode);
|
|
|
|
const result = await api.getSession(state.sessionCode);
|
|
state.loadingSession = false;
|
|
|
|
if (!result.ok) {
|
|
state.errorMessage = 'Kunne ikke hente lobby-status.';
|
|
return { ...state };
|
|
}
|
|
|
|
state.session = result.data;
|
|
return { ...state };
|
|
}
|
|
|
|
async function joinLobby(sessionCode: string, nickname: string): Promise<VerticalSliceState> {
|
|
state.joinState = 'loading';
|
|
state.errorMessage = null;
|
|
|
|
const join = await api.joinSession({ code: sessionCode, nickname });
|
|
if (!join.ok) {
|
|
state.joinState = 'error';
|
|
state.errorMessage = 'Join fejlede. Tjek kode eller nickname og prøv igen.';
|
|
return { ...state };
|
|
}
|
|
|
|
state.joinState = 'success';
|
|
return hydrateLobby(sessionCode);
|
|
}
|
|
|
|
async function startRound(sessionCode: string, categorySlug: string): Promise<VerticalSliceState> {
|
|
state.startRoundState = 'loading';
|
|
state.errorMessage = null;
|
|
|
|
const start = await api.startRound(sessionCode, { category_slug: categorySlug });
|
|
if (!start.ok) {
|
|
state.startRoundState = 'error';
|
|
state.errorMessage = 'Kunne ikke starte runden. Opdatér lobbyen og prøv igen.';
|
|
return { ...state };
|
|
}
|
|
|
|
state.startRoundState = 'success';
|
|
return hydrateLobby(sessionCode);
|
|
}
|
|
|
|
return {
|
|
getState: () => ({ ...state }),
|
|
hydrateLobby,
|
|
joinLobby,
|
|
startRound
|
|
};
|
|
}
|