peer count for all games
This commit is contained in:
@@ -21,6 +21,8 @@ enum InstallStatus {
|
||||
|
||||
type StatusLevel = 'info' | 'error';
|
||||
|
||||
type GameFilter = 'all' | 'available' | 'installed';
|
||||
|
||||
interface Game {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -33,15 +35,29 @@ interface Game {
|
||||
local_version?: string;
|
||||
status_message?: string;
|
||||
status_level?: StatusLevel;
|
||||
peer_count: number;
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
const [gameItems, setGameItems] = useState<Game[]>([]);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [gameDir, setGameDir] = useState('');
|
||||
const [currentFilter, setCurrentFilter] = useState<GameFilter>('all');
|
||||
const checkingPeersTimeouts = useRef<Record<string, ReturnType<typeof setTimeout>>>({});
|
||||
|
||||
const filteredGames = gameItems.filter(item =>
|
||||
const getFilteredGames = (games: Game[], filter: GameFilter): Game[] => {
|
||||
switch (filter) {
|
||||
case 'available':
|
||||
return games.filter(game => game.peer_count > 0);
|
||||
case 'installed':
|
||||
return games.filter(game => game.installed);
|
||||
case 'all':
|
||||
default:
|
||||
return games;
|
||||
}
|
||||
};
|
||||
|
||||
const filteredAndSearchedGames = getFilteredGames(gameItems, currentFilter).filter(item =>
|
||||
item.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
@@ -211,6 +227,7 @@ const App = () => {
|
||||
install_status: installStatus,
|
||||
status_message: previous?.status_message,
|
||||
status_level: previous?.status_level,
|
||||
peer_count: game.peer_count ?? 0, // Ensure peer_count is always set
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -381,20 +398,42 @@ const App = () => {
|
||||
<h1 className="align-center">SoftLAN Launcher</h1>
|
||||
<div className="main-header">
|
||||
{gameDir ? (
|
||||
<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="filter-container">
|
||||
<button
|
||||
className={`filter-button ${currentFilter === 'all' ? 'active' : ''}`}
|
||||
onClick={() => setCurrentFilter('all')}
|
||||
>
|
||||
All Games
|
||||
</button>
|
||||
<button
|
||||
className={`filter-button ${currentFilter === 'available' ? 'active' : ''}`}
|
||||
onClick={() => setCurrentFilter('available')}
|
||||
>
|
||||
Available
|
||||
</button>
|
||||
<button
|
||||
className={`filter-button ${currentFilter === 'installed' ? 'active' : ''}`}
|
||||
onClick={() => setCurrentFilter('installed')}
|
||||
>
|
||||
Installed
|
||||
</button>
|
||||
</div>
|
||||
<div className="settings-container">
|
||||
<button onClick={dialogGameDir} className="settings-button">Set Game Directory</button>
|
||||
<span className="settings-text">{gameDir}</span>
|
||||
<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>
|
||||
) : (
|
||||
@@ -410,16 +449,16 @@ const App = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-container">
|
||||
{gameDir && filteredGames.length === 0 && gameItems.length === 0 ? (
|
||||
{gameDir && filteredAndSearchedGames.length === 0 && gameItems.length === 0 ? (
|
||||
<div className="no-games-message">
|
||||
Scanning for games in your directory...
|
||||
</div>
|
||||
) : gameDir && filteredGames.length === 0 && gameItems.length > 0 ? (
|
||||
) : gameDir && filteredAndSearchedGames.length === 0 && gameItems.length > 0 ? (
|
||||
<div className="no-games-message">
|
||||
No games found matching your search.
|
||||
No games found matching your search and filters.
|
||||
</div>
|
||||
) : null}
|
||||
{filteredGames.map((item) => {
|
||||
{filteredAndSearchedGames.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)}`;
|
||||
@@ -454,7 +493,16 @@ const App = () => {
|
||||
: 'Play'}
|
||||
</div>
|
||||
<div className={`item-info${item.status_level ? ` ${item.status_level}` : ''}`}>
|
||||
{item.status_message ?? ''}
|
||||
<div className="status-left">
|
||||
{item.status_message && item.peer_count === 0 && !item.installed ? item.status_message : ''}
|
||||
</div>
|
||||
<div className="status-right">
|
||||
{item.peer_count > 0 && (
|
||||
<span className="peer-count">
|
||||
👥 {item.peer_count}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user