From a922d9a1661ae2dfff2821b322c2d257dc763f08 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Fri, 2 May 2025 08:08:58 -0300 Subject: [PATCH] feat: create start menu shortcut on Windows --- src/locales/en/translation.json | 3 ++- src/locales/pt-BR/translation.json | 3 ++- src/locales/pt-PT/translation.json | 3 ++- src/main/constants.ts | 8 ++++++ .../events/library/create-game-shortcut.ts | 11 +++++--- .../events/user-preferences/auto-launch.ts | 16 ------------ src/preload/index.ts | 8 ++++-- src/renderer/src/declaration.d.ts | 7 +++++- .../modals/game-options-modal.tsx | 25 ++++++++++++++++--- src/types/game.types.ts | 2 ++ 10 files changed, 57 insertions(+), 29 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index a76745b1..30a3aa4c 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -198,7 +198,8 @@ "download_error_not_cached_on_hydra": "This download is not available on Nimbus.", "game_removed_from_favorites": "Game removed from favorites", "game_added_to_favorites": "Game added to favorites", - "automatically_extract_downloaded_files": "Automatically extract downloaded files" + "automatically_extract_downloaded_files": "Automatically extract downloaded files", + "create_start_menu_shortcut": "Create Start Menu shortcut" }, "activation": { "title": "Activate Hydra", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 25602fd2..db6af6ed 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -187,7 +187,8 @@ "download_error_not_cached_on_hydra": "Este download não está disponível no Nimbus.", "game_removed_from_favorites": "Jogo removido dos favoritos", "game_added_to_favorites": "Jogo adicionado aos favoritos", - "automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados" + "automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados", + "create_start_menu_shortcut": "Criar atalho no Menu Iniciar" }, "activation": { "title": "Ativação", diff --git a/src/locales/pt-PT/translation.json b/src/locales/pt-PT/translation.json index 35dde292..01cb7d1b 100644 --- a/src/locales/pt-PT/translation.json +++ b/src/locales/pt-PT/translation.json @@ -178,7 +178,8 @@ "download_error_not_cached_on_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_on_torbox": "Este download não está disponível no TorBox e a verificação do status do download não está disponível.", "game_removed_from_favorites": "Jogo removido dos favoritos", - "game_added_to_favorites": "Jogo adicionado aos favoritos" + "game_added_to_favorites": "Jogo adicionado aos favoritos", + "create_start_menu_shortcut": "Criar atalho no Menu Iniciar" }, "activation": { "title": "Ativação", diff --git a/src/main/constants.ts b/src/main/constants.ts index 8650defa..8bc2332a 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -8,6 +8,14 @@ export const defaultDownloadsPath = SystemPath.getPath("downloads"); export const isStaging = import.meta.env.MAIN_VITE_API_URL.includes("staging"); +export const windowsStartMenuPath = path.join( + SystemPath.getPath("appData"), + "Microsoft", + "Windows", + "Start Menu", + "Programs" +); + export const levelDatabasePath = path.join( SystemPath.getPath("userData"), `hydra-db${isStaging ? "-staging" : ""}` diff --git a/src/main/events/library/create-game-shortcut.ts b/src/main/events/library/create-game-shortcut.ts index 0c7a75df..5df10e97 100644 --- a/src/main/events/library/create-game-shortcut.ts +++ b/src/main/events/library/create-game-shortcut.ts @@ -3,14 +3,16 @@ import createDesktopShortcut from "create-desktop-shortcuts"; import path from "node:path"; import { app } from "electron"; import { removeSymbolsFromName } from "@shared"; -import { GameShop } from "@types"; +import { GameShop, ShortcutLocation } from "@types"; import { gamesSublevel, levelKeys } from "@main/level"; import { SystemPath } from "@main/services/system-path"; +import { windowsStartMenuPath } from "@main/constants"; const createGameShortcut = async ( _event: Electron.IpcMainInvokeEvent, shop: GameShop, - objectId: string + objectId: string, + location: ShortcutLocation ): Promise => { const gameKey = levelKeys.game(shop, objectId); const game = await gamesSublevel.get(gameKey); @@ -25,7 +27,10 @@ const createGameShortcut = async ( const options = { filePath, name: removeSymbolsFromName(game.title), - outputPath: SystemPath.getPath("desktop"), + outputPath: + location === "desktop" + ? SystemPath.getPath("desktop") + : windowsStartMenuPath, }; return createDesktopShortcut({ diff --git a/src/main/events/user-preferences/auto-launch.ts b/src/main/events/user-preferences/auto-launch.ts index dc5e2a02..4db09339 100644 --- a/src/main/events/user-preferences/auto-launch.ts +++ b/src/main/events/user-preferences/auto-launch.ts @@ -1,19 +1,7 @@ import { registerEvent } from "../register-event"; import AutoLaunch from "auto-launch"; import { app } from "electron"; -import path from "path"; -import fs from "node:fs"; import { logger } from "@main/services"; -import { SystemPath } from "@main/services/system-path"; - -const windowsStartupPath = path.join( - SystemPath.getPath("appData"), - "Microsoft", - "Windows", - "Start Menu", - "Programs", - "Startup" -); const autoLaunch = async ( _event: Electron.IpcMainInvokeEvent, @@ -31,10 +19,6 @@ const autoLaunch = async ( logger.error(err); }); } else { - if (process.platform == "win32") { - fs.rm(path.join(windowsStartupPath, "Hydra.vbs"), () => {}); - } - appLauncher.disable().catch((err) => { logger.error(err); }); diff --git a/src/preload/index.ts b/src/preload/index.ts index a7e06f90..d43df0c8 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -16,6 +16,7 @@ import type { GameAchievement, Theme, FriendRequestSync, + ShortcutLocation, } from "@types"; import type { AuthPage, CatalogueCategory } from "@shared"; import type { AxiosProgressEvent } from "axios"; @@ -122,8 +123,11 @@ contextBridge.exposeInMainWorld("electron", { ), addGameToLibrary: (shop: GameShop, objectId: string, title: string) => ipcRenderer.invoke("addGameToLibrary", shop, objectId, title), - createGameShortcut: (shop: GameShop, objectId: string) => - ipcRenderer.invoke("createGameShortcut", shop, objectId), + createGameShortcut: ( + shop: GameShop, + objectId: string, + location: ShortcutLocation + ) => ipcRenderer.invoke("createGameShortcut", shop, objectId, location), updateExecutablePath: ( shop: GameShop, objectId: string, diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index dd2f24d7..353a2a50 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -32,6 +32,7 @@ import type { Theme, Badge, Auth, + ShortcutLocation, } from "@types"; import type { AxiosProgressEvent } from "axios"; import type disk from "diskusage"; @@ -101,7 +102,11 @@ declare global { objectId: string, title: string ) => Promise; - createGameShortcut: (shop: GameShop, objectId: string) => Promise; + createGameShortcut: ( + shop: GameShop, + objectId: string, + location: ShortcutLocation + ) => Promise; updateExecutablePath: ( shop: GameShop, objectId: string, 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 71d61e66..343f3d5d 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 @@ -1,7 +1,7 @@ import { useContext, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Button, CheckboxField, Modal, TextField } from "@renderer/components"; -import type { LibraryGame } from "@types"; +import type { LibraryGame, ShortcutLocation } from "@types"; import { gameDetailsContext } from "@renderer/context"; import { DeleteGameModal } from "@renderer/pages/downloads/delete-game-modal"; import { useDownload, useToast, useUserDetails } from "@renderer/hooks"; @@ -107,15 +107,18 @@ export function GameOptionsModal({ } }; - const handleCreateShortcut = async () => { + const handleCreateShortcut = async (location: ShortcutLocation) => { window.electron - .createGameShortcut(game.shop, game.objectId) + .createGameShortcut(game.shop, game.objectId, location) .then((success) => { if (success) { showSuccessToast(t("create_shortcut_success")); } else { showErrorToast(t("create_shortcut_error")); } + }) + .catch(() => { + showErrorToast(t("create_shortcut_error")); }); }; @@ -176,6 +179,9 @@ export function GameOptionsModal({ const shouldShowWinePrefixConfiguration = window.electron.platform === "linux"; + const shouldShowCreateStartMenuShortcut = + window.electron.platform === "win32"; + const handleResetAchievements = async () => { setIsDeletingAchievements(true); try { @@ -278,9 +284,20 @@ export function GameOptionsModal({ > {t("open_folder")} - + {shouldShowCreateStartMenuShortcut && ( + + )} )} diff --git a/src/types/game.types.ts b/src/types/game.types.ts index e1ba779b..cc19f09c 100644 --- a/src/types/game.types.ts +++ b/src/types/game.types.ts @@ -1,5 +1,7 @@ export type GameShop = "steam" | "epic"; +export type ShortcutLocation = "desktop" | "start_menu"; + export interface UnlockedAchievement { name: string; unlockTime: number;