diff --git a/src/locales/be/translation.json b/src/locales/be/translation.json index 6618e4f3..d89876bd 100644 --- a/src/locales/be/translation.json +++ b/src/locales/be/translation.json @@ -14,8 +14,10 @@ "paused": "{{title}} (Спынена)", "downloading": "{{title}} ({{percentage}} - Сцягванне…)", "filter": "Фільтар бібліятэкі", - "home": "Галоўная" + "home": "Галоўная", + "favorites": "Улюбленыя" }, + "header": { "search": "Пошук", "home": "Галоўная", diff --git a/src/locales/bg/translation.json b/src/locales/bg/translation.json index 857d3ed4..e71a0245 100644 --- a/src/locales/bg/translation.json +++ b/src/locales/bg/translation.json @@ -26,7 +26,8 @@ "game_has_no_executable": "Играта няма избран изпълним файл", "sign_in": "Вписване", "friends": "Приятели", - "need_help": "Имате нужда от помощ??" + "need_help": "Имате нужда от помощ??", + "favorites": "Любими игри" }, "header": { "search": "Търсене", diff --git a/src/locales/ca/translation.json b/src/locales/ca/translation.json index 6d689f2b..f87caf56 100644 --- a/src/locales/ca/translation.json +++ b/src/locales/ca/translation.json @@ -20,10 +20,12 @@ "home": "Inici", "queued": "{{title}} (En espera)", "game_has_no_executable": "El joc encara no té un executable seleccionat", - "sign_in": "Entra" + "sign_in": "Entra", + "favorites": "Favorits" }, "header": { "search": "Cerca jocs", + "home": "Inici", "catalogue": "Catàleg", "downloads": "Baixades", diff --git a/src/locales/cs/translation.json b/src/locales/cs/translation.json index b3543f94..cc54e69d 100644 --- a/src/locales/cs/translation.json +++ b/src/locales/cs/translation.json @@ -26,7 +26,8 @@ "game_has_no_executable": "Hra nemá zvolen žádný spustitelný soubor", "sign_in": "Přihlásit se", "friends": "Přátelé", - "need_help": "Potřebujete pomoc?" + "need_help": "Potřebujete pomoc?", + "favorites": "Oblíbené" }, "header": { "search": "Vyhledat hry", diff --git a/src/locales/da/translation.json b/src/locales/da/translation.json index 9a7c700f..c6f5a42c 100644 --- a/src/locales/da/translation.json +++ b/src/locales/da/translation.json @@ -24,10 +24,12 @@ "queued": "{{title}} (I køen)", "game_has_no_executable": "Spillet har ikke nogen eksekverbar fil valgt", "sign_in": "Log ind", - "friends": "Venner" + "friends": "Venner", + "favorites": "Favoritter" }, "header": { "search": "Søg efter spil", + "home": "Hjem", "catalogue": "Katalog", "downloads": "Downloads", diff --git a/src/locales/de/translation.json b/src/locales/de/translation.json index c94b0913..8910f416 100644 --- a/src/locales/de/translation.json +++ b/src/locales/de/translation.json @@ -20,10 +20,12 @@ "home": "Home", "queued": "{{title}} (In Warteschlange)", "game_has_no_executable": "Spiel hat keine ausführbare Datei gewählt", - "sign_in": "Anmelden" + "sign_in": "Anmelden", + "favorites": "Favoriten" }, "header": { "search": "Spiele suchen", + "home": "Home", "catalogue": "Katalog", "downloads": "Downloads", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index bbe922f2..94b52c75 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -26,7 +26,8 @@ "game_has_no_executable": "Game has no executable selected", "sign_in": "Sign in", "friends": "Friends", - "need_help": "Need help?" + "need_help": "Need help?", + "favorites": "Favorites" }, "header": { "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_torbox": "This download is not available on Torbox and polling download status from Torbox is not yet available." }, + "activation": { "title": "Activate Hydra", "installation_id": "Installation ID:", diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index 43839e9e..27e18f34 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -26,7 +26,8 @@ "game_has_no_executable": "El juego no tiene un ejecutable seleccionado", "sign_in": "Iniciar sesión", "friends": "Amigos", - "need_help": "¿Necesitas ayuda?" + "need_help": "¿Necesitas ayuda?", + "favorites": "Favoritos" }, "header": { "search": "Buscar juegos", diff --git a/src/locales/et/translation.json b/src/locales/et/translation.json index 97e69a90..91467e90 100644 --- a/src/locales/et/translation.json +++ b/src/locales/et/translation.json @@ -25,7 +25,8 @@ "queued": "{{title}} (Järjekorras)", "game_has_no_executable": "Mängul pole käivitusfaili valitud", "sign_in": "Logi sisse", - "friends": "Sõbrad" + "friends": "Sõbrad", + "favorites": "Lemmikud" }, "header": { "search": "Otsi mänge", diff --git a/src/locales/fa/translation.json b/src/locales/fa/translation.json index 9d4f7280..9f0be309 100644 --- a/src/locales/fa/translation.json +++ b/src/locales/fa/translation.json @@ -14,8 +14,10 @@ "paused": "{{title}} (متوقف شده)", "downloading": "{{title}} ({{percentage}} - در حال دانلود…)", "filter": "فیلتر کردن کتابخانه", - "home": "خانه" + "home": "خانه", + "favorites": "علاقه‌مندی‌ها" }, + "header": { "search": "جستجوی بازی‌ها", "home": "خانه", diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json index 4d310681..9e912e32 100644 --- a/src/locales/fr/translation.json +++ b/src/locales/fr/translation.json @@ -14,10 +14,12 @@ "paused": "{{title}} (En pause)", "downloading": "{{title}} ({{percentage}} - Téléchargement en cours…)", "filter": "Filtrer la bibliothèque", - "home": "Page d’accueil" + "home": "Page d’accueil", + "favorites": "Favoris" }, "header": { "search": "Recherche", + "catalogue": "Catalogue", "downloads": "Téléchargements", "search_results": "Résultats de la recherche", diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 0863d1e8..d5603188 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -14,10 +14,12 @@ "paused": "{{title}} (Szünet)", "downloading": "{{title}} ({{percentage}} - Letöltés…)", "filter": "Könyvtár szűrése", - "home": "Főoldal" + "home": "Főoldal", + "favorites": "Kedvenc játékok" }, "header": { "search": "Keresés", + "home": "Főoldal", "catalogue": "Katalógus", "downloads": "Letöltések", diff --git a/src/locales/id/translation.json b/src/locales/id/translation.json index a813d770..bd8d8151 100644 --- a/src/locales/id/translation.json +++ b/src/locales/id/translation.json @@ -20,10 +20,12 @@ "home": "Beranda", "queued": "{{title}} (Antrian)", "game_has_no_executable": "Game tidak punya file eksekusi yang dipilih", - "sign_in": "Masuk" + "sign_in": "Masuk", + "favorites": "Favorit" }, "header": { "search": "Cari game", + "home": "Beranda", "catalogue": "Katalog", "downloads": "Unduhan", diff --git a/src/locales/it/translation.json b/src/locales/it/translation.json index 742f889f..3aa05109 100644 --- a/src/locales/it/translation.json +++ b/src/locales/it/translation.json @@ -14,10 +14,12 @@ "paused": "{{title}} (In pausa)", "downloading": "{{title}} ({{percentage}} - Download…)", "filter": "Filtra libreria", - "home": "Home" + "home": "Home", + "favorites": "Preferiti" }, "header": { "search": "Cerca", + "home": "Home", "catalogue": "Catalogo", "downloads": "Download", diff --git a/src/locales/kk/translation.json b/src/locales/kk/translation.json index e66cd5ed..dd81d1db 100644 --- a/src/locales/kk/translation.json +++ b/src/locales/kk/translation.json @@ -20,8 +20,10 @@ "home": "Басты бет", "queued": "{{title}} (Кезекте)", "game_has_no_executable": "Ойынды іске қосу файлы таңдалмаған", - "sign_in": "Кіру" + "sign_in": "Кіру", + "favorites": "Таңдаулылар" }, + "header": { "search": "Іздеу", "home": "Басты бет", diff --git a/src/locales/ko/translation.json b/src/locales/ko/translation.json index bc8e6056..92c6352e 100644 --- a/src/locales/ko/translation.json +++ b/src/locales/ko/translation.json @@ -14,8 +14,10 @@ "paused": "{{title}} (일시 정지됨)", "downloading": "{{title}} ({{percentage}} - 다운로드 중…)", "filter": "라이브러리 정렬", - "home": "홈" + "home": "홈", + "favorites": "즐겨찾기" }, + "header": { "search": "게임 검색하기", "home": "홈", diff --git a/src/locales/nb/translation.json b/src/locales/nb/translation.json index a6fb1bcc..d821f518 100644 --- a/src/locales/nb/translation.json +++ b/src/locales/nb/translation.json @@ -24,10 +24,12 @@ "queued": "{{title}} (I køen)", "game_has_no_executable": "Spillet har ikke noen kjørbar fil valgt", "sign_in": "Logge inn", - "friends": "Venner" + "friends": "Venner", + "favorites": "Favoritter" }, "header": { "search": "Søk efter spill", + "home": "Hjem", "catalogue": "Katalog", "downloads": "Nedlastinger", diff --git a/src/locales/nl/translation.json b/src/locales/nl/translation.json index 6d9de5fa..d619303e 100644 --- a/src/locales/nl/translation.json +++ b/src/locales/nl/translation.json @@ -14,10 +14,12 @@ "paused": "{{title}} (Gepauzeerd)", "downloading": "{{title}} ({{percentage}} - Downloading…)", "filter": "Filter Bibliotheek", - "home": "Home" + "home": "Home", + "favorites": "Favorieten" }, "header": { "search": "Zoek spellen", + "home": "Home", "catalogue": "Bibliotheek", "downloads": "Downloads", diff --git a/src/locales/pl/translation.json b/src/locales/pl/translation.json index bdaf822c..1151318a 100644 --- a/src/locales/pl/translation.json +++ b/src/locales/pl/translation.json @@ -14,10 +14,12 @@ "paused": "{{title}} (Zatrzymano)", "downloading": "{{title}} ({{percentage}} - Pobieranie…)", "filter": "Filtruj biblioteke", - "home": "Główna" + "home": "Główna", + "favorites": "Ulubione" }, "header": { "search": "Szukaj", + "home": "Główna", "catalogue": "Katalog", "downloads": "Pobrane", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index b34f1874..6394ed95 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -26,10 +26,12 @@ "game_has_no_executable": "Jogo não possui executável selecionado", "sign_in": "Login", "friends": "Amigos", - "need_help": "Precisa de ajuda?" + "need_help": "Precisa de ajuda?", + "favorites": "Favoritos" }, "header": { "search": "Buscar jogos", + "catalogue": "Catálogo", "downloads": "Downloads", "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_torbox": "Este download não está disponível no Torbox e a verificação do status do download não está disponível." }, + "activation": { "title": "Ativação", "installation_id": "ID da instalação:", diff --git a/src/locales/pt-PT/translation.json b/src/locales/pt-PT/translation.json index 3b8496ae..212c01d8 100644 --- a/src/locales/pt-PT/translation.json +++ b/src/locales/pt-PT/translation.json @@ -25,10 +25,12 @@ "queued": "{{title}} (Na fila)", "game_has_no_executable": "O jogo não tem um executável selecionado", "sign_in": "Iniciar sessão", - "friends": "Amigos" + "friends": "Amigos", + "favorites": "Favoritos" }, "header": { "search": "Procurar jogos", + "catalogue": "Catálogo", "downloads": "Transferências", "search_results": "Resultados da pesquisa", diff --git a/src/locales/ro/translation.json b/src/locales/ro/translation.json index 9003ecc6..8a5403b5 100644 --- a/src/locales/ro/translation.json +++ b/src/locales/ro/translation.json @@ -14,10 +14,12 @@ "paused": "{{title}} (Pauzat)", "downloading": "{{title}} ({{percentage}} - Se descarcă...)", "filter": "Filtrează biblioteca", - "home": "Acasă" + "home": "Acasă", + "favorites": "Favorite" }, "header": { "search": "Caută jocuri", + "home": "Acasă", "catalogue": "Catalog", "downloads": "Descărcări", diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 18b8f1f5..bbd7047a 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -26,7 +26,8 @@ "game_has_no_executable": "Файл запуска игры не выбран", "sign_in": "Войти", "friends": "Друзья", - "need_help": "Нужна помощь?" + "need_help": "Нужна помощь?", + "favorites": "Избранное" }, "header": { "search": "Поиск", diff --git a/src/locales/tr/translation.json b/src/locales/tr/translation.json index 9095b693..f34a6eb4 100644 --- a/src/locales/tr/translation.json +++ b/src/locales/tr/translation.json @@ -26,7 +26,8 @@ "game_has_no_executable": "Oyun için bir çalıştırılabilir dosya seçilmedi", "sign_in": "Giriş yap", "friends": "Arkadaşlar", - "need_help": "Yardıma mı ihtiyacınız var?" + "need_help": "Yardıma mı ihtiyacınız var?", + "favorites": "Favoriler" }, "header": { "search": "Oyunları ara", diff --git a/src/locales/uk/translation.json b/src/locales/uk/translation.json index 174e768c..330b1c6d 100644 --- a/src/locales/uk/translation.json +++ b/src/locales/uk/translation.json @@ -20,10 +20,12 @@ "home": "Головна", "game_has_no_executable": "Не було вибрано файл для запуску гри", "queued": "{{title}} в черзі", - "sign_in": "Увійти" + "sign_in": "Увійти", + "favorites": "Улюблені" }, "header": { "search": "Пошук", + "home": "Головна", "catalogue": "Каталог", "downloads": "Завантаження", diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index ba48a21b..8b9c535c 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -25,7 +25,8 @@ "queued": "{{title}} (已加入下载队列)", "game_has_no_executable": "未选择游戏的可执行文件", "sign_in": "登入", - "friends": "好友" + "friends": "好友", + "favorites": "收藏" }, "header": { "search": "搜索游戏", diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 570fa378..dc64b40e 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -13,6 +13,8 @@ import "./catalogue/get-developers"; import "./hardware/get-disk-free-space"; import "./hardware/check-folder-write-permission"; 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/close-game"; import "./library/delete-game-folder"; diff --git a/src/main/events/library/add-game-to-favorites.ts b/src/main/events/library/add-game-to-favorites.ts new file mode 100644 index 00000000..8371b366 --- /dev/null +++ b/src/main/events/library/add-game-to-favorites.ts @@ -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); diff --git a/src/main/events/library/remove-game-from-favorites.ts b/src/main/events/library/remove-game-from-favorites.ts new file mode 100644 index 00000000..c802ab0b --- /dev/null +++ b/src/main/events/library/remove-game-from-favorites.ts @@ -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); diff --git a/src/preload/index.ts b/src/preload/index.ts index e1393269..ef61cbb9 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -110,11 +110,16 @@ contextBridge.exposeInMainWorld("electron", { executablePath: string | null ) => 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: ( shop: GameShop, objectId: string, launchOptions: string | null ) => ipcRenderer.invoke("updateLaunchOptions", shop, objectId, launchOptions), + selectGameWinePrefix: ( shop: GameShop, objectId: string, diff --git a/src/renderer/src/components/sidebar/sidebar-game-item.tsx b/src/renderer/src/components/sidebar/sidebar-game-item.tsx new file mode 100644 index 00000000..0672f847 --- /dev/null +++ b/src/renderer/src/components/sidebar/sidebar-game-item.tsx @@ -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) { + const location = useLocation(); + + return ( +
  • + +
  • + ); +} diff --git a/src/renderer/src/components/sidebar/sidebar.tsx b/src/renderer/src/components/sidebar/sidebar.tsx index 5801de37..a9139fdb 100644 --- a/src/renderer/src/components/sidebar/sidebar.tsx +++ b/src/renderer/src/components/sidebar/sidebar.tsx @@ -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() { +
    + {t("favorites")} + + +
    +
    {t("my_library")} @@ -217,39 +234,16 @@ export function Sidebar() { />
    diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index ab485359..8e31aa83 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -96,6 +96,11 @@ declare global { objectId: string, executablePath: string | null ) => Promise; + addGameToFavorites: (shop: GameShop, objectId: string) => Promise; + removeGameFromFavorites: ( + shop: GameShop, + objectId: string + ) => Promise; updateLaunchOptions: ( shop: GameShop, objectId: string, diff --git a/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx b/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx index 68290313..37e0ff1f 100644 --- a/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx +++ b/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx @@ -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() {
    {gameActionButton()}
    + + +