import { useState, useCallback, useEffect, useRef } from 'react' import { Game } from '@/types' import { invoke } from '@tauri-apps/api/core' import { listen } from '@tauri-apps/api/event' /** * Hook for managing games state and operations * Handles game loading, scanning, and updates */ export function useGames() { const [games, setGames] = useState([]) const [isLoading, setIsLoading] = useState(true) const [isInitialLoad, setIsInitialLoad] = useState(true) const [scanProgress, setScanProgress] = useState({ message: 'Initializing...', progress: 0, }) const [error, setError] = useState(null) const hasLoadedRef = useRef(false) // LoadGames function outside of the useEffect to make it reusable const loadGames = useCallback(async () => { try { setIsLoading(true) setError(null) console.log('Invoking scan_steam_games') const steamGames = await invoke('scan_steam_games') // Add platform property to match GameList component's expectation const gamesWithPlatform = steamGames.map((game) => ({ ...game, platform: 'Steam', })) console.log(`Loaded ${gamesWithPlatform.length} games`) setGames(gamesWithPlatform) setIsInitialLoad(false) // Mark initial load as complete return true } catch (error) { console.error('Error loading games:', error) setError(`Failed to load games: ${error}`) setIsInitialLoad(false) // Mark initial load as complete even on error return false } finally { setIsLoading(false) } }, []) // Setup event listeners for game updates useEffect(() => { let unlisteners: (() => void)[] = [] // Set up event listeners const setupEventListeners = async () => { try { console.log('Setting up game event listeners') // Listen for individual game updates const unlistenGameUpdated = await listen('game-updated', (event) => { console.log('Received game-updated event:', event) const updatedGame = event.payload // Update only the specific game in the state setGames((prevGames) => prevGames.map((game) => game.id === updatedGame.id ? { ...updatedGame, platform: 'Steam' } : game ) ) }) // Listen for scan progress events const unlistenScanProgress = await listen<{ message: string progress: number }>('scan-progress', (event) => { const { message, progress } = event.payload console.log('Received scan-progress event:', message, progress) // Update scan progress state setScanProgress({ message, progress, }) }) unlisteners = [unlistenGameUpdated, unlistenScanProgress] } catch (error) { console.error('Error setting up event listeners:', error) } } // Initialize event listeners and then load games. Guarded by a ref // (not isInitialLoad state) so this effect only ever runs once putting // isInitialLoad in the dep array would re-subscribe the listeners the // moment it flips to false, needlessly tearing down and recreating them. setupEventListeners().then(() => { if (!hasLoadedRef.current) { hasLoadedRef.current = true loadGames().catch(console.error) } }) // Cleanup function return () => { unlisteners.forEach((fn) => fn()) } }, [loadGames]) // Helper function to update a specific game in state const updateGame = useCallback((updatedGame: Game) => { setGames((prevGames) => prevGames.map((game) => (game.id === updatedGame.id ? updatedGame : game)) ) }, []) return { games, isLoading, isInitialLoad, scanProgress, error, loadGames, updateGame, setGames, } }