Merge pull request #1447 from leandroperin/feat-favorites
Some checks failed
Release / build (ubuntu-latest) (push) Has been cancelled
Release / build (windows-latest) (push) Has been cancelled

feat: favorites
This commit is contained in:
Chubby Granny Chaser 2025-02-05 23:54:05 +00:00 committed by GitHub
commit 110131f1d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 251 additions and 60 deletions

View file

@ -14,8 +14,10 @@
"paused": "{{title}} (Спынена)", "paused": "{{title}} (Спынена)",
"downloading": "{{title}} ({{percentage}} - Сцягванне…)", "downloading": "{{title}} ({{percentage}} - Сцягванне…)",
"filter": "Фільтар бібліятэкі", "filter": "Фільтар бібліятэкі",
"home": "Галоўная" "home": "Галоўная",
"favorites": "Улюбленыя"
}, },
"header": { "header": {
"search": "Пошук", "search": "Пошук",
"home": "Галоўная", "home": "Галоўная",

View file

@ -26,7 +26,8 @@
"game_has_no_executable": "Играта няма избран изпълним файл", "game_has_no_executable": "Играта няма избран изпълним файл",
"sign_in": "Вписване", "sign_in": "Вписване",
"friends": "Приятели", "friends": "Приятели",
"need_help": "Имате нужда от помощ??" "need_help": "Имате нужда от помощ??",
"favorites": "Любими игри"
}, },
"header": { "header": {
"search": "Търсене", "search": "Търсене",

View file

@ -20,10 +20,12 @@
"home": "Inici", "home": "Inici",
"queued": "{{title}} (En espera)", "queued": "{{title}} (En espera)",
"game_has_no_executable": "El joc encara no té un executable seleccionat", "game_has_no_executable": "El joc encara no té un executable seleccionat",
"sign_in": "Entra" "sign_in": "Entra",
"favorites": "Favorits"
}, },
"header": { "header": {
"search": "Cerca jocs", "search": "Cerca jocs",
"home": "Inici", "home": "Inici",
"catalogue": "Catàleg", "catalogue": "Catàleg",
"downloads": "Baixades", "downloads": "Baixades",

View file

@ -26,7 +26,8 @@
"game_has_no_executable": "Hra nemá zvolen žádný spustitelný soubor", "game_has_no_executable": "Hra nemá zvolen žádný spustitelný soubor",
"sign_in": "Přihlásit se", "sign_in": "Přihlásit se",
"friends": "Přátelé", "friends": "Přátelé",
"need_help": "Potřebujete pomoc?" "need_help": "Potřebujete pomoc?",
"favorites": "Oblíbené"
}, },
"header": { "header": {
"search": "Vyhledat hry", "search": "Vyhledat hry",

View file

@ -24,10 +24,12 @@
"queued": "{{title}} (I køen)", "queued": "{{title}} (I køen)",
"game_has_no_executable": "Spillet har ikke nogen eksekverbar fil valgt", "game_has_no_executable": "Spillet har ikke nogen eksekverbar fil valgt",
"sign_in": "Log ind", "sign_in": "Log ind",
"friends": "Venner" "friends": "Venner",
"favorites": "Favoritter"
}, },
"header": { "header": {
"search": "Søg efter spil", "search": "Søg efter spil",
"home": "Hjem", "home": "Hjem",
"catalogue": "Katalog", "catalogue": "Katalog",
"downloads": "Downloads", "downloads": "Downloads",

View file

@ -20,10 +20,12 @@
"home": "Home", "home": "Home",
"queued": "{{title}} (In Warteschlange)", "queued": "{{title}} (In Warteschlange)",
"game_has_no_executable": "Spiel hat keine ausführbare Datei gewählt", "game_has_no_executable": "Spiel hat keine ausführbare Datei gewählt",
"sign_in": "Anmelden" "sign_in": "Anmelden",
"favorites": "Favoriten"
}, },
"header": { "header": {
"search": "Spiele suchen", "search": "Spiele suchen",
"home": "Home", "home": "Home",
"catalogue": "Katalog", "catalogue": "Katalog",
"downloads": "Downloads", "downloads": "Downloads",

View file

@ -26,7 +26,8 @@
"game_has_no_executable": "Game has no executable selected", "game_has_no_executable": "Game has no executable selected",
"sign_in": "Sign in", "sign_in": "Sign in",
"friends": "Friends", "friends": "Friends",
"need_help": "Need help?" "need_help": "Need help?",
"favorites": "Favorites"
}, },
"header": { "header": {
"search": "Search games", "search": "Search games",
@ -190,6 +191,7 @@
"download_error_not_cached_in_real_debrid": "This download is not available on Real-Debrid and polling download status from Real-Debrid is not yet available.", "download_error_not_cached_in_real_debrid": "This download is not available on Real-Debrid and polling download status from Real-Debrid is not yet available.",
"download_error_not_cached_in_torbox": "This download is not available on Torbox and polling download status from Torbox is not yet available." "download_error_not_cached_in_torbox": "This download is not available on Torbox and polling download status from Torbox is not yet available."
}, },
"activation": { "activation": {
"title": "Activate Hydra", "title": "Activate Hydra",
"installation_id": "Installation ID:", "installation_id": "Installation ID:",

View file

@ -26,7 +26,8 @@
"game_has_no_executable": "El juego no tiene un ejecutable seleccionado", "game_has_no_executable": "El juego no tiene un ejecutable seleccionado",
"sign_in": "Iniciar sesión", "sign_in": "Iniciar sesión",
"friends": "Amigos", "friends": "Amigos",
"need_help": "¿Necesitas ayuda?" "need_help": "¿Necesitas ayuda?",
"favorites": "Favoritos"
}, },
"header": { "header": {
"search": "Buscar juegos", "search": "Buscar juegos",

View file

@ -25,7 +25,8 @@
"queued": "{{title}} (Järjekorras)", "queued": "{{title}} (Järjekorras)",
"game_has_no_executable": "Mängul pole käivitusfaili valitud", "game_has_no_executable": "Mängul pole käivitusfaili valitud",
"sign_in": "Logi sisse", "sign_in": "Logi sisse",
"friends": "Sõbrad" "friends": "Sõbrad",
"favorites": "Lemmikud"
}, },
"header": { "header": {
"search": "Otsi mänge", "search": "Otsi mänge",

View file

@ -14,8 +14,10 @@
"paused": "{{title}} (متوقف شده)", "paused": "{{title}} (متوقف شده)",
"downloading": "{{title}} ({{percentage}} - در حال دانلود…)", "downloading": "{{title}} ({{percentage}} - در حال دانلود…)",
"filter": "فیلتر کردن کتابخانه", "filter": "فیلتر کردن کتابخانه",
"home": "خانه" "home": "خانه",
"favorites": "علاقه‌مندی‌ها"
}, },
"header": { "header": {
"search": "جستجوی بازی‌ها", "search": "جستجوی بازی‌ها",
"home": "خانه", "home": "خانه",

View file

@ -14,10 +14,12 @@
"paused": "{{title}} (En pause)", "paused": "{{title}} (En pause)",
"downloading": "{{title}} ({{percentage}} - Téléchargement en cours…)", "downloading": "{{title}} ({{percentage}} - Téléchargement en cours…)",
"filter": "Filtrer la bibliothèque", "filter": "Filtrer la bibliothèque",
"home": "Page daccueil" "home": "Page daccueil",
"favorites": "Favoris"
}, },
"header": { "header": {
"search": "Recherche", "search": "Recherche",
"catalogue": "Catalogue", "catalogue": "Catalogue",
"downloads": "Téléchargements", "downloads": "Téléchargements",
"search_results": "Résultats de la recherche", "search_results": "Résultats de la recherche",

View file

@ -14,10 +14,12 @@
"paused": "{{title}} (Szünet)", "paused": "{{title}} (Szünet)",
"downloading": "{{title}} ({{percentage}} - Letöltés…)", "downloading": "{{title}} ({{percentage}} - Letöltés…)",
"filter": "Könyvtár szűrése", "filter": "Könyvtár szűrése",
"home": "Főoldal" "home": "Főoldal",
"favorites": "Kedvenc játékok"
}, },
"header": { "header": {
"search": "Keresés", "search": "Keresés",
"home": "Főoldal", "home": "Főoldal",
"catalogue": "Katalógus", "catalogue": "Katalógus",
"downloads": "Letöltések", "downloads": "Letöltések",

View file

@ -20,10 +20,12 @@
"home": "Beranda", "home": "Beranda",
"queued": "{{title}} (Antrian)", "queued": "{{title}} (Antrian)",
"game_has_no_executable": "Game tidak punya file eksekusi yang dipilih", "game_has_no_executable": "Game tidak punya file eksekusi yang dipilih",
"sign_in": "Masuk" "sign_in": "Masuk",
"favorites": "Favorit"
}, },
"header": { "header": {
"search": "Cari game", "search": "Cari game",
"home": "Beranda", "home": "Beranda",
"catalogue": "Katalog", "catalogue": "Katalog",
"downloads": "Unduhan", "downloads": "Unduhan",

View file

@ -14,10 +14,12 @@
"paused": "{{title}} (In pausa)", "paused": "{{title}} (In pausa)",
"downloading": "{{title}} ({{percentage}} - Download…)", "downloading": "{{title}} ({{percentage}} - Download…)",
"filter": "Filtra libreria", "filter": "Filtra libreria",
"home": "Home" "home": "Home",
"favorites": "Preferiti"
}, },
"header": { "header": {
"search": "Cerca", "search": "Cerca",
"home": "Home", "home": "Home",
"catalogue": "Catalogo", "catalogue": "Catalogo",
"downloads": "Download", "downloads": "Download",

View file

@ -20,8 +20,10 @@
"home": "Басты бет", "home": "Басты бет",
"queued": "{{title}} (Кезекте)", "queued": "{{title}} (Кезекте)",
"game_has_no_executable": "Ойынды іске қосу файлы таңдалмаған", "game_has_no_executable": "Ойынды іске қосу файлы таңдалмаған",
"sign_in": "Кіру" "sign_in": "Кіру",
"favorites": "Таңдаулылар"
}, },
"header": { "header": {
"search": "Іздеу", "search": "Іздеу",
"home": "Басты бет", "home": "Басты бет",

View file

@ -14,8 +14,10 @@
"paused": "{{title}} (일시 정지됨)", "paused": "{{title}} (일시 정지됨)",
"downloading": "{{title}} ({{percentage}} - 다운로드 중…)", "downloading": "{{title}} ({{percentage}} - 다운로드 중…)",
"filter": "라이브러리 정렬", "filter": "라이브러리 정렬",
"home": "홈" "home": "홈",
"favorites": "즐겨찾기"
}, },
"header": { "header": {
"search": "게임 검색하기", "search": "게임 검색하기",
"home": "홈", "home": "홈",

View file

@ -24,10 +24,12 @@
"queued": "{{title}} (I køen)", "queued": "{{title}} (I køen)",
"game_has_no_executable": "Spillet har ikke noen kjørbar fil valgt", "game_has_no_executable": "Spillet har ikke noen kjørbar fil valgt",
"sign_in": "Logge inn", "sign_in": "Logge inn",
"friends": "Venner" "friends": "Venner",
"favorites": "Favoritter"
}, },
"header": { "header": {
"search": "Søk efter spill", "search": "Søk efter spill",
"home": "Hjem", "home": "Hjem",
"catalogue": "Katalog", "catalogue": "Katalog",
"downloads": "Nedlastinger", "downloads": "Nedlastinger",

View file

@ -14,10 +14,12 @@
"paused": "{{title}} (Gepauzeerd)", "paused": "{{title}} (Gepauzeerd)",
"downloading": "{{title}} ({{percentage}} - Downloading…)", "downloading": "{{title}} ({{percentage}} - Downloading…)",
"filter": "Filter Bibliotheek", "filter": "Filter Bibliotheek",
"home": "Home" "home": "Home",
"favorites": "Favorieten"
}, },
"header": { "header": {
"search": "Zoek spellen", "search": "Zoek spellen",
"home": "Home", "home": "Home",
"catalogue": "Bibliotheek", "catalogue": "Bibliotheek",
"downloads": "Downloads", "downloads": "Downloads",

View file

@ -14,10 +14,12 @@
"paused": "{{title}} (Zatrzymano)", "paused": "{{title}} (Zatrzymano)",
"downloading": "{{title}} ({{percentage}} - Pobieranie…)", "downloading": "{{title}} ({{percentage}} - Pobieranie…)",
"filter": "Filtruj biblioteke", "filter": "Filtruj biblioteke",
"home": "Główna" "home": "Główna",
"favorites": "Ulubione"
}, },
"header": { "header": {
"search": "Szukaj", "search": "Szukaj",
"home": "Główna", "home": "Główna",
"catalogue": "Katalog", "catalogue": "Katalog",
"downloads": "Pobrane", "downloads": "Pobrane",

View file

@ -26,10 +26,12 @@
"game_has_no_executable": "Jogo não possui executável selecionado", "game_has_no_executable": "Jogo não possui executável selecionado",
"sign_in": "Login", "sign_in": "Login",
"friends": "Amigos", "friends": "Amigos",
"need_help": "Precisa de ajuda?" "need_help": "Precisa de ajuda?",
"favorites": "Favoritos"
}, },
"header": { "header": {
"search": "Buscar jogos", "search": "Buscar jogos",
"catalogue": "Catálogo", "catalogue": "Catálogo",
"downloads": "Downloads", "downloads": "Downloads",
"search_results": "Resultados da busca", "search_results": "Resultados da busca",
@ -179,6 +181,7 @@
"download_error_not_cached_in_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.", "download_error_not_cached_in_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.",
"download_error_not_cached_in_torbox": "Este download não está disponível no Torbox e a verificação do status do download não está disponível." "download_error_not_cached_in_torbox": "Este download não está disponível no Torbox e a verificação do status do download não está disponível."
}, },
"activation": { "activation": {
"title": "Ativação", "title": "Ativação",
"installation_id": "ID da instalação:", "installation_id": "ID da instalação:",

View file

@ -25,10 +25,12 @@
"queued": "{{title}} (Na fila)", "queued": "{{title}} (Na fila)",
"game_has_no_executable": "O jogo não tem um executável selecionado", "game_has_no_executable": "O jogo não tem um executável selecionado",
"sign_in": "Iniciar sessão", "sign_in": "Iniciar sessão",
"friends": "Amigos" "friends": "Amigos",
"favorites": "Favoritos"
}, },
"header": { "header": {
"search": "Procurar jogos", "search": "Procurar jogos",
"catalogue": "Catálogo", "catalogue": "Catálogo",
"downloads": "Transferências", "downloads": "Transferências",
"search_results": "Resultados da pesquisa", "search_results": "Resultados da pesquisa",

View file

@ -14,10 +14,12 @@
"paused": "{{title}} (Pauzat)", "paused": "{{title}} (Pauzat)",
"downloading": "{{title}} ({{percentage}} - Se descarcă...)", "downloading": "{{title}} ({{percentage}} - Se descarcă...)",
"filter": "Filtrează biblioteca", "filter": "Filtrează biblioteca",
"home": "Acasă" "home": "Acasă",
"favorites": "Favorite"
}, },
"header": { "header": {
"search": "Caută jocuri", "search": "Caută jocuri",
"home": "Acasă", "home": "Acasă",
"catalogue": "Catalog", "catalogue": "Catalog",
"downloads": "Descărcări", "downloads": "Descărcări",

View file

@ -26,7 +26,8 @@
"game_has_no_executable": "Файл запуска игры не выбран", "game_has_no_executable": "Файл запуска игры не выбран",
"sign_in": "Войти", "sign_in": "Войти",
"friends": "Друзья", "friends": "Друзья",
"need_help": "Нужна помощь?" "need_help": "Нужна помощь?",
"favorites": "Избранное"
}, },
"header": { "header": {
"search": "Поиск", "search": "Поиск",

View file

@ -26,7 +26,8 @@
"game_has_no_executable": "Oyun için bir çalıştırılabilir dosya seçilmedi", "game_has_no_executable": "Oyun için bir çalıştırılabilir dosya seçilmedi",
"sign_in": "Giriş yap", "sign_in": "Giriş yap",
"friends": "Arkadaşlar", "friends": "Arkadaşlar",
"need_help": "Yardıma mı ihtiyacınız var?" "need_help": "Yardıma mı ihtiyacınız var?",
"favorites": "Favoriler"
}, },
"header": { "header": {
"search": "Oyunları ara", "search": "Oyunları ara",

View file

@ -20,10 +20,12 @@
"home": "Головна", "home": "Головна",
"game_has_no_executable": "Не було вибрано файл для запуску гри", "game_has_no_executable": "Не було вибрано файл для запуску гри",
"queued": "{{title}} в черзі", "queued": "{{title}} в черзі",
"sign_in": "Увійти" "sign_in": "Увійти",
"favorites": "Улюблені"
}, },
"header": { "header": {
"search": "Пошук", "search": "Пошук",
"home": "Головна", "home": "Головна",
"catalogue": "Каталог", "catalogue": "Каталог",
"downloads": "Завантаження", "downloads": "Завантаження",

View file

@ -25,7 +25,8 @@
"queued": "{{title}} (已加入下载队列)", "queued": "{{title}} (已加入下载队列)",
"game_has_no_executable": "未选择游戏的可执行文件", "game_has_no_executable": "未选择游戏的可执行文件",
"sign_in": "登入", "sign_in": "登入",
"friends": "好友" "friends": "好友",
"favorites": "收藏"
}, },
"header": { "header": {
"search": "搜索游戏", "search": "搜索游戏",

View file

@ -13,6 +13,8 @@ import "./catalogue/get-developers";
import "./hardware/get-disk-free-space"; import "./hardware/get-disk-free-space";
import "./hardware/check-folder-write-permission"; import "./hardware/check-folder-write-permission";
import "./library/add-game-to-library"; import "./library/add-game-to-library";
import "./library/add-game-to-favorites";
import "./library/remove-game-from-favorites";
import "./library/create-game-shortcut"; import "./library/create-game-shortcut";
import "./library/close-game"; import "./library/close-game";
import "./library/delete-game-folder"; import "./library/delete-game-folder";

View file

@ -0,0 +1,25 @@
import { registerEvent } from "../register-event";
import { gamesSublevel, levelKeys } from "@main/level";
import type { GameShop } from "@types";
const addGameToFavorites = async (
_event: Electron.IpcMainInvokeEvent,
shop: GameShop,
objectId: string
) => {
const gameKey = levelKeys.game(shop, objectId);
const game = await gamesSublevel.get(gameKey);
if (!game) return;
try {
await gamesSublevel.put(gameKey, {
...game,
favorite: true,
});
} catch (error) {
throw new Error(`Failed to update game favorite status: ${error}`);
}
};
registerEvent("addGameToFavorites", addGameToFavorites);

View file

@ -0,0 +1,25 @@
import { registerEvent } from "../register-event";
import { gamesSublevel, levelKeys } from "@main/level";
import type { GameShop } from "@types";
const removeGameFromFavorites = async (
_event: Electron.IpcMainInvokeEvent,
shop: GameShop,
objectId: string
) => {
const gameKey = levelKeys.game(shop, objectId);
const game = await gamesSublevel.get(gameKey);
if (!game) return;
try {
await gamesSublevel.put(gameKey, {
...game,
favorite: false,
});
} catch (error) {
throw new Error(`Failed to update game favorite status: ${error}`);
}
};
registerEvent("removeGameFromFavorites", removeGameFromFavorites);

View file

@ -110,11 +110,16 @@ contextBridge.exposeInMainWorld("electron", {
executablePath: string | null executablePath: string | null
) => ) =>
ipcRenderer.invoke("updateExecutablePath", shop, objectId, executablePath), ipcRenderer.invoke("updateExecutablePath", shop, objectId, executablePath),
addGameToFavorites: (shop: GameShop, objectId: string) =>
ipcRenderer.invoke("addGameToFavorites", shop, objectId),
removeGameFromFavorites: (shop: GameShop, objectId: string) =>
ipcRenderer.invoke("removeGameFromFavorites", shop, objectId),
updateLaunchOptions: ( updateLaunchOptions: (
shop: GameShop, shop: GameShop,
objectId: string, objectId: string,
launchOptions: string | null launchOptions: string | null
) => ipcRenderer.invoke("updateLaunchOptions", shop, objectId, launchOptions), ) => ipcRenderer.invoke("updateLaunchOptions", shop, objectId, launchOptions),
selectGameWinePrefix: ( selectGameWinePrefix: (
shop: GameShop, shop: GameShop,
objectId: string, objectId: string,

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 { buildGameDetailsPath } from "@renderer/helpers";
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
import { SidebarProfile } from "./sidebar-profile"; import { SidebarProfile } from "./sidebar-profile";
import { sortBy } from "lodash-es"; import { sortBy } from "lodash-es";
import cn from "classnames"; import cn from "classnames";
import { CommentDiscussionIcon } from "@primer/octicons-react"; import { CommentDiscussionIcon } from "@primer/octicons-react";
import { SidebarGameItem } from "./sidebar-game-item";
const SIDEBAR_MIN_WIDTH = 200; const SIDEBAR_MIN_WIDTH = 200;
const SIDEBAR_INITIAL_WIDTH = 250; const SIDEBAR_INITIAL_WIDTH = 250;
@ -206,6 +206,23 @@ export function Sidebar() {
</ul> </ul>
</section> </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"> <section className="sidebar__section">
<small className="sidebar__section-title">{t("my_library")}</small> <small className="sidebar__section-title">{t("my_library")}</small>
@ -217,39 +234,16 @@ export function Sidebar() {
/> />
<ul className="sidebar__menu"> <ul className="sidebar__menu">
{filteredLibrary.map((game) => ( {filteredLibrary
<li .filter((game) => !game.favorite)
key={game.id} .map((game) => (
className={cn("sidebar__menu-item", { <SidebarGameItem
"sidebar__menu-item--active": key={game.id}
location.pathname === game={game}
`/game/${game.shop}/${game.objectId}`, handleSidebarGameClick={handleSidebarGameClick}
"sidebar__menu-item--muted": getGameTitle={getGameTitle}
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>
))}
</ul> </ul>
</section> </section>
</div> </div>

View file

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

View file

@ -1,6 +1,8 @@
import { import {
DownloadIcon, DownloadIcon,
GearIcon, GearIcon,
HeartFillIcon,
HeartIcon,
PlayIcon, PlayIcon,
PlusCircleIcon, PlusCircleIcon,
} from "@primer/octicons-react"; } 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 () => { const openGame = async () => {
if (game) { if (game) {
if (game.executablePath) { if (game.executablePath) {
@ -159,6 +187,16 @@ export function HeroPanelActions() {
<div className="hero-panel-actions__container"> <div className="hero-panel-actions__container">
{gameActionButton()} {gameActionButton()}
<div className="hero-panel-actions__separator" /> <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 <Button
onClick={() => setShowGameOptionsModal(true)} onClick={() => setShowGameOptionsModal(true)}
theme="outline" theme="outline"

View file

@ -42,6 +42,7 @@ export interface Game {
winePrefixPath?: string | null; winePrefixPath?: string | null;
executablePath?: string | null; executablePath?: string | null;
launchOptions?: string | null; launchOptions?: string | null;
favorite?: boolean;
} }
export interface Download { export interface Download {