From 48b6d1c941af6c1a088e98c45b84819af27f372e Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:33:21 -0300 Subject: [PATCH 01/34] feat: create game options modal fix error when getSteamAppDetails fails dont set game as removed when deleting instalation folder fix game not deleting installation folder organize code feat: add open game executable path and installer path --- src/main/events/index.ts | 3 + .../library/open-game-executable-path.ts | 22 +++ .../library/open-game-installer-path.ts | 27 +++ .../events/library/update-executable-path.ts | 20 +++ src/preload/index.ts | 6 + src/renderer/src/declaration.d.ts | 3 + .../game-details/hero/hero-panel-actions.tsx | 23 ++- .../modals/game-options-modal.css.ts | 13 ++ .../modals/game-options-modal.tsx | 167 ++++++++++++++++++ 9 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 src/main/events/library/open-game-executable-path.ts create mode 100644 src/main/events/library/open-game-installer-path.ts create mode 100644 src/main/events/library/update-executable-path.ts create mode 100644 src/renderer/src/pages/game-details/modals/game-options-modal.css.ts create mode 100644 src/renderer/src/pages/game-details/modals/game-options-modal.tsx diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 83b90595..23271eba 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -15,7 +15,10 @@ import "./library/delete-game-folder"; import "./library/get-game-by-object-id"; import "./library/get-library"; import "./library/open-game"; +import "./library/open-game-executable-path"; import "./library/open-game-installer"; +import "./library/open-game-installer-path"; +import "./library/update-executable-path"; import "./library/remove-game"; import "./library/remove-game-from-library"; import "./misc/open-external"; diff --git a/src/main/events/library/open-game-executable-path.ts b/src/main/events/library/open-game-executable-path.ts new file mode 100644 index 00000000..47aa0b47 --- /dev/null +++ b/src/main/events/library/open-game-executable-path.ts @@ -0,0 +1,22 @@ +import { shell } from "electron"; +import path from "node:path"; +import { gameRepository } from "@main/repository"; +import { registerEvent } from "../register-event"; + +const openGameExecutablePath = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number +) => { + const game = await gameRepository.findOne({ + where: { id: gameId, isDeleted: false }, + }); + + if (!game || !game.executablePath) return true; + + const gamePath = path.join(game.executablePath, "../"); + + shell.openPath(gamePath); + return true; +}; + +registerEvent("openGameExecutablePath", openGameExecutablePath); diff --git a/src/main/events/library/open-game-installer-path.ts b/src/main/events/library/open-game-installer-path.ts new file mode 100644 index 00000000..bb5d0c6f --- /dev/null +++ b/src/main/events/library/open-game-installer-path.ts @@ -0,0 +1,27 @@ +import { shell } from "electron"; +import path from "node:path"; +import { gameRepository } from "@main/repository"; +import { getDownloadsPath } from "../helpers/get-downloads-path"; +import { registerEvent } from "../register-event"; + +const openGameInstallerPath = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number +) => { + const game = await gameRepository.findOne({ + where: { id: gameId, isDeleted: false }, + }); + + if (!game || !game.folderName) return true; + + const gamePath = path.join( + game.downloadPath ?? (await getDownloadsPath()), + game.folderName! + ); + + shell.openPath(gamePath); + + return true; +}; + +registerEvent("openGameInstallerPath", openGameInstallerPath); diff --git a/src/main/events/library/update-executable-path.ts b/src/main/events/library/update-executable-path.ts new file mode 100644 index 00000000..6546e8b9 --- /dev/null +++ b/src/main/events/library/update-executable-path.ts @@ -0,0 +1,20 @@ +import { gameRepository } from "@main/repository"; + +import { registerEvent } from "../register-event"; + +const updateExecutablePath = async ( + _event: Electron.IpcMainInvokeEvent, + id: number, + executablePath: string +) => { + return gameRepository.update( + { + id, + }, + { + executablePath, + } + ); +}; + +registerEvent("updateExecutablePath", updateExecutablePath); diff --git a/src/preload/index.ts b/src/preload/index.ts index 4d1f0eea..8f0ca282 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -73,9 +73,15 @@ contextBridge.exposeInMainWorld("electron", { shop, executablePath ), + updateExecutablePath: (id: number, executablePath: string) => + ipcRenderer.invoke("updateExecutablePath", id, executablePath), getLibrary: () => ipcRenderer.invoke("getLibrary"), openGameInstaller: (gameId: number) => ipcRenderer.invoke("openGameInstaller", gameId), + openGameInstallerPath: (gameId: number) => + ipcRenderer.invoke("openGameInstallerPath", gameId), + openGameExecutablePath: (gameId: number) => + ipcRenderer.invoke("openGameExecutablePath", gameId), openGame: (gameId: number, executablePath: string) => ipcRenderer.invoke("openGame", gameId, executablePath), closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId), diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 5d5bf0ec..f9bf8a7e 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -59,8 +59,11 @@ declare global { shop: GameShop, executablePath: string | null ) => Promise; + updateExecutablePath: (id: number, executablePath: string) => Promise; getLibrary: () => Promise; openGameInstaller: (gameId: number) => Promise; + openGameInstallerPath: (gameId: number) => Promise; + openGameExecutablePath: (gameId: number) => Promise; openGame: (gameId: number, executablePath: string) => Promise; closeGame: (gameId: number) => Promise; removeGameFromLibrary: (gameId: number) => Promise; 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 06406d22..64730b6c 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,4 +1,4 @@ -import { NoEntryIcon, PlusCircleIcon } from "@primer/octicons-react"; +import { GearIcon, NoEntryIcon, PlusCircleIcon } from "@primer/octicons-react"; import { BinaryNotFoundModal } from "../../shared-modals/binary-not-found-modal"; @@ -10,11 +10,13 @@ import { useTranslation } from "react-i18next"; import * as styles from "./hero-panel-actions.css"; import { gameDetailsContext } from "../game-details.context"; import { Downloader } from "@shared"; +import { GameOptionsModal } from "../modals/game-options-modal"; export function HeroPanelActions() { const [toggleLibraryGameDisabled, setToggleLibraryGameDisabled] = useState(false); const [showBinaryNotFoundModal, setShowBinaryNotFoundModal] = useState(false); + const [showGameOptionsModal, setShowGameOptionsModal] = useState(false); const { resumeDownload, @@ -224,6 +226,25 @@ export function HeroPanelActions() { if (game) { return ( <> + { + setShowGameOptionsModal(false); + }} + selectGameExecutable={selectGameExecutable} + /> + + {game?.progress === 1 && game?.folderName && ( <> void; + selectGameExecutable: () => Promise; +} + +export function GameOptionsModal({ + visible, + game, + onClose, + selectGameExecutable, +}: GameOptionsModalProps) { + const [currentCategoryIndex, setCurrentCategoryIndex] = useState(0); + + const { updateGame, openRepacksModal } = useContext(gameDetailsContext); + + const [showDeleteModal, setShowDeleteModal] = useState(false); + + const { removeGameInstaller, isGameDeleting } = useDownload(); + + const deleting = game ? isGameDeleting(game?.id) : false; + + const { t } = useTranslation("game_details"); + + const handleChangeExecutableLocation = async () => { + const location = await selectGameExecutable(); + + if (location) { + await window.electron.updateExecutablePath(game.id, location); + updateGame(); + } + }; + + const handleDeleteGame = async () => { + await removeGameInstaller(game.id); + }; + + const handleOpenGameInstallerPath = async () => { + await window.electron.openGameInstallerPath(game.id); + }; + + const handleOpenGameExecutablePath = async () => { + await window.electron.openGameExecutablePath(game.id); + }; + + return ( + <> + + setShowDeleteModal(false)} + deleteGame={handleDeleteGame} + /> + +
+
+ +
+ +
+ + + + + +
+ +
+ +
+ +
+ + + +
+
+
+ + ); +} From 366cb1ac2f99370c1b961e8248880740bc320b90 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 5 Jun 2024 15:29:50 -0300 Subject: [PATCH 02/34] separator --- .../src/pages/game-details/hero/hero-panel-actions.css.ts | 8 +++++++- .../src/pages/game-details/hero/hero-panel-actions.tsx | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/pages/game-details/hero/hero-panel-actions.css.ts b/src/renderer/src/pages/game-details/hero/hero-panel-actions.css.ts index 6280c8ce..7f56588a 100644 --- a/src/renderer/src/pages/game-details/hero/hero-panel-actions.css.ts +++ b/src/renderer/src/pages/game-details/hero/hero-panel-actions.css.ts @@ -1,7 +1,13 @@ import { style } from "@vanilla-extract/css"; -import { vars } from "../../../theme.css"; +import { SPACING_UNIT, vars } from "../../../theme.css"; export const heroPanelAction = style({ border: `solid 1px ${vars.color.muted}`, }); + +export const separator = style({ + width: "1px", + backgroundColor: vars.color.border, + margin: `${SPACING_UNIT / 2}px 0`, +}); 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 21e7c510..ba35e404 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 @@ -245,6 +245,8 @@ export function HeroPanelActions() { +
+ {game?.progress === 1 && game?.folderName && ( <> Date: Wed, 5 Jun 2024 16:42:45 -0300 Subject: [PATCH 03/34] feat: create game shortcut --- package.json | 1 + src/main/events/index.ts | 1 + .../events/library/create-game-shortcut.ts | 26 +++++++++++++++++++ src/preload/index.ts | 2 ++ src/renderer/src/declaration.d.ts | 1 + .../modals/game-options-modal.tsx | 13 ++++++++++ yarn.lock | 9 ++++++- 7 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/main/events/library/create-game-shortcut.ts diff --git a/package.json b/package.json index d7b6fb9e..31bac590 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "classnames": "^2.5.1", "color": "^4.2.3", "color.js": "^1.2.0", + "create-desktop-shortcuts": "^1.11.0", "date-fns": "^3.6.0", "electron-log": "^5.1.4", "electron-updater": "^6.1.8", diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 3d406b64..86e74fcb 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -10,6 +10,7 @@ import "./catalogue/search-games"; import "./catalogue/search-game-repacks"; import "./hardware/get-disk-free-space"; import "./library/add-game-to-library"; +import "./library/create-game-shortcut"; import "./library/close-game"; import "./library/delete-game-folder"; import "./library/get-game-by-object-id"; diff --git a/src/main/events/library/create-game-shortcut.ts b/src/main/events/library/create-game-shortcut.ts new file mode 100644 index 00000000..7ce37bee --- /dev/null +++ b/src/main/events/library/create-game-shortcut.ts @@ -0,0 +1,26 @@ +import { gameRepository } from "@main/repository"; +import { registerEvent } from "../register-event"; +import { IsNull, Not } from "typeorm"; +import createDesktopShortcut from "create-desktop-shortcuts"; + +const createGameShortcut = async ( + _event: Electron.IpcMainInvokeEvent, + id: number +): Promise => { + const game = await gameRepository.findOne({ + where: { id, executablePath: Not(IsNull()) }, + }); + + if (game) { + const filePath = game.executablePath; + return createDesktopShortcut({ + windows: { filePath }, + linux: { filePath }, + osx: { filePath }, + }); + } + + return false; +}; + +registerEvent("createGameShortcut", createGameShortcut); diff --git a/src/preload/index.ts b/src/preload/index.ts index 5e6d97af..3748baaa 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -74,6 +74,8 @@ contextBridge.exposeInMainWorld("electron", { shop, executablePath ), + createGameShortcut: (id: number) => + ipcRenderer.invoke("createGameShortcut", id), updateExecutablePath: (id: number, executablePath: string) => ipcRenderer.invoke("updateExecutablePath", id, executablePath), getLibrary: () => ipcRenderer.invoke("getLibrary"), diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 7f3fad3d..14d8bb80 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -59,6 +59,7 @@ declare global { shop: GameShop, executablePath: string | null ) => Promise; + createGameShortcut: (id: number) => Promise; updateExecutablePath: (id: number, executablePath: string) => Promise; getLibrary: () => Promise; openGameInstaller: (gameId: number) => Promise; diff --git a/src/renderer/src/pages/game-details/modals/game-options-modal.tsx b/src/renderer/src/pages/game-details/modals/game-options-modal.tsx index f23db9b1..106e725a 100644 --- a/src/renderer/src/pages/game-details/modals/game-options-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/game-options-modal.tsx @@ -50,6 +50,10 @@ export function GameOptionsModal({ } }; + const handleCreateShortcut = async () => { + await window.electron.createGameShortcut(game.id); + }; + const handleDeleteGame = async () => { await removeGameInstaller(game.id); }; @@ -160,6 +164,15 @@ export function GameOptionsModal({ {t("open_download_options")}
+
+ +
diff --git a/yarn.lock b/yarn.lock index 5ffbd1a1..f438ecf4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2403,6 +2403,13 @@ crc@^3.8.0: dependencies: buffer "^5.1.0" +create-desktop-shortcuts@^1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/create-desktop-shortcuts/-/create-desktop-shortcuts-1.11.0.tgz#8eed89329e9bce70dece46d02a80573fe1f2536d" + integrity sha512-nmVtPVqNyMuAyMpDnd7l++hb2laqCWZXnHQaFhqGT1YEi2Ve3unu6QyuyIpGxAwIscNHcG1Ehnl+lFw6ygB2nQ== + dependencies: + which "2.0.2" + cross-fetch-ponyfill@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/cross-fetch-ponyfill/-/cross-fetch-ponyfill-1.0.3.tgz#5c5524e3bd3374e71d5016c2327e416369a57527" @@ -6278,7 +6285,7 @@ which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.2" -which@^2.0.1: +which@2.0.2, which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== From 45d8923561b853b66d855f4fb84235a26604f59f Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:14:13 -0300 Subject: [PATCH 04/34] feat: double click on sidebar opens game --- .../src/components/sidebar/sidebar.tsx | 18 ++++++-- .../modals/game-options-modal.tsx | 45 +++++++------------ 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/renderer/src/components/sidebar/sidebar.tsx b/src/renderer/src/components/sidebar/sidebar.tsx index 76ab3137..faa77708 100644 --- a/src/renderer/src/components/sidebar/sidebar.tsx +++ b/src/renderer/src/components/sidebar/sidebar.tsx @@ -122,6 +122,20 @@ export function Sidebar() { } }; + const handleSidebarGameClick = ( + event: React.MouseEvent, + game: LibraryGame + ) => { + const path = buildGameDetailsPath(game); + if (path !== location.pathname) { + navigate(path); + } + + if (event.detail == 2 && game.executablePath) { + window.electron.openGame(game.id, game.executablePath); + } + }; + return (