import { useEffect, useState } from 'react'; import { invoke } from '@tauri-apps/api/core'; import { listen } from '@tauri-apps/api/event'; import { open } from '@tauri-apps/plugin-dialog'; import { load } from '@tauri-apps/plugin-store'; import "./App.css"; const FILE_STORAGE = 'launcher-settings.json'; const GAME_DIR_KEY = 'game-directory'; // enum with install status enum InstallStatus { NotInstalled = 'NotInstalled', CheckingServer = 'CheckingServer', Downloading = 'Downloading', Unpacking = 'Unpacking', Installed = 'Installed', } interface Game { id: string; name: string; description: string; size: number; thumbnail: Uint8Array; installed: boolean; install_status: InstallStatus; } const App = () => { const [gameItems, setGameItems] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [gameDir, setGameDir] = useState(''); const filteredGames = gameItems.filter(item => item.name.toLowerCase().includes(searchTerm.toLowerCase()) ); const getInitialGameDir = async () => { // update game directory from storage (if exists) // only if it's not already set await new Promise(resolve => setTimeout(resolve, 1000)); const store = await load(FILE_STORAGE, { autoSave: true }); const savedGameDir = await store.get(GAME_DIR_KEY); if (savedGameDir) { setGameDir(savedGameDir); } }; useEffect(() => { // Listen for game-unpack-finished events specifically const setupUnpackListener = async () => { const unlisten = await listen('game-unpack-finished', (event) => { const game_id = event.payload as string; console.log(`๐Ÿ—ฒ game-unpack-finished ${game_id} event received`); console.log('Current gameDir in listener:', gameDir); // Add this log setGameItems(prev => prev.map(item => item.id === game_id ? {...item, install_status: InstallStatus.Installed} : item)); // Convert to string explicitly and verify it's not empty const pathString = String(gameDir); if (!pathString) { console.error('gameDir is empty before invoke!'); return; } invoke('update_game_directory', { path: pathString }) .catch(error => console.error('โŒ Error updating game directory:', error)); }); return unlisten; }; setupUnpackListener(); }, [gameDir]); useEffect(() => { if (gameDir) { // store game directory in persistent storage const updateStorage = async (game_dir: string) => { try { const store = await load(FILE_STORAGE, { autoSave: true }); await store.set(GAME_DIR_KEY, game_dir); console.info(`๐Ÿ“ฆ Storage updated with game directory: ${game_dir}`); } catch (error) { console.error('โŒ Error updating storage:', error); } }; updateStorage(gameDir); console.log(`๐Ÿ“‚ Game directory changed to: ${gameDir}`); invoke('update_game_directory', { path: gameDir }) .catch(error => console.error('โŒ Error updating game directory:', error)); } }, [gameDir]); useEffect(() => { console.log('๐Ÿ”ต Effect starting - setting up listener and requesting games'); const setupEventListener = async () => { try { // Listen for games-list-updated events const unlisten_games = await listen('games-list-updated', (event) => { console.log('๐Ÿ—ฒ Received games-list-updated event'); const games = event.payload as Game[]; console.log(`๐ŸŽฎ ${games.length} Games received`); setGameItems(games); getInitialGameDir(); }); // Listen for game-download-begin events const unlisten_game_download_begin = await listen('game-download-begin', (event) => { const game_id = event.payload as string; console.log(`๐Ÿ—ฒ game-download-begin ${game_id} event received`); setGameItems(prev => prev.map(item => item.id === game_id ? { ...item, install_status: InstallStatus.Downloading } : item)); }); // Listen for game-download-finished events const unlisten_game_download_finished = await listen('game-download-finished', (event) => { const game_id = event.payload as string; console.log(`๐Ÿ—ฒ game-download-finished ${game_id} event received`); setGameItems(prev => prev.map(item => item.id === game_id ? { ...item, install_status: InstallStatus.Unpacking } : item)); }); // Initial request for games console.log('๐Ÿ“ค Requesting initial games list'); await invoke('request_games'); // Cleanup function return () => { console.log('๐Ÿงน Cleaning up - removing listener'); unlisten_games(); unlisten_game_download_begin(); unlisten_game_download_finished(); }; } catch (error) { console.error('โŒ Error in setup:', error); } }; setupEventListener(); // Cleanup return () => { console.log('๐Ÿšซ Effect cleanup - component unmounting'); }; }, []); // Empty dependency array means this runs once on mount const runGame = async (id: string) => { console.log(`๐ŸŽฏ Running game with id=${id}`); try { const result = await invoke('run_game', { id }); console.log(`โœ… Game started, result=${result}`); } catch (error) { console.error('โŒ Error running game:', error); } }; const installGame = async (id: string) => { console.log(`๐ŸŽฏ Installing game with id=${id}`); try { const success = await invoke('install_game', { id }); if (success) { console.log(`โœ… Game install for id=${id} started...`); // update install status in gameItems for this game setGameItems(prev => prev.map(item => item.id === id ? { ...item, install_status: InstallStatus.CheckingServer } : item)); } else { // game is already being installed console.warn(`๐Ÿšง Game with id=${id} is already being installed`); } } catch (error) { console.error('โŒ Error installing game:', error); } }; const dialogGameDir = async () => { const file = await open({ multiple: false, directory: true, }); if (file) { setGameDir(file); } }; // Rest of your component remains the same return (

SoftLAN Launcher

{gameItems.length > 0 ? (
setSearchTerm(e.target.value)} className="search-input" />
{gameDir}
) : (
Waiting for connection to server...
)}
{filteredGames.map((item) => { const uint8Array = new Uint8Array(item.thumbnail); const binaryString = uint8Array.reduce((acc, byte) => acc + String.fromCharCode(byte), ''); const thumbnailUrl = `data:image/jpeg;base64,${btoa(binaryString)}`; return (
{`${item.name}
{item.name}
{item.description.slice(0, 10)} {(item.size / 1024 / 1024 / 1024).toFixed(1)} GB
item.installed ? runGame(item.id) : installGame(item.id)}> {item.installed ? 'Play' : item.install_status === InstallStatus.CheckingServer ? 'Checking server...' : item.install_status === InstallStatus.Downloading ? 'Downloading...' : item.install_status === InstallStatus.Unpacking ? 'Unpacking...' : 'Install'}
); })}
); }; export default App;