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'; interface Game { id: string; name: string; description: string; size: number; thumbnail: Uint8Array; installed: boolean; } 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(() => { 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 events that update the game list 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(); }); // 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(); }; } 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_backend', {id}); console.log(`โœ… Game started, result=${result}`); } catch (error) { console.error('โŒ Error running 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

setSearchTerm(e.target.value)} className="search-input" />
{gameDir}
{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 (
runGame(item.id)}> {`${item.name}
{item.name}
{item.description.slice(0, 10)} {item.size.toString()}
{item.installed ? 'Play' : 'Install'}
); })}
); }; export default App;