diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c51d9ea6..dc65189e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Build +name: Release on: push: diff --git a/README.es.md b/README.es.md new file mode 100644 index 00000000..3b261f60 --- /dev/null +++ b/README.es.md @@ -0,0 +1,182 @@ +
+ +
+ +[](https://hydralauncher.site) + +

Hydra Launcher

+ +

+ Hydra es un launcher de juegos con su propio cliente de bittorrent y gestor propio de repacks. +

+ +[![build](https://img.shields.io/github/actions/workflow/status/hydralauncher/hydra/build.yml)](https://github.com/hydralauncher/hydra/actions) +[![release](https://img.shields.io/github/package-json/v/hydralauncher/hydra)](https://github.com/hydralauncher/hydra/releases) + +[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md) +[![pl](https://img.shields.io/badge/lang-pl-white)](README.pl.md) +[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md) +[![ru](https://img.shields.io/badge/lang-ru-yellow.svg)](README.ru.md) +[![uk-UA](https://img.shields.io/badge/lang-uk--UA-blue)](README.uk-UA.md) +[![es](https://img.shields.io/badge/lang-es-red)](README.es.md) + +![Hydra Catalogue](./docs/screenshot.png) + +
+ +## Tabla de Contenidos + +- [Acerca de](#acerca-de) +- [Características](#caracteristicas) +- [Instalación](#Instalacion) +- [Contribuir](#contribuir) + - [Únete a nuestro Telegram](#unete-a-nuestro-telegram) + - [Haz un fork y clona tu repositorio](#haz-un-fork-y-clona-tu-repositorio) + - [Maneras en las que puedes contribuir](#maneras-en-las-que-puedes-contribuir) + - [Estructura del proyecto](#estructura-del-proyecto) +- [Compilar desde el código fuente](#compilar-desde-el-código-fuente) + - [Instalar Node.js](#instalar-nodejs) + - [Instalar Yarn](#instalar-yarn) + - [Instalar Dependencias de Node](#instalar-dependencias-de-node) + - [Instalar Python 3.9](#instalar-python-39) + - [Instalar Dependencias de Python](#Instalar-dependencias-de-python) +- [Variables del Entorno](#variables-del-entorno) +- [Ejecución](#ejecucion) +- [Compilación](#compilacion) + - [Compilar el cliente de bittorrent](#compilar-el-cliente-de-bittorrent) + - [Compilar la aplicación Electron](#compilar-la-aplicacion-electron) +- [Colaboradores](#colaboradores) + +## Acerca de + +**Hydra** es un **Launcher de Juegos** con su propio **Cliente Bittorrent** y **autogestor de Repacks**. +
+El launcher está escrito en TypeScript (Electron) y Python, el cuál se encarga del sistema de torrent usando libtorrent. + +## Caracteristicas + +- Buscador e instalador autogestionado de repacks a través de las páginas más confiables en él [Megahilo](https://www.reddit.com/r/Piracy/wiki/megathread/) +- Cliente propio de bittorrent integrado +- Integración de How Long To Beat (HLTB) en la página del juego +- Customización de rutas de descargas +- Notificaciones en actualizaciones a listas de repacks +- Soporte a Windows y Linux +- En constante actualización +- Y mucho más ... + +## Instalacion + +Sigue los pasos de abajo para instalar: + +1. Descarga la última versión de Hydra desde la página de [Releases](https://github.com/hydralauncher/hydra/releases/latest). + - Descarga solo el .exe si quieres instalar Hydra en Windows. + - Descarga el .deb o .rpm o .zip si quieres instalar Hydra en Linux. (Depende de tu distro de Linux) +2. Ejecuta el archivo descargado. +3. ¡Disfruta de Hydra! + +## Contribuir + +### Unete a nuestro Telegram + +Puedes unirte a nuestra conversación y discusiones en nuestro canal de [Telegram](https://t.me/hydralauncher). + +### Haz un fork y clona tu repositorio + +1. Rea;iza un fork del repositorio [(Haz click acá para hacer un fork ahora)](https://github.com/hydralauncher/hydra/fork) +2. Clona el código forkeado `git clone https://github.com/tu_nombredeusuario/hydra` +3. Crea una nueva rama +4. Sube tus commits +5. Envía nuevas solicitudes de pull + +### Maneras en las que puedes contribuir + +- Traducción: Queremos que Hydra esté disponible para todas las personas que sean posible. Siéntete libre de ayudarnos a traducirlo a nuevos lenguajes o actualizar y mejorar las ya disponibles en Hydra. +- Código: Hydra está hecho con Typescript, Electron y un poquito de Python. Si quieres contribuir, ¡únete a nuestro [Telegram](https://t.me/hydralauncher)! + +### Estructura del proyecto + +- torrent-client: Usamos libtorrent, una librería de Python que se encarga de manejar las descargas torrent +- src/renderer: El UI de la aplicación +- src/main: El resto de la lógica va acá. + +## Compilar desde el código fuente + +### Instalar Node.js + +Asegúrate que tienes Node.js instalado en tú máquina. Si no es así, puedes descargarlo e instalarlo desde [nodejs.org](https://nodejs.org/). + +### Instalar Yarn + +Yarn es un gestor de paquetes para Node.js. Si no tienes aún instalado Yarn todavía, puedes hacerlo siguiendo las instrucciones en [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/). + +### Instalar Dependencias de Node + +Dirígete hasta el directorio del proyecto e instala las dependencias de Node usando Yarn: + +```bash +cd hydra +yarn +``` + +### Instalar Python 3.9 + +Asegúrate que tienes Python 3.9 instalado en tu máquina. Puedes descargarlo e instalarlo desde [python.org](https://www.python.org/downloads/release/python-3913/). + +### Instalar Dependencias de Python + +Instala las dependencias de Python requeridas usando pip: + +```bash +pip install -r requirements.txt +``` + +## Variables del Entorno + +Necesitas una llave API de SteamGridDB para así poder obtener los íconos de los juegos en la instalación. +Si quieres también tener los repacks de onlinefix, necesitarás añadir tus credenciales al .env + +Una vez que los tengas, puedes copiar o renombrar el archivo `.env.example` cómo `.env` y colocarlo en `STEAMGRIDDB_API_KEY`, `ONLINEFIX_USERNAME`, `ONLINEFIX_PASSWORD`. + +## Ejecucion + +Una vez que tengas todas las cosas listas, puedes ejecutar el siguiente comando para así iniciar el proceso de Electron y el cliente de bittorrent: + +```bash +yarn dev +``` + +## Compilacion + +### Compilar el cliente de bittorrent + +Crea el cliente bittorrent usando este comando: + +```bash +python torrent-client/setup.py build +``` + +### Compilar la aplicacion Electron + +Crea la aplicación de Electron usando este comando: + +En Windows: + +```bash +yarn build:win +``` + +En Linux: + +```bash +yarn build:linux +``` + +## Colaboradores + + + + + +## Licencia + +Hydra está licenciado bajo la [MIT License](LICENSE). diff --git a/hydra.db b/hydra.db index 4522e1ae..dad57120 100644 Binary files a/hydra.db and b/hydra.db differ diff --git a/package.json b/package.json index 6a16dfdf..dc823367 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "hydra", + "name": "hydralauncher", "version": "1.2.3", "description": "Hydra", "main": "./out/main/index.js", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 25c97aff..08dc5b53 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -177,5 +177,11 @@ }, "modal": { "close": "Close button" + }, + "splash": { + "downloading_version": "Downloading version {{version}}", + "searching_updates": "Searching for updates", + "update_found": "Update {{version}} found", + "restarting_and_applying": "Restarting and applying update" } } diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index b6db914c..2afe41a1 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -177,5 +177,11 @@ }, "modal": { "close": "Botón de cierre" + }, + "splash": { + "downloading_version": "Descargando versión {{version}}", + "searching_updates": "Buscando actualizaciones", + "update_found": "Actualización {{version}} encontrada", + "restarting_and_applying": "Reiniciando y aplicando actualización" } } diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 624dceeb..8d3e0a76 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -177,5 +177,11 @@ }, "modal": { "close": "Botão de fechar" + }, + "splash": { + "downloading_version": "Baixando versão {{version}}", + "searching_updates": "Buscando atualizações", + "update_found": "Versão {{version}} encontrada", + "restarting_and_applying": "Reiniciando e aplicando atualização" } } diff --git a/src/main/constants.ts b/src/main/constants.ts index 4d43518b..17eea7ca 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -22,7 +22,7 @@ export const defaultDownloadsPath = app.getPath("downloads"); export const databasePath = path.join( app.getPath("appData"), - app.getName(), + "hydra", "hydra.db" ); diff --git a/src/main/events/autoupdater/check-for-updates.ts b/src/main/events/autoupdater/check-for-updates.ts new file mode 100644 index 00000000..aa63575f --- /dev/null +++ b/src/main/events/autoupdater/check-for-updates.ts @@ -0,0 +1,48 @@ +import { AppUpdaterEvents } from "@types"; +import { registerEvent } from "../register-event"; +import updater, { ProgressInfo, UpdateInfo } from "electron-updater"; +import { WindowManager } from "@main/services"; +import { app } from "electron"; + +const { autoUpdater } = updater; + +const sendEvent = (event: AppUpdaterEvents) => { + WindowManager.splashWindow?.webContents.send("autoUpdaterEvent", event); +}; + +const mockValuesForDebug = async () => { + sendEvent({ type: "update-downloaded" }); +}; + +const checkForUpdates = async (_event: Electron.IpcMainInvokeEvent) => { + autoUpdater + .addListener("error", () => { + sendEvent({ type: "error" }); + }) + .addListener("checking-for-update", () => { + sendEvent({ type: "checking-for-updates" }); + }) + .addListener("update-not-available", () => { + sendEvent({ type: "update-not-available" }); + }) + .addListener("update-available", (info: UpdateInfo) => { + sendEvent({ type: "update-available", info }); + }) + .addListener("update-downloaded", () => { + sendEvent({ type: "update-downloaded" }); + }) + .addListener("download-progress", (info: ProgressInfo) => { + sendEvent({ type: "download-progress", info }); + }) + .addListener("update-cancelled", () => { + sendEvent({ type: "update-cancelled" }); + }); + + if (app.isPackaged) { + autoUpdater.checkForUpdates(); + } else { + await mockValuesForDebug(); + } +}; + +registerEvent("checkForUpdates", checkForUpdates); diff --git a/src/main/events/autoupdater/continue-to-main-window.ts b/src/main/events/autoupdater/continue-to-main-window.ts new file mode 100644 index 00000000..6a8965f9 --- /dev/null +++ b/src/main/events/autoupdater/continue-to-main-window.ts @@ -0,0 +1,12 @@ +import { WindowManager } from "@main/services"; +import { registerEvent } from "../register-event"; +import updater from "electron-updater"; + +const { autoUpdater } = updater; + +const continueToMainWindow = async (_event: Electron.IpcMainInvokeEvent) => { + autoUpdater.removeAllListeners(); + WindowManager.prepareMainWindowAndCloseSplash(); +}; + +registerEvent("continueToMainWindow", continueToMainWindow); diff --git a/src/main/events/autoupdater/restart-and-install-update.ts b/src/main/events/autoupdater/restart-and-install-update.ts new file mode 100644 index 00000000..be301c18 --- /dev/null +++ b/src/main/events/autoupdater/restart-and-install-update.ts @@ -0,0 +1,17 @@ +import { app } from "electron"; +import { registerEvent } from "../register-event"; +import updater from "electron-updater"; +import { WindowManager } from "@main/services"; + +const { autoUpdater } = updater; + +const restartAndInstallUpdate = async (_event: Electron.IpcMainInvokeEvent) => { + if (app.isPackaged) { + autoUpdater.quitAndInstall(true, true); + } else { + autoUpdater.removeAllListeners(); + WindowManager.prepareMainWindowAndCloseSplash(); + } +}; + +registerEvent("restartAndInstallUpdate", restartAndInstallUpdate); diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 5d721c62..debca0e4 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -27,6 +27,9 @@ import "./torrenting/start-game-download"; import "./user-preferences/get-user-preferences"; import "./user-preferences/update-user-preferences"; import "./user-preferences/auto-launch"; +import "./autoupdater/check-for-updates"; +import "./autoupdater/restart-and-install-update"; +import "./autoupdater/continue-to-main-window"; ipcMain.handle("ping", () => "pong"); ipcMain.handle("getVersion", () => app.getVersion()); diff --git a/src/main/index.ts b/src/main/index.ts index dabdeb4e..22c13388 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -3,11 +3,10 @@ import updater from "electron-updater"; import i18n from "i18next"; import path from "node:path"; import { electronApp, optimizer } from "@electron-toolkit/utils"; -import { resolveDatabaseUpdates, WindowManager } from "@main/services"; +import { logger, resolveDatabaseUpdates, WindowManager } from "@main/services"; import { dataSource } from "@main/data-source"; import * as resources from "@locales"; import { userPreferencesRepository } from "@main/repository"; - const { autoUpdater } = updater; autoUpdater.setFeedURL({ @@ -16,6 +15,8 @@ autoUpdater.setFeedURL({ repo: "hydra", }); +autoUpdater.logger = logger; + const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) app.quit(); @@ -63,12 +64,8 @@ app.whenReady().then(() => { where: { id: 1 }, }); - WindowManager.createMainWindow(); + WindowManager.createSplashScreen(); WindowManager.createSystemTray(userPreferences?.language || "en"); - - WindowManager.mainWindow?.on("ready-to-show", () => { - autoUpdater.checkForUpdatesAndNotify(); - }); }); }); diff --git a/src/main/services/repack-tracker/online-fix.ts b/src/main/services/repack-tracker/online-fix.ts index 38864a8b..265f6b70 100644 --- a/src/main/services/repack-tracker/online-fix.ts +++ b/src/main/services/repack-tracker/online-fix.ts @@ -148,9 +148,10 @@ export const getNewRepacksFromOnlineFix = async ( ); if (!newRepacks.length) return; - if (page === totalPages) return; await savePage(newRepacks); + if (page === totalPages) return; + return getNewRepacksFromOnlineFix(existingRepacks, page + 1, cookieJar); }; diff --git a/src/main/services/repack-tracker/xatab.ts b/src/main/services/repack-tracker/xatab.ts index 34ebfa4c..e765bebf 100644 --- a/src/main/services/repack-tracker/xatab.ts +++ b/src/main/services/repack-tracker/xatab.ts @@ -111,9 +111,10 @@ export const getNewRepacksFromXatab = async ( ); if (!newRepacks.length) return; - if (page === totalPages) return; await savePage(newRepacks); + if (page === totalPages) return; + return getNewRepacksFromXatab(existingRepacks, page + 1); }; diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index 973a0c64..e435ddb2 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -17,6 +17,8 @@ import { IsNull, Not } from "typeorm"; export class WindowManager { public static mainWindow: Electron.BrowserWindow | null = null; + public static splashWindow: Electron.BrowserWindow | null = null; + public static isReadyToShowMainWindow = false; private static loadURL(hash = "") { // HMR for renderer base on electron-vite cli. @@ -35,13 +37,51 @@ export class WindowManager { } } + private static loadSplashURL() { + // HMR for renderer base on electron-vite cli. + // Load the remote URL for development or the local html file for production. + if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { + this.splashWindow?.loadURL( + `${process.env["ELECTRON_RENDERER_URL"]}#/splash` + ); + } else { + this.splashWindow?.loadFile( + path.join(__dirname, "../renderer/index.html"), + { + hash: "splash", + } + ); + } + } + + public static createSplashScreen() { + if (this.splashWindow) return; + + this.splashWindow = new BrowserWindow({ + width: 380, + height: 380, + frame: false, + resizable: false, + backgroundColor: "#1c1c1c", + webPreferences: { + preload: path.join(__dirname, "../preload/index.mjs"), + sandbox: false, + }, + }); + + this.loadSplashURL(); + this.splashWindow.removeMenu(); + } + public static createMainWindow() { - // Create the browser window. + if (this.mainWindow || !this.isReadyToShowMainWindow) return; + this.mainWindow = new BrowserWindow({ width: 1200, height: 720, minWidth: 1024, minHeight: 540, + backgroundColor: "#1c1c1c", titleBarStyle: "hidden", ...(process.platform === "linux" ? { icon } : {}), trafficLightPosition: { x: 16, y: 16 }, @@ -75,6 +115,12 @@ export class WindowManager { }); } + public static prepareMainWindowAndCloseSplash() { + this.isReadyToShowMainWindow = true; + this.splashWindow?.close(); + this.createMainWindow(); + } + public static redirect(hash: string) { if (!this.mainWindow) this.createMainWindow(); this.loadURL(hash); diff --git a/src/preload/index.ts b/src/preload/index.ts index 6a209787..4ddf5009 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -7,6 +7,7 @@ import type { GameShop, TorrentProgress, UserPreferences, + AppUpdaterEvents, } from "@types"; contextBridge.exposeInMainWorld("electron", { @@ -112,4 +113,21 @@ contextBridge.exposeInMainWorld("electron", { showOpenDialog: (options: Electron.OpenDialogOptions) => ipcRenderer.invoke("showOpenDialog", options), platform: process.platform, + + /* Splash */ + onAutoUpdaterEvent: (cb: (value: AppUpdaterEvents) => void) => { + const listener = ( + _event: Electron.IpcRendererEvent, + value: AppUpdaterEvents + ) => cb(value); + + ipcRenderer.on("autoUpdaterEvent", listener); + + return () => { + ipcRenderer.removeListener("autoUpdaterEvent", listener); + }; + }, + checkForUpdates: () => ipcRenderer.invoke("checkForUpdates"), + restartAndInstallUpdate: () => ipcRenderer.invoke("restartAndInstallUpdate"), + continueToMainWindow: () => ipcRenderer.invoke("continueToMainWindow"), }); diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index da95f292..a1dd3d9a 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -12,7 +12,7 @@ import { import * as styles from "./app.css"; import { themeClass } from "./theme.css"; -import { useLocation, useNavigate } from "react-router-dom"; +import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { setSearch, clearSearch, @@ -27,7 +27,7 @@ export interface AppProps { children: React.ReactNode; } -export function App({ children }: AppProps) { +export function App() { const contentRef = useRef(null); const { updateLibrary } = useLibrary(); @@ -128,7 +128,7 @@ export function App({ children }: AppProps) { />
- {children} +
diff --git a/src/renderer/src/assets/icon.png b/src/renderer/src/assets/icon.png new file mode 100644 index 00000000..9254a8fb Binary files /dev/null and b/src/renderer/src/assets/icon.png differ diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 045915bb..608f21a0 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -1,4 +1,5 @@ import type { + AppUpdaterEvents, CatalogueCategory, CatalogueEntry, Game, @@ -90,6 +91,14 @@ declare global { options: Electron.OpenDialogOptions ) => Promise; platform: NodeJS.Platform; + + /* Splash */ + onAutoUpdaterEvent: ( + cb: (event: AppUpdaterEvents) => void + ) => () => Electron.IpcRenderer; + checkForUpdates: () => Promise; + restartAndInstallUpdate: () => Promise; + continueToMainWindow: () => Promise; } interface Window { diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index f44653cb..3608af8d 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -27,6 +27,7 @@ import { import { store } from "./store"; import * as resources from "@locales"; +import Splash from "./pages/splash/splash"; i18n .use(LanguageDetector) @@ -46,16 +47,17 @@ ReactDOM.createRoot(document.getElementById("root")!).render( - - + + + }> - - + + diff --git a/src/renderer/src/pages/game-details/gallery-slider.tsx b/src/renderer/src/pages/game-details/gallery-slider.tsx index 8879b2ff..e1925065 100644 --- a/src/renderer/src/pages/game-details/gallery-slider.tsx +++ b/src/renderer/src/pages/game-details/gallery-slider.tsx @@ -58,9 +58,7 @@ export function GallerySlider({ gameDetails }: GallerySliderProps) { if (hasMovies && mediaContainerRef.current) { mediaContainerRef.current.childNodes.forEach((node, index) => { if (node instanceof HTMLVideoElement) { - if (index == mediaIndex) { - node.play(); - } else { + if (index !== mediaIndex) { node.pause(); } } diff --git a/src/renderer/src/pages/splash/splash.css.ts b/src/renderer/src/pages/splash/splash.css.ts new file mode 100644 index 00000000..36aacfff --- /dev/null +++ b/src/renderer/src/pages/splash/splash.css.ts @@ -0,0 +1,49 @@ +import { style } from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const main = style({ + width: "100%", + height: "100%", + display: "flex", + flexDirection: "column", + padding: `${SPACING_UNIT * 3}px`, + flex: "1", + overflowY: "auto", + alignItems: "center", +}); + +export const splashIcon = style({ + width: "75%", +}); + +export const updateInfoSection = style({ + width: "100%", + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT * 2}px`, + flex: "1", + overflowY: "auto", + alignItems: "center", + justifyContent: "center", +}); + +export const progressBar = style({ + WebkitAppearance: "none", + appearance: "none", + borderRadius: "4px", + width: "100%", + border: `solid 1px ${vars.color.border}`, + overflow: "hidden", + height: "18px", + "::-webkit-progress-value": { + backgroundColor: vars.color.muted, + transition: "width 0.2s", + }, + "::-webkit-progress-bar": { + backgroundColor: vars.color.darkBackground, + }, +}); + +export const progressBarText = style({ + zIndex: 2, +}); diff --git a/src/renderer/src/pages/splash/splash.tsx b/src/renderer/src/pages/splash/splash.tsx new file mode 100644 index 00000000..dec308c4 --- /dev/null +++ b/src/renderer/src/pages/splash/splash.tsx @@ -0,0 +1,82 @@ +import icon from "@renderer/assets/icon.png"; +import * as styles from "./splash.css"; +import { themeClass } from "../../theme.css"; + +import "../../app.css"; +import { useEffect, useState } from "react"; +import { AppUpdaterEvents } from "@types"; +import { useTranslation } from "react-i18next"; + +document.body.classList.add(themeClass); + +export default function Splash() { + const [status, setStatus] = useState(null); + const [newVersion, setNewVersion] = useState(""); + + const { t } = useTranslation("splash"); + + useEffect(() => { + const unsubscribe = window.electron.onAutoUpdaterEvent( + (event: AppUpdaterEvents) => { + setStatus(event); + + switch (event.type) { + case "error": + window.electron.continueToMainWindow(); + break; + case "update-available": + setNewVersion(event.info.version); + break; + case "update-cancelled": + window.electron.continueToMainWindow(); + break; + case "update-downloaded": + window.electron.restartAndInstallUpdate(); + break; + case "update-not-available": + window.electron.continueToMainWindow(); + break; + } + } + ); + + window.electron.checkForUpdates(); + + return () => { + unsubscribe(); + }; + }, []); + + const renderUpdateInfo = () => { + switch (status?.type) { + case "download-progress": + return ( + <> +

{t("downloading_version", { version: newVersion })}

+ + + ); + case "checking-for-updates": + return

{t("searching_updates")}

; + case "update-available": + return

{t("update_found", { version: newVersion })}

; + case "update-downloaded": + return

{t("restarting_and_applying")}

; + default: + return <>; + } + }; + + return ( +
+ Hydra Launcher Logo +
+ {renderUpdateInfo()} +
+
+ ); +} diff --git a/src/types/index.ts b/src/types/index.ts index 7895686d..0abce31f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,5 @@ import type { Downloader, GameStatus } from "@shared"; +import { ProgressInfo, UpdateInfo } from "electron-updater"; export type GameShop = "steam" | "epic"; export type CatalogueCategory = "recently_added" | "trending"; @@ -143,3 +144,12 @@ export interface SteamGame { name: string; clientIcon: string | null; } + +export type AppUpdaterEvents = + | { type: "error" } + | { type: "checking-for-updates" } + | { type: "update-not-available" } + | { type: "update-available"; info: UpdateInfo } + | { type: "update-downloaded" } + | { type: "download-progress"; info: ProgressInfo } + | { type: "update-cancelled" };