[improve] set game dir on client -> updates Play/Install button based on games existing
This commit is contained in:
@ -102,13 +102,6 @@ h1.align-center {
|
||||
box-shadow: 0 8px 15px rgba(0, 191, 255, 0.2);
|
||||
}
|
||||
|
||||
/* .play-button:hover {
|
||||
background: linear-gradient(45deg, #09305a, #4866b9);
|
||||
/* box-shadow: 0 15px 20px rgba(0, 191, 255, 0.4); */
|
||||
/* box-shadow: 0 20px 25px rgba(0, 191, 255, 0.6), 0 0 15px rgba(0, 191, 255, 0.5);
|
||||
transform: translateY(-5px);
|
||||
} */
|
||||
|
||||
.play-button:hover {
|
||||
background: linear-gradient(45deg, #09305a, #4866b9);
|
||||
box-shadow: 0 20px 25px rgba(0, 191, 255, 0.6);
|
||||
@ -124,7 +117,6 @@ h1.align-center {
|
||||
}
|
||||
|
||||
.search-container {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
@ -150,3 +142,45 @@ h1.align-center {
|
||||
.search-input::placeholder {
|
||||
color: #8892b0;
|
||||
}
|
||||
|
||||
.search-settings-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.settings-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.settings-button {
|
||||
padding: 8px 16px;
|
||||
background: linear-gradient(45deg, #09305a, #37529c);
|
||||
color: #D5DBFE;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 8px rgba(0, 191, 255, 0.2);
|
||||
}
|
||||
|
||||
.settings-button:hover {
|
||||
background: linear-gradient(45deg, #09305a, #4866b9);
|
||||
box-shadow: 0 8px 12px rgba(0, 191, 255, 0.4);
|
||||
border: 1px solid rgba(0, 191, 255, 0.6);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.settings-text {
|
||||
color: #8892b0;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
@ -1,40 +1,78 @@
|
||||
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<Game[]>([]);
|
||||
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<string>(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');
|
||||
|
||||
let isSubscribed = true; // For tracking if component is still mounted
|
||||
|
||||
const setupEventListener = async () => {
|
||||
try {
|
||||
const unlisten = await listen('games-list-updated', (event) => {
|
||||
if (!isSubscribed) return; // Don't update state if unmounted
|
||||
|
||||
console.log('📥 Received games-list-updated event');
|
||||
// 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[];
|
||||
games.forEach(game => {
|
||||
console.log(`🎮 game: ${JSON.stringify(game.id)}`);
|
||||
});
|
||||
console.log(`🎮 ${games.length} Games received`);
|
||||
setGameItems(games);
|
||||
getInitialGameDir();
|
||||
});
|
||||
|
||||
// Initial request for games
|
||||
@ -44,8 +82,7 @@ const App = () => {
|
||||
// Cleanup function
|
||||
return () => {
|
||||
console.log('🧹 Cleaning up - removing listener');
|
||||
isSubscribed = false;
|
||||
unlisten();
|
||||
unlisten_games();
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('❌ Error in setup:', error);
|
||||
@ -57,7 +94,6 @@ const App = () => {
|
||||
// Cleanup
|
||||
return () => {
|
||||
console.log('🚫 Effect cleanup - component unmounting');
|
||||
isSubscribed = false;
|
||||
};
|
||||
}, []); // Empty dependency array means this runs once on mount
|
||||
|
||||
@ -71,20 +107,38 @@ const App = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const dialogGameDir = async () => {
|
||||
const file = await open({
|
||||
multiple: false,
|
||||
directory: true,
|
||||
});
|
||||
|
||||
if (file) {
|
||||
setGameDir(file);
|
||||
}
|
||||
};
|
||||
|
||||
// Rest of your component remains the same
|
||||
return (
|
||||
<main className="container">
|
||||
<div className="fixed-header">
|
||||
<h1 className="align-center">SoftLAN Launcher</h1>
|
||||
<div className="main-header">
|
||||
<div className="search-container">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search games..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="search-input"
|
||||
/>
|
||||
<div className="search-settings-wrapper">
|
||||
<div></div>
|
||||
<div className="search-container">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search games..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="search-input"
|
||||
/>
|
||||
</div>
|
||||
<div className="settings-container">
|
||||
<button onClick={dialogGameDir} className="settings-button">Set Game Directory</button>
|
||||
<span className="settings-text">{gameDir}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -101,7 +155,7 @@ const App = () => {
|
||||
<span className="desc-text">{item.description.slice(0, 10)}</span>
|
||||
<span className="size-text">{item.size.toString()}</span>
|
||||
</div>
|
||||
<div className="play-button">Play</div>
|
||||
<div className="play-button">{item.installed ? 'Play' : 'Install'}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
Reference in New Issue
Block a user