Files
bscscore/src/lib/state/useGameState.ts
Frank Schwenk 8a46a8a019 refactor: extract reusable library
- move reusable domain, data, state, ui code into src/lib
- update host screens to consume new library exports
- document architecture and configure path aliases
- bump astro integration dependencies for compatibility

Refs #30
2025-11-13 10:41:55 +01:00

121 lines
3.8 KiB
TypeScript

import { useState, useEffect, useCallback } from 'preact/hooks';
import type { Game, NewGameData, GameFilter } from '@lib/domain/types';
import { GameService } from '@lib/data/gameService';
export function useGameState() {
const [games, setGames] = useState<Game[]>([]);
const [filter, setFilter] = useState<GameFilter>('all');
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// Load games from IndexedDB on mount
useEffect(() => {
const loadGames = async () => {
try {
setLoading(true);
setError(null);
const savedGames = await GameService.loadGames();
setGames(savedGames);
} catch (err) {
console.error('Failed to load games:', err);
setError('Failed to load games from storage');
} finally {
setLoading(false);
}
};
loadGames();
}, []);
const addGame = useCallback(async (gameData: NewGameData): Promise<number> => {
try {
const newGame = GameService.createGame(gameData);
await GameService.saveGame(newGame);
setGames(prevGames => [newGame, ...prevGames]);
return newGame.id;
} catch (err) {
console.error('Failed to add game:', err);
setError('Failed to save new game');
throw err;
}
}, []);
const updateGame = useCallback(async (gameId: number, updatedGame: Game) => {
try {
await GameService.saveGame(updatedGame);
setGames(prevGames =>
prevGames.map(game => game.id === gameId ? updatedGame : game)
);
} catch (err) {
console.error('Failed to update game:', err);
setError('Failed to save game changes');
throw err;
}
}, []);
const deleteGame = useCallback(async (gameId: number) => {
try {
await GameService.deleteGame(gameId);
setGames(prevGames => prevGames.filter(game => game.id !== gameId));
} catch (err) {
console.error('Failed to delete game:', err);
setError('Failed to delete game');
throw err;
}
}, []);
const getGameById = useCallback((gameId: number): Game | undefined => {
return games.find(game => game.id === gameId);
}, [games]);
const getFilteredGames = useCallback(async (): Promise<Game[]> => {
try {
return await GameService.getGamesByFilter(filter);
} catch (err) {
console.error('Failed to get filtered games:', err);
setError('Failed to load filtered games');
return games.filter(game => {
if (filter === 'active') return game.status === 'active';
if (filter === 'completed') return game.status === 'completed';
return true;
});
}
}, [filter, games]);
const getPlayerNameHistory = useCallback((): string[] => {
// Extract player names from current games for immediate UI use
const nameLastUsed: Record<string, number> = {};
games.forEach(game => {
const timestamp = new Date(game.updatedAt).getTime();
if ('players' in game) {
// EndlosGame
game.players.forEach(player => {
nameLastUsed[player.name] = Math.max(nameLastUsed[player.name] || 0, timestamp);
});
} else {
// StandardGame
if (game.player1) nameLastUsed[game.player1] = Math.max(nameLastUsed[game.player1] || 0, timestamp);
if (game.player2) nameLastUsed[game.player2] = Math.max(nameLastUsed[game.player2] || 0, timestamp);
if (game.player3) nameLastUsed[game.player3] = Math.max(nameLastUsed[game.player3] || 0, timestamp);
}
});
return [...new Set(Object.keys(nameLastUsed))].sort((a, b) => nameLastUsed[b] - nameLastUsed[a]);
}, [games]);
return {
games,
filter,
setFilter,
loading,
error,
addGame,
updateGame,
deleteGame,
getGameById,
getFilteredGames,
getPlayerNameHistory,
};
}