diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 293a898b..3e5072c7 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -1,6 +1,6 @@
name: Lint
-on: [pull_request, push]
+on: [push]
jobs:
lint:
diff --git a/resources/hydra.db b/resources/hydra.db
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/locales/be/translation.json b/src/locales/be/translation.json
index ccada6a7..c55ec394 100644
--- a/src/locales/be/translation.json
+++ b/src/locales/be/translation.json
@@ -88,7 +88,6 @@
"repacks_modal_description": "Абярыце рэпак, які хочаце сьцягнуць",
"downloads_path": "Шлях сьцягваньня",
"select_folder_hint": "Каб зьмяніць папку па змоўчаньні, адкрыйце",
- "settings": "Налады Hydra",
"download_now": "Сьцягнуць зараз",
"installation_instructions": "Інструкцыя ўсталёўкі",
"installation_instructions_description": "Усталёўка гэтай гульні патрабуе дадатковых крокаў",
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index 0674d1b5..7b54b889 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -86,7 +86,6 @@
"playing_now": "Playing now",
"change": "Change",
"repacks_modal_description": "Choose the repack you want to download",
- "downloads_path": "Downloads path",
"select_folder_hint": "To change the default folder, go to the <0>Settings0>",
"download_now": "Download now",
"installation_instructions": "Installation Instructions",
@@ -96,7 +95,14 @@
"dont_show_it_again": "Don't show it again",
"copy_to_clipboard": "Copy",
"copied_to_clipboard": "Copied",
- "got_it": "Got it"
+ "got_it": "Got it",
+ "no_shop_details": "Could not retrieve shop details.",
+ "download_options": "Download options",
+ "download_path": "Download path",
+ "previous_screenshot": "Previous screenshot",
+ "next_screenshot": "Next screenshot",
+ "screenshot": "Screenshot {{number}}",
+ "open_screenshot": "Open screenshot {{number}}"
},
"activation": {
"title": "Activate Hydra",
@@ -139,7 +145,7 @@
"enable_repack_list_notifications": "When a new repack is added",
"telemetry": "Telemetry",
"telemetry_description": "Enable anonymous usage statistics",
- "real_debrid_api_token_description": "Real Debrid API token",
+ "real_debrid_api_token_label": "Real Debrid API token",
"quit_app_instead_hiding": "Quit Hydra instead of minimizing to tray",
"launch_with_system": "Launch Hydra on system start-up",
"general": "General",
diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json
index a692fd16..1c97adae 100644
--- a/src/locales/es/translation.json
+++ b/src/locales/es/translation.json
@@ -85,7 +85,6 @@
"repacks_modal_description": "Selecciona el repack que quieres descargar",
"downloads_path": "Ruta de descarga",
"select_folder_hint": "Para cambiar la carpeta predeterminada, accede a",
- "settings": "Ajustes",
"download_now": "Descargar ahora",
"installation_instructions": "Instrucciones de instalación",
"installation_instructions_description": "Se requieren de pasos adicionales para instalar este juego",
diff --git a/src/locales/id/translation.json b/src/locales/id/translation.json
index 49d3a991..60de327a 100644
--- a/src/locales/id/translation.json
+++ b/src/locales/id/translation.json
@@ -88,7 +88,6 @@
"repacks_modal_description": "Pilih repack yang kamu ingin unduh",
"downloads_path": "Lokasi Unduhan",
"select_folder_hint": "Untuk merubah folder bawaan, akses melalui",
- "settings": "Pengaturan",
"download_now": "Unduh sekarang",
"installation_instructions": "Instruksi Instalasi",
"installation_instructions_description": "Langkah tambahan dibutuhkan untuk meng-instal game ini",
diff --git a/src/locales/it/translation.json b/src/locales/it/translation.json
index 9889dd0c..57706dc9 100644
--- a/src/locales/it/translation.json
+++ b/src/locales/it/translation.json
@@ -88,7 +88,6 @@
"repacks_modal_description": "Scegli il repack che vuoi scaricare",
"downloads_path": "Percorso dei download",
"select_folder_hint": "Per cambiare la cartella predefinita, accedi alle",
- "settings": "Impostazioni",
"download_now": "Scarica ora",
"installation_instructions": "Istruzioni di installazione",
"installation_instructions_description": "Sono necessari passaggi aggiuntivi per installare questo gioco",
diff --git a/src/locales/nl/translation.json b/src/locales/nl/translation.json
index 22cb1d90..4be69007 100644
--- a/src/locales/nl/translation.json
+++ b/src/locales/nl/translation.json
@@ -139,7 +139,7 @@
"enable_repack_list_notifications": "Wanneer een nieuwe herverpakking wordt toegevoegd",
"telemetry": "Telemetrie",
"telemetry_description": "Schakel anonieme gebruiksstatistieken in",
- "real_debrid_api_token_description": "Real Debrid API token",
+ "real_debrid_api_token_label": "Real Debrid API token",
"quit_app_instead_hiding": "Sluit Hydra af in plaats van te minimaliseren naar de lade",
"launch_with_system": "Start Hydra bij het opstarten van het systeem",
"general": "Algemeen",
diff --git a/src/locales/pl/translation.json b/src/locales/pl/translation.json
index 5623c74e..5214019e 100644
--- a/src/locales/pl/translation.json
+++ b/src/locales/pl/translation.json
@@ -82,7 +82,6 @@
"repacks_modal_description": "Wybierz repack, który chcesz pobrać",
"downloads_path": "Ścieżka pobierania",
"select_folder_hint": "Aby zmienić domyślny folder, przejdź do",
- "settings": "Ustawienia Hydra",
"download_now": "Pobierz teraz"
},
"activation": {
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index dda53065..57ec0470 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -82,7 +82,6 @@
"playing_now": "Jogando agora",
"change": "Mudar",
"repacks_modal_description": "Escolha o repack do jogo que deseja baixar",
- "downloads_path": "Diretório do download",
"select_folder_hint": "Para trocar a pasta padrão, acesse a <0>Tela de Configurações0>",
"download_now": "Baixe agora",
"installation_instructions": "Instruções de Instalação",
@@ -92,7 +91,14 @@
"dont_show_it_again": "Não mostrar novamente",
"copy_to_clipboard": "Copiar",
"copied_to_clipboard": "Copiado",
- "got_it": "Entendi"
+ "got_it": "Entendi",
+ "no_shop_details": "Não foi possível obter os detalhes da loja.",
+ "download_options": "Opções de download",
+ "download_path": "Diretório de download",
+ "previous_screenshot": "Captura de tela anterior",
+ "next_screenshot": "Próxima captura de tela",
+ "screenshot": "Captura de tela {{number}}",
+ "open_screenshot": "Ver captura de tela {{number}}"
},
"activation": {
"title": "Ativação",
@@ -123,7 +129,9 @@
"delete_modal_description": "Isso removerá todos os arquivos de instalação do seu computador",
"delete_modal_title": "Tem certeza?",
"deleting": "Excluindo instalador…",
- "install": "Instalar"
+ "install": "Instalar",
+ "torrent": "Torrent",
+ "real_debrid": "Real Debrid"
},
"settings": {
"downloads_path": "Diretório dos downloads",
@@ -133,6 +141,7 @@
"enable_repack_list_notifications": "Quando a lista de repacks for atualizada",
"telemetry": "Telemetria",
"telemetry_description": "Habilitar estatísticas de uso anônimas",
+ "real_debrid_api_token_label": "Token de API do Real Debrid",
"quit_app_instead_hiding": "Fechar o aplicativo em vez de minimizá-lo",
"launch_with_system": "Iniciar aplicativo na inicialização do sistema",
"general": "Geral",
diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json
index efeaba37..c2c30cd5 100644
--- a/src/locales/ru/translation.json
+++ b/src/locales/ru/translation.json
@@ -88,7 +88,6 @@
"repacks_modal_description": "Выберите репак для загрузки",
"downloads_path": "Путь загрузок",
"select_folder_hint": "Изменить папку по умолчанию",
- "settings": "Настройки Hydra",
"download_now": "Загрузить сейчас",
"installation_instructions": "Инструкция по установке",
"installation_instructions_description": "Для установки этой игры требуются дополнительные шаги",
diff --git a/src/locales/tr/translation.json b/src/locales/tr/translation.json
index 6a7033b3..be40e013 100644
--- a/src/locales/tr/translation.json
+++ b/src/locales/tr/translation.json
@@ -88,7 +88,6 @@
"repacks_modal_description": "İndirmek istediğiiniz repacki seçin",
"downloads_path": "İndirme yolu",
"select_folder_hint": "Varsayılan klasörü değiştirmek için ulaşmanız gereken ayar",
- "settings": "Ayarlar",
"download_now": "Şimdi",
"installation_instructions": "Kurulum",
"installation_instructions_description": "Bu oyunu kurmak için ek adımlar gerekiyor",
diff --git a/src/locales/uk/translation.json b/src/locales/uk/translation.json
index e8d1c117..e0f6af7b 100644
--- a/src/locales/uk/translation.json
+++ b/src/locales/uk/translation.json
@@ -88,7 +88,6 @@
"repacks_modal_description": "Виберіть репак, який хочете завантажити",
"downloads_path": "Шлях завантажень",
"select_folder_hint": "Щоб змінити теку за замовчуванням, відкрийте",
- "settings": "Налаштування Hydra",
"download_now": "Завантажити зараз",
"installation_instructions": "Інструкція зі встановлення",
"installation_instructions_description": "Для встановлення цієї гри потрібні додаткові кроки",
diff --git a/src/main/events/catalogue/get-game-shop-details.ts b/src/main/events/catalogue/get-game-shop-details.ts
index 61629242..bbc3b08a 100644
--- a/src/main/events/catalogue/get-game-shop-details.ts
+++ b/src/main/events/catalogue/get-game-shop-details.ts
@@ -1,10 +1,32 @@
-import { gameShopCacheRepository } from "@main/repository";
+import { gameShopCacheRepository, steamGameRepository } from "@main/repository";
import { getSteamAppDetails } from "@main/services";
import type { ShopDetails, GameShop, SteamAppDetails } from "@types";
import { registerEvent } from "../register-event";
-import { searchRepacks } from "../helpers/search-games";
+
+const getLocalizedSteamAppDetails = (
+ objectID: string,
+ language: string
+): Promise => {
+ if (language === "english") {
+ return getSteamAppDetails(objectID, language);
+ }
+
+ return Promise.all([
+ steamGameRepository.findOne({ where: { id: Number(objectID) } }),
+ getSteamAppDetails(objectID, language),
+ ]).then(([steamGame, localizedAppDetails]) => {
+ if (steamGame && localizedAppDetails) {
+ return {
+ ...localizedAppDetails,
+ name: steamGame.name,
+ };
+ }
+
+ return null;
+ });
+};
const getGameShopDetails = async (
_event: Electron.IpcMainInvokeEvent,
@@ -17,27 +39,21 @@ const getGameShopDetails = async (
where: { objectID, language },
});
- const result = Promise.all([
- getSteamAppDetails(objectID, "english"),
- getSteamAppDetails(objectID, language),
- ]).then(([appDetails, localizedAppDetails]) => {
- if (appDetails && localizedAppDetails) {
+ const appDetails = getLocalizedSteamAppDetails(objectID, language).then(
+ (result) => {
gameShopCacheRepository.upsert(
{
objectID,
shop: "steam",
language,
- serializedData: JSON.stringify({
- ...localizedAppDetails,
- name: appDetails.name,
- }),
+ serializedData: JSON.stringify(result),
},
["objectID"]
);
- }
- return [appDetails, localizedAppDetails];
- });
+ return result;
+ }
+ );
const cachedGame = cachedData?.serializedData
? (JSON.parse(cachedData?.serializedData) as SteamAppDetails)
@@ -46,21 +62,11 @@ const getGameShopDetails = async (
if (cachedGame) {
return {
...cachedGame,
- repacks: searchRepacks(cachedGame.name),
objectID,
} as ShopDetails;
}
- return result.then(([appDetails, localizedAppDetails]) => {
- if (!appDetails || !localizedAppDetails) return null;
-
- return {
- ...localizedAppDetails,
- name: appDetails.name,
- repacks: searchRepacks(appDetails.name),
- objectID,
- } as ShopDetails;
- });
+ return Promise.resolve(appDetails);
}
throw new Error("Not implemented");
diff --git a/src/main/events/catalogue/get-random-game.ts b/src/main/events/catalogue/get-random-game.ts
index 72f9cd90..dd3741e3 100644
--- a/src/main/events/catalogue/get-random-game.ts
+++ b/src/main/events/catalogue/get-random-game.ts
@@ -1,9 +1,10 @@
import { shuffle } from "lodash-es";
-import { Steam250Game, getSteam250List } from "@main/services";
+import { getSteam250List } from "@main/services";
import { registerEvent } from "../register-event";
import { searchGames, searchRepacks } from "../helpers/search-games";
+import type { Steam250Game } from "@types";
const state = { games: Array(), index: 0 };
@@ -25,8 +26,6 @@ const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => {
return "";
}
- const resultObjectId = state.games[state.index].objectID;
-
state.index += 1;
if (state.index == state.games.length) {
@@ -34,7 +33,7 @@ const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => {
state.games = shuffle(state.games);
}
- return resultObjectId;
+ return state.games[state.index];
};
registerEvent(getRandomGame, {
diff --git a/src/main/events/catalogue/search-game-repacks.ts b/src/main/events/catalogue/search-game-repacks.ts
new file mode 100644
index 00000000..448c6daf
--- /dev/null
+++ b/src/main/events/catalogue/search-game-repacks.ts
@@ -0,0 +1,14 @@
+import { searchRepacks } from "../helpers/search-games";
+import { registerEvent } from "../register-event";
+
+const searchGameRepacks = (
+ _event: Electron.IpcMainInvokeEvent,
+ query: string
+) => {
+ return searchRepacks(query);
+};
+
+registerEvent(searchGameRepacks, {
+ name: "searchGameRepacks",
+ memoize: true,
+});
diff --git a/src/main/events/index.ts b/src/main/events/index.ts
index 822cb9d5..ab35ff79 100644
--- a/src/main/events/index.ts
+++ b/src/main/events/index.ts
@@ -7,6 +7,7 @@ import "./catalogue/get-games";
import "./catalogue/get-how-long-to-beat";
import "./catalogue/get-random-game";
import "./catalogue/search-games";
+import "./catalogue/search-game-repacks";
import "./hardware/get-disk-free-space";
import "./library/add-game-to-library";
import "./library/close-game";
diff --git a/src/main/services/real-debrid.ts b/src/main/services/real-debrid.ts
index 44798062..355a59b3 100644
--- a/src/main/services/real-debrid.ts
+++ b/src/main/services/real-debrid.ts
@@ -12,8 +12,7 @@ export class RealDebridClient {
private static instance: AxiosInstance;
static async addMagnet(magnet: string) {
- const searchParams = new URLSearchParams();
- searchParams.append("magnet", magnet);
+ const searchParams = new URLSearchParams({ magnet });
const response = await this.instance.post(
"/torrents/addMagnet",
@@ -31,8 +30,7 @@ export class RealDebridClient {
}
static async selectAllFiles(id: string) {
- const searchParams = new URLSearchParams();
- searchParams.append("files", "all");
+ const searchParams = new URLSearchParams({ files: "all" });
await this.instance.post(
`/torrents/selectFiles/${id}`,
@@ -41,8 +39,7 @@ export class RealDebridClient {
}
static async unrestrictLink(link: string) {
- const searchParams = new URLSearchParams();
- searchParams.append("link", link);
+ const searchParams = new URLSearchParams({ link });
const response = await this.instance.post(
"/unrestrict/link",
diff --git a/src/main/services/steam-250.ts b/src/main/services/steam-250.ts
index db505b47..9833c278 100644
--- a/src/main/services/steam-250.ts
+++ b/src/main/services/steam-250.ts
@@ -1,10 +1,7 @@
import axios from "axios";
import { JSDOM } from "jsdom";
-export interface Steam250Game {
- title: string;
- objectID: string;
-}
+import type { Steam250Game } from "@types";
export const requestSteam250 = async (path: string) => {
return axios
diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts
deleted file mode 100644
index 51163f1a..00000000
--- a/src/preload/index.d.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-// See the Electron documentation for details on how to use preload scripts:
-// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
-import { contextBridge, ipcRenderer } from "electron";
-
-import type {
- CatalogueCategory,
- GameShop,
- TorrentProgress,
- UserPreferences,
-} from "@types";
-
-contextBridge.exposeInMainWorld("electron", {
- /* Torrenting */
- startGameDownload: (
- repackId: number,
- objectID: string,
- title: string,
- shop: GameShop
- ) => ipcRenderer.invoke("startGameDownload", repackId, objectID, title, shop),
- cancelGameDownload: (gameId: number) =>
- ipcRenderer.invoke("cancelGameDownload", gameId),
- pauseGameDownload: (gameId: number) =>
- ipcRenderer.invoke("pauseGameDownload", gameId),
- resumeGameDownload: (gameId: number) =>
- ipcRenderer.invoke("resumeGameDownload", gameId),
- onDownloadProgress: (cb: (value: TorrentProgress) => void) => {
- const listener = (
- _event: Electron.IpcRendererEvent,
- value: TorrentProgress
- ) => cb(value);
- ipcRenderer.on("on-download-progress", listener);
- return () => ipcRenderer.removeListener("on-download-progress", listener);
- },
-
- /* Catalogue */
- searchGames: (query: string) => ipcRenderer.invoke("searchGames", query),
- getCatalogue: (category: CatalogueCategory) =>
- ipcRenderer.invoke("getCatalogue", category),
- getGameShopDetails: (objectID: string, shop: GameShop, language: string) =>
- ipcRenderer.invoke("getGameShopDetails", objectID, shop, language),
- getRandomGame: () => ipcRenderer.invoke("getRandomGame"),
- getHowLongToBeat: (objectID: string, shop: GameShop, title: string) =>
- ipcRenderer.invoke("getHowLongToBeat", objectID, shop, title),
- getGames: (take?: number, prevCursor?: number) =>
- ipcRenderer.invoke("getGames", take, prevCursor),
-
- /* User preferences */
- getUserPreferences: () => ipcRenderer.invoke("getUserPreferences"),
- updateUserPreferences: (preferences: UserPreferences) =>
- ipcRenderer.invoke("updateUserPreferences", preferences),
- autoLaunch: (enabled: boolean) => ipcRenderer.invoke("autoLaunch", enabled),
-
- /* Library */
- addGameToLibrary: (
- objectID: string,
- title: string,
- shop: GameShop,
- executablePath: string
- ) =>
- ipcRenderer.invoke(
- "addGameToLibrary",
- objectID,
- title,
- shop,
- executablePath
- ),
- getLibrary: () => ipcRenderer.invoke("getLibrary"),
- getRepackersFriendlyNames: () =>
- ipcRenderer.invoke("getRepackersFriendlyNames"),
- openGameInstaller: (gameId: number) =>
- ipcRenderer.invoke("openGameInstaller", gameId),
- openGame: (gameId: number, executablePath: string) =>
- ipcRenderer.invoke("openGame", gameId, executablePath),
- closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId),
- removeGameFromLibrary: (gameId: number) =>
- ipcRenderer.invoke("removeGameFromLibrary", gameId),
- deleteGameFolder: (gameId: number) =>
- ipcRenderer.invoke("deleteGameFolder", gameId),
- getGameByObjectID: (objectID: string) =>
- ipcRenderer.invoke("getGameByObjectID", objectID),
- onPlaytime: (cb: (gameId: number) => void) => {
- const listener = (_event: Electron.IpcRendererEvent, gameId: number) =>
- cb(gameId);
- ipcRenderer.on("on-playtime", listener);
- return () => ipcRenderer.removeListener("on-playtime", listener);
- },
- onGameClose: (cb: (gameId: number) => void) => {
- const listener = (_event: Electron.IpcRendererEvent, gameId: number) =>
- cb(gameId);
- ipcRenderer.on("on-game-close", listener);
- return () => ipcRenderer.removeListener("on-game-close", listener);
- },
-
- /* Hardware */
- getDiskFreeSpace: () => ipcRenderer.invoke("getDiskFreeSpace"),
-
- /* Misc */
- ping: () => ipcRenderer.invoke("ping"),
- getVersion: () => ipcRenderer.invoke("getVersion"),
- getDefaultDownloadsPath: () => ipcRenderer.invoke("getDefaultDownloadsPath"),
- openExternal: (src: string) => ipcRenderer.invoke("openExternal", src),
- showOpenDialog: (options: Electron.OpenDialogOptions) =>
- ipcRenderer.invoke("showOpenDialog", options),
- platform: process.platform,
-});
diff --git a/src/preload/index.ts b/src/preload/index.ts
index 7d5eb7fe..9151942f 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -52,6 +52,8 @@ contextBridge.exposeInMainWorld("electron", {
ipcRenderer.invoke("getHowLongToBeat", objectID, shop, title),
getGames: (take?: number, prevCursor?: number) =>
ipcRenderer.invoke("getGames", take, prevCursor),
+ searchGameRepacks: (query: string) =>
+ ipcRenderer.invoke("searchGameRepacks", query),
/* User preferences */
getUserPreferences: () => ipcRenderer.invoke("getUserPreferences"),
diff --git a/src/renderer/src/components/game-card/game-card.css.ts b/src/renderer/src/components/game-card/game-card.css.ts
index 9f2f0654..1f45c106 100644
--- a/src/renderer/src/components/game-card/game-card.css.ts
+++ b/src/renderer/src/components/game-card/game-card.css.ts
@@ -1,31 +1,18 @@
import { style } from "@vanilla-extract/css";
-import { recipe } from "@vanilla-extract/recipes";
import { SPACING_UNIT, vars } from "../../theme.css";
-export const card = recipe({
- base: {
- width: "100%",
- height: "180px",
- boxShadow: "0px 0px 15px 0px #000000",
- overflow: "hidden",
- borderRadius: "4px",
- transition: "all ease 0.2s",
- border: `solid 1px ${vars.color.border}`,
- cursor: "pointer",
- zIndex: "1",
- ":active": {
- opacity: vars.opacity.active,
- },
- },
- variants: {
- disabled: {
- true: {
- pointerEvents: "none",
- boxShadow: "none",
- opacity: vars.opacity.disabled,
- filter: "grayscale(50%)",
- },
- },
+export const card = style({
+ width: "100%",
+ height: "180px",
+ boxShadow: "0px 0px 15px 0px #000000",
+ overflow: "hidden",
+ borderRadius: "4px",
+ transition: "all ease 0.2s",
+ border: `solid 1px ${vars.color.border}`,
+ cursor: "pointer",
+ zIndex: "1",
+ ":active": {
+ opacity: vars.opacity.active,
},
});
@@ -48,7 +35,7 @@ export const cover = style({
zIndex: "-1",
transition: "all ease 0.2s",
selectors: {
- [`${card({})}:hover &`]: {
+ [`${card}:hover &`]: {
transform: "scale(1.05)",
},
},
@@ -64,7 +51,7 @@ export const content = style({
transition: "all ease 0.2s",
transform: "translateY(24px)",
selectors: {
- [`${card({})}:hover &`]: {
+ [`${card}:hover &`]: {
transform: "translateY(0px)",
},
},
diff --git a/src/renderer/src/components/game-card/game-card.tsx b/src/renderer/src/components/game-card/game-card.tsx
index f7f6ffe4..b3ac3fa2 100644
--- a/src/renderer/src/components/game-card/game-card.tsx
+++ b/src/renderer/src/components/game-card/game-card.tsx
@@ -14,7 +14,6 @@ export interface GameCardProps
HTMLButtonElement
> {
game: CatalogueEntry;
- disabled?: boolean;
}
const shopIcon = {
@@ -22,7 +21,7 @@ const shopIcon = {
steam: ,
};
-export function GameCard({ game, disabled, ...props }: GameCardProps) {
+export function GameCard({ game, ...props }: GameCardProps) {
const { t } = useTranslation("game_card");
const repackersFriendlyNames = useAppSelector(
@@ -34,12 +33,7 @@ export function GameCard({ game, disabled, ...props }: GameCardProps) {
);
return (
-
- {t("publisher", { publisher: gameDetails?.publishers[0] })}
+ {t("publisher", { publisher: gameDetails.publishers[0] })}
(null);
+ const mediaContainerRef = useRef(null);
+
+ const { t } = useTranslation("game_details");
+
+ const hasScreenshots = gameDetails && gameDetails.screenshots.length;
+ const hasMovies = gameDetails && gameDetails.movies?.length;
const [mediaCount] = useState(() => {
- if (gameDetails) {
- if (gameDetails.screenshots && gameDetails.movies) {
- return gameDetails.screenshots.length + gameDetails.movies.length;
- } else if (gameDetails.movies) {
- return gameDetails.movies.length;
- } else if (gameDetails.screenshots) {
- return gameDetails.screenshots.length;
- }
+ if (gameDetails.screenshots && gameDetails.movies) {
+ return gameDetails.screenshots.length + gameDetails.movies.length;
+ } else if (gameDetails.movies) {
+ return gameDetails.movies.length;
+ } else if (gameDetails.screenshots) {
+ return gameDetails.screenshots.length;
}
return 0;
});
const [mediaIndex, setMediaIndex] = useState(0);
- const [arrowShow, setArrowShow] = useState(false);
+ const [showArrows, setShowArrows] = useState(false);
const showNextImage = () => {
setMediaIndex((index: number) => {
@@ -47,6 +54,20 @@ export function GallerySlider({ gameDetails }: GallerySliderProps) {
setMediaIndex(0);
}, [gameDetails]);
+ useEffect(() => {
+ if (hasMovies && mediaContainerRef.current) {
+ mediaContainerRef.current.childNodes.forEach((node, index) => {
+ if (node instanceof HTMLVideoElement) {
+ if (index == mediaIndex) {
+ node.play();
+ } else {
+ node.pause();
+ }
+ }
+ });
+ }
+ }, [hasMovies, mediaContainerRef, mediaIndex]);
+
useEffect(() => {
if (scrollContainerRef.current) {
const container = scrollContainerRef.current;
@@ -57,92 +78,107 @@ export function GallerySlider({ gameDetails }: GallerySliderProps) {
}
}, [gameDetails, mediaIndex, mediaCount]);
- const hasScreenshots = gameDetails && gameDetails.screenshots.length;
- const hasMovies = gameDetails && gameDetails.movies?.length;
+ const previews = useMemo(() => {
+ const screenshotPreviews =
+ gameDetails?.screenshots.map(({ id, path_thumbnail }) => ({
+ id,
+ thumbnail: path_thumbnail,
+ })) ?? [];
+
+ if (gameDetails?.movies) {
+ const moviePreviews = gameDetails.movies.map(({ id, thumbnail }) => ({
+ id,
+ thumbnail,
+ }));
+
+ return [...moviePreviews, ...screenshotPreviews];
+ }
+
+ return screenshotPreviews;
+ }, [gameDetails]);
return (
<>
{hasScreenshots && (
setArrowShow(true)}
- onMouseLeave={() => setArrowShow(false)}
+ onMouseEnter={() => setShowArrows(true)}
+ onMouseLeave={() => setShowArrows(false)}
className={styles.gallerySliderAnimationContainer}
+ ref={mediaContainerRef}
>
{gameDetails.movies &&
- gameDetails.movies.map((video: SteamMovies) => (
+ gameDetails.movies.map((video) => (
))}
- {gameDetails.screenshots &&
- gameDetails.screenshots.map(
- (image: SteamScreenshot, i: number) => (
-
![]({image.path_full})
- )
- )}
- {arrowShow && (
- <>
-
-
-
-
-
-
- >
- )}
+ {hasScreenshots &&
+ gameDetails.screenshots.map((image, i) => (
+
![{t("screenshot",]({image.path_full})
+ ))}
+
+
+
+
+
+
+
+
- {hasMovies &&
- gameDetails.movies?.map((video: SteamMovies, i: number) => (
+ {previews.map((media, i) => (
+
setMediaIndex(i)}
+ aria-label={t("open_screenshot", { number: i + 1 })}
+ >
setMediaIndex(i)}
- src={video.thumbnail}
- className={`${styles.gallerySliderMediaPreview} ${mediaIndex === i ? styles.gallerySliderMediaPreviewActive : ""}`}
+ src={media.thumbnail}
+ className={styles.mediaPreview}
+ alt={t("screenshot", { number: i + 1 })}
/>
- ))}
- {gameDetails.screenshots &&
- gameDetails.screenshots.map(
- (image: SteamScreenshot, i: number) => (
-
- setMediaIndex(
- i + (gameDetails.movies ? gameDetails.movies.length : 0)
- )
- }
- className={`${styles.gallerySliderMediaPreview} ${mediaIndex === i + (gameDetails.movies ? gameDetails.movies.length : 0) ? styles.gallerySliderMediaPreviewActive : ""}`}
- src={image.path_full}
- />
- )
- )}
+
+ ))}
)}
diff --git a/src/renderer/src/pages/game-details/game-details-skeleton.tsx b/src/renderer/src/pages/game-details/game-details-skeleton.tsx
index 1334362b..be481247 100644
--- a/src/renderer/src/pages/game-details/game-details-skeleton.tsx
+++ b/src/renderer/src/pages/game-details/game-details-skeleton.tsx
@@ -1,7 +1,10 @@
import Skeleton from "react-loading-skeleton";
import { Button } from "@renderer/components";
+
import * as styles from "./game-details.css";
+import * as sidebarStyles from "./sidebar/sidebar.css";
+
import { useTranslation } from "react-i18next";
import { ShareAndroidIcon } from "@primer/octicons-react";
@@ -43,41 +46,41 @@ export function GameDetailsSkeleton() {
-