Merge branch 'main' into fix/fixing-seeding-on-level

This commit is contained in:
Zamitto 2025-02-15 23:13:20 -03:00 committed by GitHub
commit ef08d54cd3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 251 additions and 60 deletions

View file

@ -0,0 +1,50 @@
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
import { LibraryGame } from "@types";
import cn from "classnames";
import { useLocation } from "react-router-dom";
interface SidebarGameItemProps {
game: LibraryGame;
handleSidebarGameClick: (event: React.MouseEvent, game: LibraryGame) => void;
getGameTitle: (game: LibraryGame) => string;
}
export function SidebarGameItem({
game,
handleSidebarGameClick,
getGameTitle,
}: Readonly<SidebarGameItemProps>) {
const location = useLocation();
return (
<li
key={game.id}
className={cn("sidebar__menu-item", {
"sidebar__menu-item--active":
location.pathname === `/game/${game.shop}/${game.objectId}`,
"sidebar__menu-item--muted": game.download?.status === "removed",
})}
>
<button
type="button"
className="sidebar__menu-item-button"
onClick={(event) => handleSidebarGameClick(event, game)}
>
{game.iconUrl ? (
<img
className="sidebar__game-icon"
src={game.iconUrl}
alt={game.title}
loading="lazy"
/>
) : (
<SteamLogo className="sidebar__game-icon" />
)}
<span className="sidebar__menu-item-button-label">
{getGameTitle(game)}
</span>
</button>
</li>
);
}

View file

@ -18,11 +18,11 @@ import "./sidebar.scss";
import { buildGameDetailsPath } from "@renderer/helpers";
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
import { SidebarProfile } from "./sidebar-profile";
import { sortBy } from "lodash-es";
import cn from "classnames";
import { CommentDiscussionIcon } from "@primer/octicons-react";
import { SidebarGameItem } from "./sidebar-game-item";
const SIDEBAR_MIN_WIDTH = 200;
const SIDEBAR_INITIAL_WIDTH = 250;
@ -206,6 +206,23 @@ export function Sidebar() {
</ul>
</section>
<section className="sidebar__section">
<small className="sidebar__section-title">{t("favorites")}</small>
<ul className="sidebar__menu">
{sortedLibrary
.filter((game) => game.favorite)
.map((game) => (
<SidebarGameItem
key={game.id}
game={game}
handleSidebarGameClick={handleSidebarGameClick}
getGameTitle={getGameTitle}
/>
))}
</ul>
</section>
<section className="sidebar__section">
<small className="sidebar__section-title">{t("my_library")}</small>
@ -217,39 +234,16 @@ export function Sidebar() {
/>
<ul className="sidebar__menu">
{filteredLibrary.map((game) => (
<li
key={game.id}
className={cn("sidebar__menu-item", {
"sidebar__menu-item--active":
location.pathname ===
`/game/${game.shop}/${game.objectId}`,
"sidebar__menu-item--muted":
game.download?.status === "removed",
})}
>
<button
type="button"
className="sidebar__menu-item-button"
onClick={(event) => handleSidebarGameClick(event, game)}
>
{game.iconUrl ? (
<img
className="sidebar__game-icon"
src={game.iconUrl}
alt={game.title}
loading="lazy"
/>
) : (
<SteamLogo className="sidebar__game-icon" />
)}
<span className="sidebar__menu-item-button-label">
{getGameTitle(game)}
</span>
</button>
</li>
))}
{filteredLibrary
.filter((game) => !game.favorite)
.map((game) => (
<SidebarGameItem
key={game.id}
game={game}
handleSidebarGameClick={handleSidebarGameClick}
getGameTitle={getGameTitle}
/>
))}
</ul>
</section>
</div>

View file

@ -96,6 +96,11 @@ declare global {
objectId: string,
executablePath: string | null
) => Promise<void>;
addGameToFavorites: (shop: GameShop, objectId: string) => Promise<void>;
removeGameFromFavorites: (
shop: GameShop,
objectId: string
) => Promise<void>;
updateLaunchOptions: (
shop: GameShop,
objectId: string,

View file

@ -1,6 +1,8 @@
import {
DownloadIcon,
GearIcon,
HeartFillIcon,
HeartIcon,
PlayIcon,
PlusCircleIcon,
} from "@primer/octicons-react";
@ -52,6 +54,32 @@ export function HeroPanelActions() {
}
};
const addGameToFavorites = async () => {
setToggleLibraryGameDisabled(true);
try {
if (!objectId) throw new Error("objectId is required");
await window.electron.addGameToFavorites(shop, objectId);
updateLibrary();
updateGame();
} finally {
setToggleLibraryGameDisabled(false);
}
};
const removeGameFromFavorites = async () => {
setToggleLibraryGameDisabled(true);
try {
if (!objectId) throw new Error("objectId is required");
await window.electron.removeGameFromFavorites(shop, objectId);
updateLibrary();
updateGame();
} finally {
setToggleLibraryGameDisabled(false);
}
};
const openGame = async () => {
if (game) {
if (game.executablePath) {
@ -159,6 +187,16 @@ export function HeroPanelActions() {
<div className="hero-panel-actions__container">
{gameActionButton()}
<div className="hero-panel-actions__separator" />
<Button
onClick={game.favorite ? removeGameFromFavorites : addGameToFavorites}
theme="outline"
disabled={deleting}
className="hero-panel-actions__action"
>
{game.favorite ? <HeartFillIcon /> : <HeartIcon />}
</Button>
<Button
onClick={() => setShowGameOptionsModal(true)}
theme="outline"