- 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
121 lines
3.8 KiB
TypeScript
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,
|
|
};
|
|
} |