Merge pull request #1748 from v1ctorsales/quick-add-to-library-button
Some checks failed
Release / build (ubuntu-latest) (push) Has been cancelled
Release / build (windows-latest) (push) Has been cancelled

feature: Add button to quickly add games to library
This commit is contained in:
Zamitto
2025-09-01 14:47:56 -03:00
committed by GitHub
4 changed files with 78 additions and 5 deletions

View File

@@ -89,6 +89,7 @@
"amount_minutes": "{{amount}} minutes",
"accuracy": "{{accuracy}}% accuracy",
"add_to_library": "Add to library",
"already_in_library": "Already in library",
"remove_from_library": "Remove from library",
"no_downloads": "No downloads available",
"play_time": "Played for {{amount}}",

View File

@@ -76,6 +76,7 @@
"amount_minutes": "{{amount}} minutos",
"accuracy": "{{accuracy}}% de precisão",
"add_to_library": "Adicionar à biblioteca",
"already_in_library": "Já está na biblioteca",
"remove_from_library": "Remover da biblioteca",
"no_downloads": "Nenhum download disponível",
"play_time": "Jogou por {{amount}}",

View File

@@ -16,6 +16,25 @@
&:hover {
background-color: rgba(255, 255, 255, 0.05);
.game-item__plus-wrapper {
opacity: 1;
pointer-events: auto;
}
}
&__plus-wrapper {
position: absolute;
top: 8px;
right: 8px;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease-in-out;
cursor: pointer;
}
&__plus-wrapper--added {
opacity: 0.5;
}
&__cover {

View File

@@ -1,13 +1,14 @@
import { Badge } from "@renderer/components";
import { buildGameDetailsPath } from "@renderer/helpers";
import { useAppSelector, useRepacks } from "@renderer/hooks";
import { useMemo } from "react";
import { useAppSelector, useRepacks, useLibrary } from "@renderer/hooks";
import { useMemo, useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import "./game-item.scss";
import { useTranslation } from "react-i18next";
import { CatalogueSearchResult } from "@types";
import { QuestionIcon } from "@primer/octicons-react";
import { QuestionIcon, PlusIcon, CheckIcon } from "@primer/octicons-react";
import cn from "classnames";
export interface GameItemProps {
game: CatalogueSearchResult;
@@ -16,7 +17,9 @@ export interface GameItemProps {
export function GameItem({ game }: GameItemProps) {
const navigate = useNavigate();
const { i18n } = useTranslation();
const { i18n, t } = useTranslation("game_details");
const language = i18n.language.split("-")[0];
const { steamGenres } = useAppSelector((state) => state.catalogueSearch);
@@ -24,7 +27,39 @@ export function GameItem({ game }: GameItemProps) {
const repacks = getRepacksForObjectId(game.objectId);
const language = i18n.language.split("-")[0];
const [isAddingToLibrary, setIsAddingToLibrary] = useState(false);
const [added, setAdded] = useState(false);
const { library, updateLibrary } = useLibrary();
useEffect(() => {
const exists = library.some(
(libItem) =>
libItem.shop === game.shop && libItem.objectId === game.objectId
);
setAdded(exists);
}, [library, game.shop, game.objectId]);
const addGameToLibrary = async (event: React.MouseEvent | React.KeyboardEvent) => {
event.stopPropagation();
if (added || isAddingToLibrary) return;
setIsAddingToLibrary(true);
try {
await window.electron.addGameToLibrary(
game.shop,
game.objectId,
game.title
);
updateLibrary();
} catch (error) {
console.error(error);
} finally {
setIsAddingToLibrary(false);
}
};
const uniqueRepackers = useMemo(() => {
return Array.from(new Set(repacks.map((repack) => repack.repacker)));
@@ -85,6 +120,23 @@ export function GameItem({ game }: GameItemProps) {
))}
</div>
</div>
<div
className={cn("game-item__plus-wrapper", {
"game-item__plus-wrapper--added": added,
})}
role="button"
tabIndex={0}
onClick={addGameToLibrary}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
addGameToLibrary(e);
}
}}
title={added ? t("already_in_library") : t("add_to_library")}
>
{added ? <CheckIcon size={16} /> : <PlusIcon size={16} />}
</div>
</button>
);
}