mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-09 03:37:45 +03:00
Merge branch 'main' into teste-locale2
This commit is contained in:
commit
8f63bc5f15
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: Build
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
182
README.es.md
Normal file
182
README.es.md
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<br>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site)
|
||||||
|
|
||||||
|
<h1 align="center">Hydra Launcher</h1>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<strong>Hydra es un launcher de juegos con su propio cliente de bittorrent y gestor propio de repacks.</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
[![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)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 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**.
|
||||||
|
<br>
|
||||||
|
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!
|
||||||
|
|
||||||
|
## <a name="contribuir"> Contribuir
|
||||||
|
|
||||||
|
### <a name="unete-a-nuestro-telegram"></a> 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
|
||||||
|
|
||||||
|
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
|
||||||
|
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
## Licencia
|
||||||
|
|
||||||
|
Hydra está licenciado bajo la [MIT License](LICENSE).
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "hydra",
|
"name": "hydralauncher",
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"description": "Hydra",
|
"description": "Hydra",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
|
@ -177,5 +177,11 @@
|
|||||||
},
|
},
|
||||||
"modal": {
|
"modal": {
|
||||||
"close": "Close button"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,5 +177,11 @@
|
|||||||
},
|
},
|
||||||
"modal": {
|
"modal": {
|
||||||
"close": "Botón de cierre"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,5 +177,11 @@
|
|||||||
},
|
},
|
||||||
"modal": {
|
"modal": {
|
||||||
"close": "Botão de fechar"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ export const defaultDownloadsPath = app.getPath("downloads");
|
|||||||
|
|
||||||
export const databasePath = path.join(
|
export const databasePath = path.join(
|
||||||
app.getPath("appData"),
|
app.getPath("appData"),
|
||||||
app.getName(),
|
"hydra",
|
||||||
"hydra.db"
|
"hydra.db"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
48
src/main/events/autoupdater/check-for-updates.ts
Normal file
48
src/main/events/autoupdater/check-for-updates.ts
Normal file
@ -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);
|
12
src/main/events/autoupdater/continue-to-main-window.ts
Normal file
12
src/main/events/autoupdater/continue-to-main-window.ts
Normal file
@ -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);
|
17
src/main/events/autoupdater/restart-and-install-update.ts
Normal file
17
src/main/events/autoupdater/restart-and-install-update.ts
Normal file
@ -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);
|
@ -27,6 +27,9 @@ import "./torrenting/start-game-download";
|
|||||||
import "./user-preferences/get-user-preferences";
|
import "./user-preferences/get-user-preferences";
|
||||||
import "./user-preferences/update-user-preferences";
|
import "./user-preferences/update-user-preferences";
|
||||||
import "./user-preferences/auto-launch";
|
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("ping", () => "pong");
|
||||||
ipcMain.handle("getVersion", () => app.getVersion());
|
ipcMain.handle("getVersion", () => app.getVersion());
|
||||||
|
@ -3,11 +3,10 @@ import updater from "electron-updater";
|
|||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { electronApp, optimizer } from "@electron-toolkit/utils";
|
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 { dataSource } from "@main/data-source";
|
||||||
import * as resources from "@locales";
|
import * as resources from "@locales";
|
||||||
import { userPreferencesRepository } from "@main/repository";
|
import { userPreferencesRepository } from "@main/repository";
|
||||||
|
|
||||||
const { autoUpdater } = updater;
|
const { autoUpdater } = updater;
|
||||||
|
|
||||||
autoUpdater.setFeedURL({
|
autoUpdater.setFeedURL({
|
||||||
@ -16,6 +15,8 @@ autoUpdater.setFeedURL({
|
|||||||
repo: "hydra",
|
repo: "hydra",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
autoUpdater.logger = logger;
|
||||||
|
|
||||||
const gotTheLock = app.requestSingleInstanceLock();
|
const gotTheLock = app.requestSingleInstanceLock();
|
||||||
if (!gotTheLock) app.quit();
|
if (!gotTheLock) app.quit();
|
||||||
|
|
||||||
@ -63,12 +64,8 @@ app.whenReady().then(() => {
|
|||||||
where: { id: 1 },
|
where: { id: 1 },
|
||||||
});
|
});
|
||||||
|
|
||||||
WindowManager.createMainWindow();
|
WindowManager.createSplashScreen();
|
||||||
WindowManager.createSystemTray(userPreferences?.language || "en");
|
WindowManager.createSystemTray(userPreferences?.language || "en");
|
||||||
|
|
||||||
WindowManager.mainWindow?.on("ready-to-show", () => {
|
|
||||||
autoUpdater.checkForUpdatesAndNotify();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -148,9 +148,10 @@ export const getNewRepacksFromOnlineFix = async (
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!newRepacks.length) return;
|
if (!newRepacks.length) return;
|
||||||
if (page === totalPages) return;
|
|
||||||
|
|
||||||
await savePage(newRepacks);
|
await savePage(newRepacks);
|
||||||
|
|
||||||
|
if (page === totalPages) return;
|
||||||
|
|
||||||
return getNewRepacksFromOnlineFix(existingRepacks, page + 1, cookieJar);
|
return getNewRepacksFromOnlineFix(existingRepacks, page + 1, cookieJar);
|
||||||
};
|
};
|
||||||
|
@ -111,9 +111,10 @@ export const getNewRepacksFromXatab = async (
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!newRepacks.length) return;
|
if (!newRepacks.length) return;
|
||||||
if (page === totalPages) return;
|
|
||||||
|
|
||||||
await savePage(newRepacks);
|
await savePage(newRepacks);
|
||||||
|
|
||||||
|
if (page === totalPages) return;
|
||||||
|
|
||||||
return getNewRepacksFromXatab(existingRepacks, page + 1);
|
return getNewRepacksFromXatab(existingRepacks, page + 1);
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,8 @@ import { IsNull, Not } from "typeorm";
|
|||||||
|
|
||||||
export class WindowManager {
|
export class WindowManager {
|
||||||
public static mainWindow: Electron.BrowserWindow | null = null;
|
public static mainWindow: Electron.BrowserWindow | null = null;
|
||||||
|
public static splashWindow: Electron.BrowserWindow | null = null;
|
||||||
|
public static isReadyToShowMainWindow = false;
|
||||||
|
|
||||||
private static loadURL(hash = "") {
|
private static loadURL(hash = "") {
|
||||||
// HMR for renderer base on electron-vite cli.
|
// 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() {
|
public static createMainWindow() {
|
||||||
// Create the browser window.
|
if (this.mainWindow || !this.isReadyToShowMainWindow) return;
|
||||||
|
|
||||||
this.mainWindow = new BrowserWindow({
|
this.mainWindow = new BrowserWindow({
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 720,
|
height: 720,
|
||||||
minWidth: 1024,
|
minWidth: 1024,
|
||||||
minHeight: 540,
|
minHeight: 540,
|
||||||
|
backgroundColor: "#1c1c1c",
|
||||||
titleBarStyle: "hidden",
|
titleBarStyle: "hidden",
|
||||||
...(process.platform === "linux" ? { icon } : {}),
|
...(process.platform === "linux" ? { icon } : {}),
|
||||||
trafficLightPosition: { x: 16, y: 16 },
|
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) {
|
public static redirect(hash: string) {
|
||||||
if (!this.mainWindow) this.createMainWindow();
|
if (!this.mainWindow) this.createMainWindow();
|
||||||
this.loadURL(hash);
|
this.loadURL(hash);
|
||||||
|
@ -7,6 +7,7 @@ import type {
|
|||||||
GameShop,
|
GameShop,
|
||||||
TorrentProgress,
|
TorrentProgress,
|
||||||
UserPreferences,
|
UserPreferences,
|
||||||
|
AppUpdaterEvents,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("electron", {
|
contextBridge.exposeInMainWorld("electron", {
|
||||||
@ -112,4 +113,21 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
showOpenDialog: (options: Electron.OpenDialogOptions) =>
|
showOpenDialog: (options: Electron.OpenDialogOptions) =>
|
||||||
ipcRenderer.invoke("showOpenDialog", options),
|
ipcRenderer.invoke("showOpenDialog", options),
|
||||||
platform: process.platform,
|
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"),
|
||||||
});
|
});
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
import * as styles from "./app.css";
|
import * as styles from "./app.css";
|
||||||
import { themeClass } from "./theme.css";
|
import { themeClass } from "./theme.css";
|
||||||
|
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
setSearch,
|
setSearch,
|
||||||
clearSearch,
|
clearSearch,
|
||||||
@ -27,7 +27,7 @@ export interface AppProps {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function App({ children }: AppProps) {
|
export function App() {
|
||||||
const contentRef = useRef<HTMLDivElement>(null);
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
const { updateLibrary } = useLibrary();
|
const { updateLibrary } = useLibrary();
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ export function App({ children }: AppProps) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<section ref={contentRef} className={styles.content}>
|
<section ref={contentRef} className={styles.content}>
|
||||||
{children}
|
<Outlet />
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
|
BIN
src/renderer/src/assets/icon.png
Normal file
BIN
src/renderer/src/assets/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
9
src/renderer/src/declaration.d.ts
vendored
9
src/renderer/src/declaration.d.ts
vendored
@ -1,4 +1,5 @@
|
|||||||
import type {
|
import type {
|
||||||
|
AppUpdaterEvents,
|
||||||
CatalogueCategory,
|
CatalogueCategory,
|
||||||
CatalogueEntry,
|
CatalogueEntry,
|
||||||
Game,
|
Game,
|
||||||
@ -90,6 +91,14 @@ declare global {
|
|||||||
options: Electron.OpenDialogOptions
|
options: Electron.OpenDialogOptions
|
||||||
) => Promise<Electron.OpenDialogReturnValue>;
|
) => Promise<Electron.OpenDialogReturnValue>;
|
||||||
platform: NodeJS.Platform;
|
platform: NodeJS.Platform;
|
||||||
|
|
||||||
|
/* Splash */
|
||||||
|
onAutoUpdaterEvent: (
|
||||||
|
cb: (event: AppUpdaterEvents) => void
|
||||||
|
) => () => Electron.IpcRenderer;
|
||||||
|
checkForUpdates: () => Promise<void>;
|
||||||
|
restartAndInstallUpdate: () => Promise<void>;
|
||||||
|
continueToMainWindow: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
|
@ -27,6 +27,7 @@ import {
|
|||||||
import { store } from "./store";
|
import { store } from "./store";
|
||||||
|
|
||||||
import * as resources from "@locales";
|
import * as resources from "@locales";
|
||||||
|
import Splash from "./pages/splash/splash";
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
@ -46,16 +47,17 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
|
|||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<HashRouter>
|
<HashRouter>
|
||||||
<App>
|
<Routes>
|
||||||
<Routes>
|
<Route path="/splash" Component={Splash} />
|
||||||
|
<Route element={<App />}>
|
||||||
<Route path="/" Component={Home} />
|
<Route path="/" Component={Home} />
|
||||||
<Route path="/catalogue" Component={Catalogue} />
|
<Route path="/catalogue" Component={Catalogue} />
|
||||||
<Route path="/downloads" Component={Downloads} />
|
<Route path="/downloads" Component={Downloads} />
|
||||||
<Route path="/game/:shop/:objectID" Component={GameDetails} />
|
<Route path="/game/:shop/:objectID" Component={GameDetails} />
|
||||||
<Route path="/search" Component={SearchResults} />
|
<Route path="/search" Component={SearchResults} />
|
||||||
<Route path="/settings" Component={Settings} />
|
<Route path="/settings" Component={Settings} />
|
||||||
</Routes>
|
</Route>
|
||||||
</App>
|
</Routes>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</Provider>
|
</Provider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
|
@ -58,9 +58,7 @@ export function GallerySlider({ gameDetails }: GallerySliderProps) {
|
|||||||
if (hasMovies && mediaContainerRef.current) {
|
if (hasMovies && mediaContainerRef.current) {
|
||||||
mediaContainerRef.current.childNodes.forEach((node, index) => {
|
mediaContainerRef.current.childNodes.forEach((node, index) => {
|
||||||
if (node instanceof HTMLVideoElement) {
|
if (node instanceof HTMLVideoElement) {
|
||||||
if (index == mediaIndex) {
|
if (index !== mediaIndex) {
|
||||||
node.play();
|
|
||||||
} else {
|
|
||||||
node.pause();
|
node.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
49
src/renderer/src/pages/splash/splash.css.ts
Normal file
49
src/renderer/src/pages/splash/splash.css.ts
Normal file
@ -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,
|
||||||
|
});
|
82
src/renderer/src/pages/splash/splash.tsx
Normal file
82
src/renderer/src/pages/splash/splash.tsx
Normal file
@ -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<AppUpdaterEvents | null>(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 (
|
||||||
|
<>
|
||||||
|
<p>{t("downloading_version", { version: newVersion })}</p>
|
||||||
|
<progress
|
||||||
|
className={styles.progressBar}
|
||||||
|
max="100"
|
||||||
|
value={status.info.percent}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case "checking-for-updates":
|
||||||
|
return <p>{t("searching_updates")}</p>;
|
||||||
|
case "update-available":
|
||||||
|
return <p>{t("update_found", { version: newVersion })}</p>;
|
||||||
|
case "update-downloaded":
|
||||||
|
return <p>{t("restarting_and_applying")}</p>;
|
||||||
|
default:
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className={styles.main}>
|
||||||
|
<img src={icon} className={styles.splashIcon} alt="Hydra Launcher Logo" />
|
||||||
|
<section className={styles.updateInfoSection}>
|
||||||
|
{renderUpdateInfo()}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import type { Downloader, GameStatus } from "@shared";
|
import type { Downloader, GameStatus } from "@shared";
|
||||||
|
import { ProgressInfo, UpdateInfo } from "electron-updater";
|
||||||
|
|
||||||
export type GameShop = "steam" | "epic";
|
export type GameShop = "steam" | "epic";
|
||||||
export type CatalogueCategory = "recently_added" | "trending";
|
export type CatalogueCategory = "recently_added" | "trending";
|
||||||
@ -143,3 +144,12 @@ export interface SteamGame {
|
|||||||
name: string;
|
name: string;
|
||||||
clientIcon: string | null;
|
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" };
|
||||||
|
Loading…
Reference in New Issue
Block a user