From d3fb60e4369b3d83e3e09f64794f36d5ed947ed3 Mon Sep 17 00:00:00 2001 From: Hydra Date: Mon, 15 Apr 2024 03:59:49 +0100 Subject: [PATCH 1/4] feat: adding mark game as favorite button --- src/locales/en/translation.json | 4 +- src/locales/es/translation.json | 4 +- src/locales/fr/translation.json | 8 ++- src/locales/pt/translation.json | 4 +- src/main/entity/game.entity.ts | 4 +- src/main/events/index.ts | 3 +- .../events/library/add-game-to-library.ts | 27 ++++++++++ src/preload.ts | 6 ++- src/renderer/declaration.d.ts | 17 ++++--- .../pages/game-details/hero-panel.tsx | 49 +++++++++++++++++-- 10 files changed, 105 insertions(+), 21 deletions(-) create mode 100644 src/main/events/library/add-game-to-library.ts diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 1f1f7b638..cb0ac574d 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -60,7 +60,9 @@ "copied_link_to_clipboard": "Link copied", "hours": "hours", "minutes": "minutes", - "accuracy": "{{accuracy}}% accuracy" + "accuracy": "{{accuracy}}% accuracy", + "add_to_library": "Add to library", + "added_to_library": "Added to library" }, "activation": { "title": "Activate Hydra", diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index d059c08c7..cbeebcc0a 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -60,7 +60,9 @@ "copied_link_to_clipboard": "Enlace copiado", "hours": "horas", "minutes": "minutos", - "accuracy": "{{accuracy}}% precisión" + "accuracy": "{{accuracy}}% precisión", + "add_to_library": "Añadir a biblioteca", + "added_to_library": "Añadido a biblioteca" }, "activation": { "title": "Activar Hydra", diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json index 19cbfe13b..b2f5b43be 100644 --- a/src/locales/fr/translation.json +++ b/src/locales/fr/translation.json @@ -57,7 +57,12 @@ "release_date": "Sorti le {{date}}", "publisher": "Édité par {{publisher}}", "copy_link_to_clipboard": "Copier le lien", - "copied_link_to_clipboard": "Lien copié" + "copied_link_to_clipboard": "Lien copié", + "hours": "heures", + "minutes": "minutes", + "accuracy": "{{accuracy}}% précision", + "add_to_library": "Ajouter à la bibliothèque", + "added_to_library": "Ajouté à la bibliothèque" }, "activation": { "title": "Activer Hydra", @@ -109,4 +114,3 @@ "no_downloads": "Aucun téléchargement disponible" } } - diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 0342fb893..106824b4e 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -60,7 +60,9 @@ "copied_link_to_clipboard": "Link copiado", "hours": "horas", "minutes": "minutos", - "accuracy": "{{accuracy}}% de precisão" + "accuracy": "{{accuracy}}% de precisão", + "add_to_library": "Adicionar à biblioteca", + "added_to_library": "Adicionado à biblioteca" }, "activation": { "title": "Ativação", diff --git a/src/main/entity/game.entity.ts b/src/main/entity/game.entity.ts index 86580fb3e..37c8581fe 100644 --- a/src/main/entity/game.entity.ts +++ b/src/main/entity/game.entity.ts @@ -33,7 +33,7 @@ export class Game { @Column("text") shop: GameShop; - @Column("text") + @Column("text", { nullable: true }) status: string; @Column("float", { default: 0 }) @@ -48,7 +48,7 @@ export class Game { @Column("float", { default: 0 }) fileSize: number; - @OneToOne(() => Repack) + @OneToOne(() => Repack, { nullable: true }) @JoinColumn() repack: Repack; diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 4910fbc5f..64ac71443 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -1,7 +1,7 @@ import { app, ipcMain } from "electron"; import { defaultDownloadsPath } from "@main/constants"; -import "./torrenting/start-game-download"; +import "./library/add-game-to-library"; import "./catalogue/search-games"; import "./catalogue/get-game-shop-details"; import "./catalogue/get-catalogue"; @@ -10,6 +10,7 @@ import "./hardware/get-disk-free-space"; import "./torrenting/cancel-game-download"; import "./torrenting/pause-game-download"; import "./torrenting/resume-game-download"; +import "./torrenting/start-game-download"; import "./misc/get-or-cache-image"; import "./user-preferences/update-user-preferences"; import "./user-preferences/get-user-preferences"; diff --git a/src/main/events/library/add-game-to-library.ts b/src/main/events/library/add-game-to-library.ts new file mode 100644 index 000000000..8680b29a6 --- /dev/null +++ b/src/main/events/library/add-game-to-library.ts @@ -0,0 +1,27 @@ +import { gameRepository } from "@main/repository"; + +import { registerEvent } from "../register-event"; + +import type { GameShop } from "@types"; +import { getImageBase64 } from "@main/helpers"; +import { getSteamGameIconUrl } from "@main/services"; + +const addGameToLibrary = async ( + _event: Electron.IpcMainInvokeEvent, + objectID: string, + title: string, + gameShop: GameShop, +) => { + const iconUrl = await getImageBase64(await getSteamGameIconUrl(objectID)); + + return gameRepository.insert({ + title, + iconUrl, + objectID, + shop: gameShop, + }); +}; + +registerEvent(addGameToLibrary, { + name: "addGameToLibrary", +}); diff --git a/src/preload.ts b/src/preload.ts index 939f87ca1..22cd36536 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -15,7 +15,7 @@ contextBridge.exposeInMainWorld("electron", { repackId: number, objectID: string, title: string, - shop: GameShop + shop: GameShop, ) => ipcRenderer.invoke("startGameDownload", repackId, objectID, title, shop), cancelGameDownload: (gameId: number) => ipcRenderer.invoke("cancelGameDownload", gameId), @@ -26,7 +26,7 @@ contextBridge.exposeInMainWorld("electron", { onDownloadProgress: (callback: (value: TorrentProgress) => void) => { const listener = ( _event: Electron.IpcRendererEvent, - value: TorrentProgress + value: TorrentProgress, ) => callback(value); ipcRenderer.on("on-download-progress", listener); return () => ipcRenderer.removeListener("on-download-progress", listener); @@ -48,6 +48,8 @@ contextBridge.exposeInMainWorld("electron", { ipcRenderer.invoke("updateUserPreferences", preferences), /* Library */ + addGameToLibrary: (objectID: string, title: string, shop: GameShop) => + ipcRenderer.invoke("addGameToLibrary", objectID, title, shop), getLibrary: () => ipcRenderer.invoke("getLibrary"), getRepackersFriendlyNames: () => ipcRenderer.invoke("getRepackersFriendlyNames"), diff --git a/src/renderer/declaration.d.ts b/src/renderer/declaration.d.ts index 00377675a..2f2acf9e3 100644 --- a/src/renderer/declaration.d.ts +++ b/src/renderer/declaration.d.ts @@ -22,13 +22,13 @@ declare global { repackId: number, objectID: string, title: string, - shop: GameShop + shop: GameShop, ) => Promise; cancelGameDownload: (gameId: number) => Promise; pauseGameDownload: (gameId: number) => Promise; resumeGameDownload: (gameId: number) => Promise; onDownloadProgress: ( - cb: (value: TorrentProgress) => void + cb: (value: TorrentProgress) => void, ) => () => Electron.IpcRenderer; /* Catalogue */ @@ -37,16 +37,21 @@ declare global { getGameShopDetails: ( objectID: string, shop: GameShop, - language: string + language: string, ) => Promise; getRandomGame: () => Promise; getHowLongToBeat: ( objectID: string, shop: GameShop, - title: string + title: string, ) => Promise; /* Library */ + addGameToLibrary: ( + objectID: string, + title: string, + shop: GameShop, + ) => Promise; getLibrary: () => Promise; getRepackersFriendlyNames: () => Promise>; openGame: (gameId: number) => Promise; @@ -57,7 +62,7 @@ declare global { /* User preferences */ getUserPreferences: () => Promise; updateUserPreferences: ( - preferences: Partial + preferences: Partial, ) => Promise; /* Hardware */ @@ -69,7 +74,7 @@ declare global { ping: () => string; getDefaultDownloadsPath: () => Promise; showOpenDialog: ( - options: Electron.OpenDialogOptions + options: Electron.OpenDialogOptions, ) => Promise; platform: NodeJS.Platform; } diff --git a/src/renderer/pages/game-details/hero-panel.tsx b/src/renderer/pages/game-details/hero-panel.tsx index 6189a261a..8af4a365f 100644 --- a/src/renderer/pages/game-details/hero-panel.tsx +++ b/src/renderer/pages/game-details/hero-panel.tsx @@ -1,4 +1,4 @@ -import { useMemo } from "react"; +import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import prettyBytes from "pretty-bytes"; import { format } from "date-fns"; @@ -9,6 +9,7 @@ import type { Game, ShopDetails } from "@types"; import * as styles from "./hero-panel.css"; import { formatDownloadProgress } from "@renderer/helpers"; +import { HeartFillIcon, HeartIcon } from "@primer/octicons-react"; export interface HeroPanelProps { game: Game | null; @@ -41,7 +42,14 @@ export function HeroPanel({ removeGame, isGameDeleting, } = useDownload(); - const { updateLibrary } = useLibrary(); + const { updateLibrary, library } = useLibrary(); + + const [toggleLibraryGameDisabled, setToggleLibraryGameDisabled] = + useState(false); + + const gameOnLibrary = library.find( + ({ objectID }) => objectID === gameDetails?.objectID, + ); const isGameDownloading = isDownloading && gameDownloading?.id === game?.id; @@ -60,6 +68,26 @@ export function HeroPanel({ return game.repack?.fileSize ?? "N/A"; }, [game, isGameDownloading, gameDownloading]); + const toggleLibraryGame = async () => { + setToggleLibraryGameDisabled(true); + + try { + if (gameOnLibrary) { + await window.electron.removeGame(gameOnLibrary.id); + } else { + await window.electron.addGameToLibrary( + gameDetails.objectID, + gameDetails.name, + "steam", + ); + } + + await updateLibrary(); + } finally { + setToggleLibraryGameDisabled(false); + } + }; + const getInfo = () => { if (!gameDetails) return null; @@ -194,9 +222,20 @@ export function HeroPanel({ } return ( - + <> + + + + ); }; From cb5b6113da5d35b83b985dc4efc6b0c1285cb7db Mon Sep 17 00:00:00 2001 From: Hydra Date: Mon, 15 Apr 2024 04:03:20 +0100 Subject: [PATCH 2/4] feat: adding game to library without downloading it --- .gitignore | 2 + forge.config.ts | 11 ++++- package.json | 3 +- src/locales/en/translation.json | 5 +++ src/locales/es/translation.json | 5 +++ src/locales/pt/translation.json | 5 +++ src/main/events/library/open-game.ts | 44 ++++++++++++++----- src/main/events/misc/generate-lutris-yaml.ts | 37 ++++++++++++++++ src/main/services/window-manager.ts | 2 + src/renderer/declaration.d.ts | 2 +- src/renderer/pages/downloads/downloads.tsx | 6 ++- .../pages/game-details/hero-panel.tsx | 20 ++++++--- .../shared-modals/binary-not-found-modal.tsx | 25 +++++++++++ yarn.lock | 5 +++ 14 files changed, 150 insertions(+), 22 deletions(-) create mode 100644 src/main/events/misc/generate-lutris-yaml.ts create mode 100644 src/renderer/pages/shared-modals/binary-not-found-modal.tsx diff --git a/.gitignore b/.gitignore index dadf36d3a..85a90269c 100644 --- a/.gitignore +++ b/.gitignore @@ -93,6 +93,8 @@ out/ .vscode/ +.venv + dev.db __pycache__ diff --git a/forge.config.ts b/forge.config.ts index de42f3ead..b8bf46d3d 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -17,6 +17,7 @@ const config: ForgeConfig = { packagerConfig: { asar: true, icon: "./images/icon.png", + executableName: "Hydra", extraResource: [ "./resources/hydra.db", "./resources/icon_tray.png", @@ -34,11 +35,17 @@ const config: ForgeConfig = { new MakerSquirrel({ setupIcon: "./images/icon.ico", }), - new MakerZIP({}, ["darwin"]), - new MakerRpm({}), + new MakerZIP({}, ["darwin", "linux"]), + new MakerRpm({ + options: { + mimeType: ["x-scheme-handler/hydralauncher"], + bin: './Hydra' + }, + }), new MakerDeb({ options: { mimeType: ["x-scheme-handler/hydralauncher"], + bin: './Hydra' }, }), ], diff --git a/package.json b/package.json index cea50da9d..a08033eb0 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "uuid": "^9.0.1", "vite-plugin-svgr": "^4.2.0", "vite-tsconfig-paths": "^4.3.2", - "winston": "^3.12.0" + "winston": "^3.12.0", + "yaml": "^2.4.1" } } diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index cb0ac574d..ade0d8579 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -112,5 +112,10 @@ }, "game_card": { "no_downloads": "No downloads available" + }, + "binary_not_found_modal": { + "title": "Programs not installed", + "description": "Wine or Lutris executables were not found on your system", + "instructions": "Check the correct way to install any of them on your Linux distro so that the game can run normally" } } diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index cbeebcc0a..91b811754 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -112,5 +112,10 @@ }, "game_card": { "no_downloads": "No hay descargas disponibles" + }, + "binary_not_found_modal": { + "title": "Programas no instalados", + "description": "Los ejecutables de Wine o Lutris no se encontraron en su sistema", + "instructions": "Comprueba la forma correcta de instalar cualquiera de ellos en tu distro Linux para que el juego pueda ejecutarse con normalidad" } } diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 106824b4e..c77ebf3fa 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -112,5 +112,10 @@ }, "game_card": { "no_downloads": "Sem downloads disponíveis" + }, + "binary_not_found_modal": { + "title": "Programas não instalados", + "description": "Não foram encontrados no seu sistema os executáveis do Wine ou Lutris", + "instructions": "Verifique a forma correta de instalar algum deles na sua distro Linux para que o jogo possa ser executado normalmente" } } diff --git a/src/main/events/library/open-game.ts b/src/main/events/library/open-game.ts index 82c097bdb..9b55348c8 100644 --- a/src/main/events/library/open-game.ts +++ b/src/main/events/library/open-game.ts @@ -1,6 +1,9 @@ import { gameRepository } from "@main/repository"; +import { generateYML } from "../misc/generate-lutris-yaml"; import path from "node:path"; import fs from "node:fs"; +import { writeFile } from "node:fs/promises"; +import { spawnSync, exec } from "node:child_process"; import { registerEvent } from "../register-event"; import { shell } from "electron"; @@ -12,25 +15,42 @@ const openGame = async ( ) => { const game = await gameRepository.findOne({ where: { id: gameId } }); - if (!game) return; + if (!game) return true; const gamePath = path.join( game.downloadPath ?? (await getDownloadsPath()), game.folderName ); - if (fs.existsSync(gamePath)) { - const setupPath = path.join(gamePath, "setup.exe"); - if (fs.existsSync(setupPath)) { - shell.openExternal(setupPath); - } else { - shell.openPath(gamePath); - } - } else { - await gameRepository.delete({ - id: gameId, - }); + if (!fs.existsSync(gamePath)) { + await gameRepository.delete({ id: gameId, }); + return true; } + + const setupPath = path.join(gamePath, "setup.exe"); + if (!fs.existsSync(setupPath)) { + shell.openPath(gamePath); + return true; + } + + if (process.platform === "win32") { + shell.openExternal(setupPath); + return true; + } + + if (spawnSync("which", ["lutris"]).status === 0) { + const ymlPath = path.join(gamePath, "setup.yml"); + await writeFile(ymlPath, generateYML(game)); + exec(`lutris --install "${ymlPath}"`); + return true; + } + + if (spawnSync("which", ["wine"]).status === 0) { + exec(`wine "${setupPath}"`); + return true; + } + + return false; }; registerEvent(openGame, { diff --git a/src/main/events/misc/generate-lutris-yaml.ts b/src/main/events/misc/generate-lutris-yaml.ts new file mode 100644 index 000000000..2b6bfed6b --- /dev/null +++ b/src/main/events/misc/generate-lutris-yaml.ts @@ -0,0 +1,37 @@ +import { Document as YMLDocument } from "yaml"; +import { Game } from "@main/entity"; +import path from "node:path"; + +export const generateYML = (game: Game) => { + const slugifiedGameTitle = game.title.replace(/\s/g, "-").toLocaleLowerCase(); + + const doc = new YMLDocument({ + name: game.title, + game_slug: slugifiedGameTitle, + slug: `${slugifiedGameTitle}-installer`, + version: "Installer", + runner: "wine", + script: { + game: { + prefix: "$GAMEDIR", + arch: "win64", + working_dir: "$GAMEDIR" + }, + installer: [{ + task: { + name: "create_prefix", + arch: "win64", + prefix: "$GAMEDIR" + } + }, { + task: { + executable: path.join(game.downloadPath, game.folderName, "setup.exe"), + name: "wineexec", + prefix: "$GAMEDIR" + } + }] + } + }); + + return doc.toString(); +} diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index 4e06ae8fa..5e6b11d2e 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -16,6 +16,8 @@ export class WindowManager { this.mainWindow = new BrowserWindow({ width: 1200, height: 720, + minWidth: 1024, + minHeight: 540, titleBarStyle: "hidden", icon: path.join(__dirname, "..", "..", "images", "icon.png"), trafficLightPosition: { x: 16, y: 16 }, diff --git a/src/renderer/declaration.d.ts b/src/renderer/declaration.d.ts index 2f2acf9e3..fb609c549 100644 --- a/src/renderer/declaration.d.ts +++ b/src/renderer/declaration.d.ts @@ -54,7 +54,7 @@ declare global { ) => Promise; getLibrary: () => Promise; getRepackersFriendlyNames: () => Promise>; - openGame: (gameId: number) => Promise; + openGame: (gameId: number) => Promise; removeGame: (gameId: number) => Promise; deleteGameFolder: (gameId: number) => Promise; getGameByObjectID: (objectID: string) => Promise; diff --git a/src/renderer/pages/downloads/downloads.tsx b/src/renderer/pages/downloads/downloads.tsx index f0fd80381..196eb1769 100644 --- a/src/renderer/pages/downloads/downloads.tsx +++ b/src/renderer/pages/downloads/downloads.tsx @@ -9,6 +9,7 @@ import type { Game } from "@types"; import * as styles from "./downloads.css"; import { useEffect, useState } from "react"; +import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal"; export function Downloads() { const { library, updateLibrary } = useLibrary(); @@ -18,6 +19,7 @@ export function Downloads() { const navigate = useNavigate(); const [filteredLibrary, setFilteredLibrary] = useState([]); + const [showBinaryNotFoundModal, setShowBinaryNotFoundModal] = useState(false); const { game: gameDownloading, @@ -37,7 +39,8 @@ export function Downloads() { }, [library]); const openGame = (gameId: number) => - window.electron.openGame(gameId).then(() => { + window.electron.openGame(gameId).then(isBinaryInPath => { + if (!isBinaryInPath) setShowBinaryNotFoundModal(true); updateLibrary(); }); @@ -202,6 +205,7 @@ export function Downloads() { return (
+ setShowBinaryNotFoundModal(false)} />
    diff --git a/src/renderer/pages/game-details/hero-panel.tsx b/src/renderer/pages/game-details/hero-panel.tsx index 8af4a365f..2d4588a39 100644 --- a/src/renderer/pages/game-details/hero-panel.tsx +++ b/src/renderer/pages/game-details/hero-panel.tsx @@ -10,6 +10,7 @@ import type { Game, ShopDetails } from "@types"; import * as styles from "./hero-panel.css"; import { formatDownloadProgress } from "@renderer/helpers"; import { HeartFillIcon, HeartIcon } from "@primer/octicons-react"; +import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal"; export interface HeroPanelProps { game: Game | null; @@ -28,6 +29,8 @@ export function HeroPanel({ }: HeroPanelProps) { const { t } = useTranslation("game_details"); + const [showBinaryNotFoundModal, setShowBinaryNotFoundModal] = useState(false); + const { game: gameDownloading, isDownloading, @@ -54,7 +57,8 @@ export function HeroPanel({ const isGameDownloading = isDownloading && gameDownloading?.id === game?.id; const openGame = (gameId: number) => - window.electron.openGame(gameId).then(() => { + window.electron.openGame(gameId).then((isBinaryInPath) => { + if (!isBinaryInPath) setShowBinaryNotFoundModal(true); updateLibrary(); }); @@ -240,9 +244,15 @@ export function HeroPanel({ }; return ( -
    -
    {getInfo()}
    -
    {getActions()}
    -
    + <> + setShowBinaryNotFoundModal(false)} + /> +
    +
    {getInfo()}
    +
    {getActions()}
    +
    + ); } diff --git a/src/renderer/pages/shared-modals/binary-not-found-modal.tsx b/src/renderer/pages/shared-modals/binary-not-found-modal.tsx new file mode 100644 index 000000000..ddbde5b61 --- /dev/null +++ b/src/renderer/pages/shared-modals/binary-not-found-modal.tsx @@ -0,0 +1,25 @@ +import { Modal } from "@renderer/components" +import { useTranslation } from "react-i18next"; + +interface BinaryNotFoundModalProps { + visible: boolean; + onClose: () => void; +} + +export const BinaryNotFoundModal = ({ + visible, + onClose +}: BinaryNotFoundModalProps) => { + const { t } = useTranslation("binary_not_found_modal"); + + return ( + + {t("instructions")} + + ) +} diff --git a/yarn.lock b/yarn.lock index d382d5025..79ab849c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10460,6 +10460,11 @@ yaml@^1.10.0: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.1.tgz#2e57e0b5e995292c25c75d2658f0664765210eed" + integrity sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg== + yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" From aade13f2e2ca0f500357d382fea85330ef8c0eb9 Mon Sep 17 00:00:00 2001 From: Hydra Date: Tue, 16 Apr 2024 11:28:44 +0100 Subject: [PATCH 3/4] fix: removing unused variables --- src/renderer/pages/game-details/hero-panel.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/renderer/pages/game-details/hero-panel.tsx b/src/renderer/pages/game-details/hero-panel.tsx index 5e6b38001..911a8dfec 100644 --- a/src/renderer/pages/game-details/hero-panel.tsx +++ b/src/renderer/pages/game-details/hero-panel.tsx @@ -8,12 +8,7 @@ import { useDownload, useLibrary } from "@renderer/hooks"; import type { Game, ShopDetails } from "@types"; import { formatDownloadProgress } from "@renderer/helpers"; -import { - HeartFillIcon, - HeartIcon, - NoEntryIcon, - PlusCircleIcon, -} from "@primer/octicons-react"; +import { NoEntryIcon, PlusCircleIcon } from "@primer/octicons-react"; import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal"; import { DeleteModal } from "./delete-modal"; import * as styles from "./hero-panel.css"; From efbab40e00abd99b04c6cec2c0126792aa5ee2b0 Mon Sep 17 00:00:00 2001 From: Hydra Date: Tue, 16 Apr 2024 11:50:19 +0100 Subject: [PATCH 4/4] fix: fixing alignment on game card specifics --- src/renderer/components/game-card/game-card.css.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/components/game-card/game-card.css.ts b/src/renderer/components/game-card/game-card.css.ts index d9bba4640..f8d835fb1 100644 --- a/src/renderer/components/game-card/game-card.css.ts +++ b/src/renderer/components/game-card/game-card.css.ts @@ -105,6 +105,7 @@ export const specificsItem = style({ display: "flex", color: "#c0c1c7", fontSize: "12px", + alignItems: "flex-end", }); export const titleContainer = style({