Skip to content

Commit fbb67af

Browse files
authored
Merge pull request #1748 from v1ctorsales/quick-add-to-library-button
feature: Add button to quickly add games to library
2 parents 18e4bae + 4211f97 commit fbb67af

File tree

4 files changed

+78
-5
lines changed

4 files changed

+78
-5
lines changed

src/locales/en/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"amount_minutes": "{{amount}} minutes",
9090
"accuracy": "{{accuracy}}% accuracy",
9191
"add_to_library": "Add to library",
92+
"already_in_library": "Already in library",
9293
"remove_from_library": "Remove from library",
9394
"no_downloads": "No downloads available",
9495
"play_time": "Played for {{amount}}",

src/locales/pt-BR/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"amount_minutes": "{{amount}} minutos",
7777
"accuracy": "{{accuracy}}% de precisão",
7878
"add_to_library": "Adicionar à biblioteca",
79+
"already_in_library": "Já está na biblioteca",
7980
"remove_from_library": "Remover da biblioteca",
8081
"no_downloads": "Nenhum download disponível",
8182
"play_time": "Jogou por {{amount}}",

src/renderer/src/pages/catalogue/game-item.scss

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@
1616

1717
&:hover {
1818
background-color: rgba(255, 255, 255, 0.05);
19+
20+
.game-item__plus-wrapper {
21+
opacity: 1;
22+
pointer-events: auto;
23+
}
24+
}
25+
26+
&__plus-wrapper {
27+
position: absolute;
28+
top: 8px;
29+
right: 8px;
30+
opacity: 0;
31+
pointer-events: none;
32+
transition: opacity 0.2s ease-in-out;
33+
cursor: pointer;
34+
}
35+
36+
&__plus-wrapper--added {
37+
opacity: 0.5;
1938
}
2039

2140
&__cover {

src/renderer/src/pages/catalogue/game-item.tsx

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { Badge } from "@renderer/components";
22
import { buildGameDetailsPath } from "@renderer/helpers";
3-
import { useAppSelector, useRepacks } from "@renderer/hooks";
4-
import { useMemo } from "react";
3+
import { useAppSelector, useRepacks, useLibrary } from "@renderer/hooks";
4+
import { useMemo, useState, useEffect } from "react";
55
import { useNavigate } from "react-router-dom";
66

77
import "./game-item.scss";
88
import { useTranslation } from "react-i18next";
99
import { CatalogueSearchResult } from "@types";
10-
import { QuestionIcon } from "@primer/octicons-react";
10+
import { QuestionIcon, PlusIcon, CheckIcon } from "@primer/octicons-react";
11+
import cn from "classnames";
1112

1213
export interface GameItemProps {
1314
game: CatalogueSearchResult;
@@ -16,15 +17,49 @@ export interface GameItemProps {
1617
export function GameItem({ game }: GameItemProps) {
1718
const navigate = useNavigate();
1819

19-
const { i18n } = useTranslation();
20+
const { i18n, t } = useTranslation("game_details");
21+
22+
const language = i18n.language.split("-")[0];
2023

2124
const { steamGenres } = useAppSelector((state) => state.catalogueSearch);
2225

2326
const { getRepacksForObjectId } = useRepacks();
2427

2528
const repacks = getRepacksForObjectId(game.objectId);
2629

27-
const language = i18n.language.split("-")[0];
30+
const [isAddingToLibrary, setIsAddingToLibrary] = useState(false);
31+
32+
const [added, setAdded] = useState(false);
33+
34+
const { library, updateLibrary } = useLibrary();
35+
36+
useEffect(() => {
37+
const exists = library.some(
38+
(libItem) =>
39+
libItem.shop === game.shop && libItem.objectId === game.objectId
40+
);
41+
setAdded(exists);
42+
}, [library, game.shop, game.objectId]);
43+
44+
const addGameToLibrary = async (event: React.MouseEvent | React.KeyboardEvent) => {
45+
event.stopPropagation();
46+
if (added || isAddingToLibrary) return;
47+
48+
setIsAddingToLibrary(true);
49+
50+
try {
51+
await window.electron.addGameToLibrary(
52+
game.shop,
53+
game.objectId,
54+
game.title
55+
);
56+
updateLibrary();
57+
} catch (error) {
58+
console.error(error);
59+
} finally {
60+
setIsAddingToLibrary(false);
61+
}
62+
};
2863

2964
const uniqueRepackers = useMemo(() => {
3065
return Array.from(new Set(repacks.map((repack) => repack.repacker)));
@@ -85,6 +120,23 @@ export function GameItem({ game }: GameItemProps) {
85120
))}
86121
</div>
87122
</div>
123+
<div
124+
className={cn("game-item__plus-wrapper", {
125+
"game-item__plus-wrapper--added": added,
126+
})}
127+
role="button"
128+
tabIndex={0}
129+
onClick={addGameToLibrary}
130+
onKeyDown={(e) => {
131+
if (e.key === "Enter" || e.key === " ") {
132+
e.preventDefault();
133+
addGameToLibrary(e);
134+
}
135+
}}
136+
title={added ? t("already_in_library") : t("add_to_library")}
137+
>
138+
{added ? <CheckIcon size={16} /> : <PlusIcon size={16} />}
139+
</div>
88140
</button>
89141
);
90142
}

0 commit comments

Comments
 (0)