From 285256bfb8c58556b8dc1ec983e3e0b0baec56c5 Mon Sep 17 00:00:00 2001 From: Tickbase Date: Thu, 30 Apr 2026 20:58:33 +0200 Subject: [PATCH] Epic games item list and game card --- src/components/games/EpicGameItem.tsx | 119 ++++++++++++++++++++++++++ src/components/games/EpicGameList.tsx | 65 ++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 src/components/games/EpicGameItem.tsx create mode 100644 src/components/games/EpicGameList.tsx diff --git a/src/components/games/EpicGameItem.tsx b/src/components/games/EpicGameItem.tsx new file mode 100644 index 0000000..ac2e01a --- /dev/null +++ b/src/components/games/EpicGameItem.tsx @@ -0,0 +1,119 @@ +import { useState, useEffect } from 'react' +import { EpicGame } from '@/types/EpicGame' +import { ActionButton, Button } from '@/components/buttons' +import { Icon } from '@/components/icons' + +interface EpicGameItemProps { + game: EpicGame + installing?: boolean + onInstall: (game: EpicGame) => void + onUninstallScream: (game: EpicGame) => void + onUninstallKoaloader: (game: EpicGame) => void + onSettings: (game: EpicGame) => void +} + +const EpicGameItem = ({ + game, + installing, + onInstall, + onUninstallScream, + onUninstallKoaloader, + onSettings, +}: EpicGameItemProps) => { + const [imageUrl, setImageUrl] = useState(null) + const [hasError, setHasError] = useState(false) + + useEffect(() => { + if (game.box_art_url) { + setImageUrl(game.box_art_url) + } + }, [game.box_art_url]) + + const backgroundImage = + imageUrl && !hasError + ? `url(${imageUrl})` + : 'linear-gradient(135deg, #232323, #1A1A1A)' + + const anyInstalled = game.scream_installed || game.koaloader_installed + const isWorking = !!installing + + return ( +
+ {imageUrl && !hasError && ( + setHasError(true)} + /> + )} + +
+
+ Epic + {game.scream_installed && ScreamAPI} + {game.koaloader_installed && Koaloader} +
+ +
+

{game.title}

+
+ +
+ {/* Nothing installed - install button */} + {!anyInstalled && ( + { if (!isWorking) onInstall(game) }} + /> + )} + + {/* ScreamAPI installed - uninstall + settings */} + {game.scream_installed && ( + { if (!isWorking) onUninstallScream(game) }} + /> + )} + + {/* Koaloader installed - uninstall */} + {game.koaloader_installed && ( + { if (!isWorking) onUninstallKoaloader(game) }} + /> + )} + + {/* Settings button - only for direct ScreamAPI (not Koaloader) */} + {game.scream_installed && !game.koaloader_installed && ( +
+
+
+ ) +} + +export default EpicGameItem \ No newline at end of file diff --git a/src/components/games/EpicGameList.tsx b/src/components/games/EpicGameList.tsx new file mode 100644 index 0000000..9234f05 --- /dev/null +++ b/src/components/games/EpicGameList.tsx @@ -0,0 +1,65 @@ +import { useMemo } from 'react' +import EpicGameItem from '@/components/games/EpicGameItem' +import { EpicGame } from '@/types/EpicGame' +import LoadingIndicator from '../common/LoadingIndicator' + +interface EpicGameListProps { + games: EpicGame[] + isLoading: boolean + installingId: string | null + onInstall: (game: EpicGame) => void + onUninstallScream: (game: EpicGame) => void + onUninstallKoaloader: (game: EpicGame) => void + onSettings: (game: EpicGame) => void +} + +const EpicGameList = ({ + games, + isLoading, + installingId, + onInstall, + onUninstallScream, + onUninstallKoaloader, + onSettings, +}: EpicGameListProps) => { + const sortedGames = useMemo( + () => [...games].sort((a, b) => a.title.localeCompare(b.title)), + [games] + ) + + if (isLoading) { + return ( +
+ +
+ ) + } + + return ( +
+

Epic Games ({games.length})

+ + {games.length === 0 ? ( +
+ No Epic games found. Make sure Heroic is installed and has games downloaded. +
+ ) : ( +
+ {sortedGames.map((game) => ( + + ))} +
+ )} +
+ ) +} + +export default EpicGameList \ No newline at end of file