mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-01-23 21:44:55 +03:00
feat: returning with edit profile modal
This commit is contained in:
commit
2304e19558
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -35,7 +35,10 @@ jobs:
|
||||
|
||||
- name: Build Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: yarn build:linux
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libarchive-tools
|
||||
yarn build:linux
|
||||
env:
|
||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
@ -65,3 +68,4 @@ jobs:
|
||||
dist/*.tar.gz
|
||||
dist/*.yml
|
||||
dist/*.blockmap
|
||||
dist/*.pacman
|
||||
|
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@ -37,7 +37,10 @@ jobs:
|
||||
|
||||
- name: Build Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: yarn build:linux
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libarchive-tools
|
||||
yarn build:linux
|
||||
env:
|
||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
@ -68,5 +71,6 @@ jobs:
|
||||
dist/*.tar.gz
|
||||
dist/*.yml
|
||||
dist/*.blockmap
|
||||
dist/*.pacman
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
10
README.be.md
10
README.be.md
@ -5,7 +5,7 @@
|
||||
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site)
|
||||
|
||||
<h1 align="center">Hydra Launcher</h1>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<strong>Hydra - гэта гульнявы лаўнчар з уласным убудаваным кліентам BitTorrent і самастойным scraper`ам для рэпакаў.</strong>
|
||||
</p>
|
||||
@ -20,6 +20,8 @@
|
||||
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
|
||||
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
|
||||
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
|
||||
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
|
||||
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
|
||||
|
||||
![Hydra Catalogue](./docs/screenshot.png)
|
||||
|
||||
@ -27,11 +29,12 @@
|
||||
|
||||
## Змест
|
||||
|
||||
- [Змест](#змест)
|
||||
- [Апісанне](#апісанне)
|
||||
- [Асаблівасці](#асаблівасці)
|
||||
- [Усталёўка](#усталёўка)
|
||||
- [Уклад](#contributing)
|
||||
- [Далучайцеся да нашага Telegram](#join-our-telegram)
|
||||
- [Уклад](#-уклад)
|
||||
- [Далучайцеся да нашага Telegram](#-далучайцеся-да-нашага-telegram)
|
||||
- [Форк і кланаванне рэпазітара](#форк-і-кланаванне-рэпазітара)
|
||||
- [Спосабы ўнесці свой уклад](#спосабы-ўнесці-свой-уклад)
|
||||
- [Структура праекту](#структура-праекту)
|
||||
@ -47,6 +50,7 @@
|
||||
- [Зборка кліента BitTorrent](#зборка-кліента-bittorrent)
|
||||
- [Зборка прыкладання Electron](#зборка-прыкладання-electron)
|
||||
- [Удзельнікі](#удзельнікі)
|
||||
- [Ліцэнзія](#ліцэнзія)
|
||||
|
||||
## Апісанне
|
||||
|
||||
|
182
README.de.md
Normal file
182
README.de.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 ist ein Launcher für Spiele mit einem eigenen eingebetteten BitTorrent-Client.</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)
|
||||
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
|
||||
[![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)
|
||||
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
|
||||
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
|
||||
|
||||
![Hydra Katalog](./docs/screenshot.png)
|
||||
|
||||
</div>
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
- [Über Hydra](#über-hydra)
|
||||
- [Eigenschaften](#eigenschaften)
|
||||
- [Installation](#installation)
|
||||
- [Mitwirken](#mitwirken)
|
||||
- [Tritt uns auf Telegram bei](#tritt-uns-auf-telegram-bei)
|
||||
- [Forke und klone dein Repo](#forke-und-klone-dein-repo)
|
||||
- [Wie du mitwirken kannst](#wie-du-mitwirken-kannst)
|
||||
- [Projektstruktur](#projektstruktur)
|
||||
- [Den Quellcode kompilieren](#den-quellcode-kompilieren)
|
||||
- [Installiere Node.js](#installiere-nodejs)
|
||||
- [Installiere Yarn](#installiere-yarn)
|
||||
- [Installiere Node-Abhängigkeiten](#installiere-node-abhängigkeiten)
|
||||
- [Installiere Python 3.9](#installiere-python-39)
|
||||
- [Installiere Python-Abhängigkeiten](#installiere-python-abhängigkeiten)
|
||||
- [Umgebungsvariablen](#umgebungsvariablen)
|
||||
- [Ausführung](#ausführung)
|
||||
- [Kompilation](#kompilation)
|
||||
- [Kompiliere den BitTorrent-Client](#kompiliere-den-bittorrent-client)
|
||||
- [Kompiliere die Electron-Applikation](#kompiliere-die-electron-applikation)
|
||||
- [Mitwirkende](#mitwirkende)
|
||||
|
||||
## Über Hydra
|
||||
|
||||
**Hydra** ist ein **Launcher für Spiele** mit einem eigenen eingebetteten **BitTorrent-Client**.
|
||||
<br>
|
||||
Der Launcher ist in TypeScript (Electron) und Python, womit das Torrentingsystem durch Einsatz von libtorrent geregelt ist, geschrieben.
|
||||
|
||||
## Eigenschaften
|
||||
|
||||
- Eigener eingebetteter BitTorrent-Client
|
||||
- How Long to Beat (HLTB) Integration auf der Spielseite
|
||||
- Anpassbarkeit des Downloadverzeichnisses
|
||||
- Unterstützung von Windows und Linux
|
||||
- Regelmäßig aktualisiert
|
||||
- Und mehr ...
|
||||
|
||||
## Installation
|
||||
|
||||
Die folgenden Schritte beschreiben den Installationsprozess:
|
||||
|
||||
1. Lade die neueste Version von Hydra von der [Releases](https://github.com/hydralauncher/hydra/releases/latest) Seite herunter.
|
||||
- Für die Installation von Hydra auf Windows, wähle die .exe Datei.
|
||||
- Für die Installation von Hydra auf Linux, wähle die .deb, .rpm oder .zip Datei. (Abhängig von deiner Linux-Distribution)
|
||||
2. Führe die heruntergeladene Datei aus.
|
||||
3. Genieße Hydra!
|
||||
|
||||
## Mitwirken
|
||||
|
||||
### Tritt uns auf Telegram bei
|
||||
|
||||
Wir konzentrieren unsere Diskussionen in unserem [Telegram](https://t.me/hydralauncher) Kanal.
|
||||
|
||||
### Forke und klone dein Repo
|
||||
|
||||
1. Forke das Repo [(Klicke hier, um direkt zu forken)](https://github.com/hydralauncher/hydra/fork)
|
||||
2. Klone deinen geforketen Code `git clone https://github.com/dein_nutzername/hydra`
|
||||
3. Erstelle einen neuen Branch
|
||||
4. Pushe deine Commits
|
||||
5. Stelle eine neue Pull-Anfrage
|
||||
|
||||
### Wie du mitwirken kannst
|
||||
|
||||
- Übersetzung: Wir wollen Hydra so vielen Menschen wie möglich zugänglich machen. Gerne kannst du uns helfen neue Sprachen zu übersetzen oder für Hydra bereits verfügbare Sprachen zu aktualisieren und verbessern.
|
||||
- Code: Hydra ist mit TypeScript, Electron und etwas Python gebaut. Wenn du mitwirken möchtest, tritt unserem [Telegram](https://t.me/hydralauncher) bei!
|
||||
|
||||
### Projektstruktur
|
||||
|
||||
- torrent-client: Wir verwenden die Python-Bibliothek libtorrent zur Verwaltung von Torrent-Downloads.
|
||||
- src/renderer: die UI der Applikation.
|
||||
- src/main: sämtliche Logik liegt hier.
|
||||
|
||||
## Den Quellcode kompilieren
|
||||
|
||||
### Installiere Node.js
|
||||
|
||||
Stelle sicher, dass du Node.js auf deinem System installiert hast. Falls nicht, installiere es von [nodejs.org](https://nodejs.org/).
|
||||
|
||||
### Installiere Yarn
|
||||
|
||||
Yarn ist ein Packetmanager für Node.js. Sollte er dir fehlen, installiere ihn mithilfe der Anleitung auf [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
|
||||
|
||||
### Installiere Node-Abhängigkeiten
|
||||
|
||||
Navigiere zum Projektverzeichnis und installiere die Node-Abhängigkeiten mit Yarn:
|
||||
|
||||
```bash
|
||||
cd hydra
|
||||
yarn
|
||||
```
|
||||
|
||||
### Installiere Python 3.9
|
||||
|
||||
Stelle sicher, dass du Python 3.9 auf deinem System installiert hast. Ansonsten kannst du es von [python.org](https://www.python.org/downloads/release/python-3913/) herunterladen und installieren.
|
||||
|
||||
### Installiere Python-Abhängigkeiten
|
||||
|
||||
Installiere die benötigten Python-Abhängigkeiten mit pip:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Umgebungsvariablen
|
||||
|
||||
Du wirst einen SteamGridDB API Schlüssel benötigen, um die Spielicons bei Installation abzurufen.
|
||||
|
||||
Sobald du einen hast, kannst du die .env.example Datei zu .env kopieren oder umbenennen und den Schlüssel bei STEAMGRIDDB_API_KEY einfügen.
|
||||
|
||||
## Ausführung
|
||||
|
||||
Sobald du alles eingerichtet hast, kannst du den folgenden Befehl nutzen, um sowohl den Electron-Prozess als auch den BitTorrent-Client zu starten:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
## Kompilation
|
||||
|
||||
### Kompiliere den BitTorrent-Client
|
||||
|
||||
Kompiliere den BitTorrent-Client mit folgendem Befehl:
|
||||
|
||||
```bash
|
||||
python torrent-client/setup.py build
|
||||
```
|
||||
|
||||
### Kompiliere die Electron-Applikation
|
||||
|
||||
Kompiliere die Electron-Applikation mit folgendem Befehl:
|
||||
|
||||
Auf Windows:
|
||||
|
||||
```bash
|
||||
yarn build:win
|
||||
```
|
||||
|
||||
Auf Linux:
|
||||
|
||||
```bash
|
||||
yarn build:linux
|
||||
```
|
||||
|
||||
## Mitwirkende
|
||||
|
||||
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
|
||||
</a>
|
||||
|
||||
## Lizenz
|
||||
|
||||
Hydra ist unter der [MIT Lizenz](LICENSE) lizensiert.
|
28
README.es.md
28
README.es.md
@ -5,7 +5,7 @@
|
||||
[<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>
|
||||
@ -13,13 +13,15 @@
|
||||
[![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)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.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)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
|
||||
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
|
||||
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
|
||||
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
|
||||
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
|
||||
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
|
||||
|
||||
![Hydra Catalogue](./docs/screenshot.png)
|
||||
|
||||
@ -27,11 +29,12 @@
|
||||
|
||||
## Tabla de Contenidos
|
||||
|
||||
- [Tabla de Contenidos](#tabla-de-contenidos)
|
||||
- [Acerca de](#acerca-de)
|
||||
- [Características](#caracteristicas)
|
||||
- [Instalación](#Instalacion)
|
||||
- [Contribuir](#contribuir)
|
||||
- [Únete a nuestro Telegram](#unete-a-nuestro-telegram)
|
||||
- [Caracteristicas](#caracteristicas)
|
||||
- [Instalacion](#instalacion)
|
||||
- [Contribuir](#-contribuir)
|
||||
- [Unete 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)
|
||||
@ -40,13 +43,14 @@
|
||||
- [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)
|
||||
- [Instalar Dependencias de Python](#instalar-dependencias-de-python)
|
||||
- [Variables del Entorno](#variables-del-entorno)
|
||||
- [Ejecución](#ejecucion)
|
||||
- [Compilación](#compilacion)
|
||||
- [Ejecucion](#ejecucion)
|
||||
- [Compilacion](#compilacion)
|
||||
- [Compilar el cliente de bittorrent](#compilar-el-cliente-de-bittorrent)
|
||||
- [Compilar la aplicación Electron](#compilar-la-aplicacion-electron)
|
||||
- [Compilar la aplicacion Electron](#compilar-la-aplicacion-electron)
|
||||
- [Colaboradores](#colaboradores)
|
||||
- [Licencia](#licencia)
|
||||
|
||||
## Acerca de
|
||||
|
||||
|
12
README.fr.md
12
README.fr.md
@ -5,7 +5,7 @@
|
||||
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site)
|
||||
|
||||
<h1 align="center">Hydra Launcher</h1>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<strong>Hydra est un lanceur de jeux avec son propre client bittorrent intégré et un scraper de repack auto-géré.</strong>
|
||||
</p>
|
||||
@ -13,13 +13,15 @@
|
||||
[![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)
|
||||
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
|
||||
[![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)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.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)
|
||||
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
|
||||
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
|
||||
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
|
||||
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
|
||||
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
|
||||
|
||||
![Catalogue Hydra](./docs/screenshot.png)
|
||||
|
||||
@ -27,6 +29,7 @@
|
||||
|
||||
## Table des Matières
|
||||
|
||||
- [Table des Matières](#table-des-matières)
|
||||
- [À propos](#à-propos)
|
||||
- [Fonctionnalités](#fonctionnalités)
|
||||
- [Installation](#installation)
|
||||
@ -47,6 +50,7 @@
|
||||
- [Compiler le client bittorrent](#compiler-le-client-bittorrent)
|
||||
- [Compiler l'application Electron](#compiler-lapplication-electron)
|
||||
- [Contributeurs](#contributeurs)
|
||||
- [License](#license)
|
||||
|
||||
## À propos
|
||||
|
||||
|
187
README.it.md
Normal file
187
README.it.md
Normal file
@ -0,0 +1,187 @@
|
||||
<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 è un game launcher con il proprio client bittorrent e autogestore di 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)
|
||||
|
||||
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.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)
|
||||
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
|
||||
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
|
||||
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
|
||||
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
|
||||
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
|
||||
|
||||
![Hydra Catalogue](./docs/screenshot.png)
|
||||
|
||||
</div>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [A proposito](#a-proposito)
|
||||
- [Caratteristiche](#caratteristiche)
|
||||
- [Installazione](#installazione)
|
||||
- [Contribuire](#-contribuire)
|
||||
- [Unisciti su Telegram](#-unisciti-su-telegram)
|
||||
- [Forka e Clona la repository](#forka-e-clona-la-repository)
|
||||
- [Modi in cui contribuire](#modi-in-cui-contribuire)
|
||||
- [Struttura del Progetto](#struttura-del-progetto)
|
||||
- [Compilazione](#compilazione)
|
||||
- [Installa Node.js](#installa-nodejs)
|
||||
- [Installa Yarn](#installa-yarn)
|
||||
- [Installa le dipendenze Node](#installa-le-dipendenze-node)
|
||||
- [Installa Python 3.9](#installa-python-39)
|
||||
- [Installa le Dipendenze Python](#installa-le-dipendenze-python)
|
||||
- [Variabili d'ambiente](#variabili-dambiente)
|
||||
- [Esecuzione](#esecuzione)
|
||||
- [Compilazione](#compilazione-1)
|
||||
- [Compila il bittorrent](#compila-il-bittorrent)
|
||||
- [Compila l'applicazione Electron](#compila-lapplicazione-electron)
|
||||
- [Collaboratori](#collaboratori)
|
||||
- [Licenza](#licenza)
|
||||
|
||||
## A proposito
|
||||
|
||||
**Hydra** è un **Game Launcher** con il proprio **Client BitTorrent** e **autogestore di repack**.
|
||||
<br>
|
||||
Il launcher è scritto in TypeScript (Electron) and Python, che gestisce il sistema di torrenting appoggiandosi a libtorrent.
|
||||
|
||||
## Caratteristiche
|
||||
|
||||
- Motore di ricerca automatizzato sulle fonti di repack dal [Megathread]("https://www.reddit.com/r/Piracy/wiki/megathread/")
|
||||
- Client Bittorrent integrato
|
||||
- Integrazione How Long To Beat (HLTB) nella pagina del gioco
|
||||
- Percorso del download Personalizzato
|
||||
- Notifiche di aggiornamenti sulla list dei repacks
|
||||
- Supporto Windows e Linux
|
||||
- Costantemente Aggiornato
|
||||
- E molto altro ...
|
||||
|
||||
## Installazione
|
||||
|
||||
Segui i seguenti passi:
|
||||
|
||||
1. Scarica l'ultima versione di Hydra dalla pagina [Releases](https://github.com/hydralauncher/hydra/releases/latest).
|
||||
- Scarica solo il file .exe per installare Hydra su Windows.
|
||||
- Scarica il file .deb o .rpm o .zip per Linux. (Dipende dalla tua distro Linux)
|
||||
2. Esegui il file scaricato.
|
||||
3. Goditi Hydra!
|
||||
|
||||
## <a name="contribuire"> Contribuire
|
||||
|
||||
### <a name="unisciti-su-telegram"></a> Unisciti su Telegram
|
||||
|
||||
Puoi unirti alle nostre conversazioni sul canale [Telegram](https://t.me/hydralauncher).
|
||||
|
||||
### Forka e Clona la repository
|
||||
|
||||
1. Forka la repository [(clicca qui per forkare)](https://github.com/hydralauncher/hydra/fork)
|
||||
2. Clona il tuo codice forkato `git clone https://github.com/your_username/hydra`
|
||||
3. Crea un nuovo branch
|
||||
4. Aggiungi le modifiche (push)
|
||||
5. Invia la richiesta di pull
|
||||
|
||||
### Modi in cui contribuire
|
||||
|
||||
- Traduzione: Vogliamo rendere Hydra disponibile a più persone possibile. Sentiti libero di tradurre in altre lingue o aggiornare e migliorare quelle già disponibili su Hydra.
|
||||
- Programmazione: Hydra è programmato in TypeScript, Electron e un po' di Python. Se intendi contribuire unisciti al nostro [Telegram](https://t.me/hydralauncher)!
|
||||
|
||||
### Struttura del Progetto
|
||||
|
||||
- client-torrent: Usiamo libtorrent, una libreria Python, per gestire i download dei torrent
|
||||
- src/renderer: l'UI dell'applicazione
|
||||
- src/main: tutta la logica qui.
|
||||
|
||||
## Compilazione
|
||||
|
||||
### Installa Node.js
|
||||
|
||||
Assicurati di avere Node.js installato sulla tua macchina. Scaricalo e installalo da [nodejs.org](https://nodejs.org/).
|
||||
|
||||
### Installa Yarn
|
||||
|
||||
Yarn è un gestore di pacchetti per Node.js. Se non hai ancora installato Yarn segui le istruzioni su [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/).
|
||||
|
||||
### Installa le dipendenze Node
|
||||
|
||||
Naviga alla cartella del progetto e installa le dipendenze Node con Yarn:
|
||||
|
||||
```bash
|
||||
cd hydra
|
||||
yarn
|
||||
```
|
||||
|
||||
### Installa Python 3.9
|
||||
|
||||
Assicurati di avere Python 3.9 installato. Puoi scaricarlo da [python.org](https://www.python.org/downloads/release/python-3913/).
|
||||
|
||||
### Installa le Dipendenze Python
|
||||
|
||||
Installa le dipendenze con pip:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Variabili d'ambiente
|
||||
|
||||
Avrai bisogno di una chiave API SteamGridDB per poter caricare le icone di gioco.
|
||||
Se intendi avere onlinefix come repacker dovrai aggiungere le tue credenziali al file .env
|
||||
|
||||
Una volta ottenuta, puoi copiare e rinominare il file `.env.example` a `.env` e metterlo in `STEAMGRIDDB_API_KEY`, `ONLINEFIX_USERNAME`, `ONLINEFIX_PASSWORD`.
|
||||
|
||||
## Esecuzione
|
||||
|
||||
Una volta impostato tutto, puoi eseguire il seguente comando per avviare il processo Electron e il client bittorrent:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
## Compilazione
|
||||
|
||||
### Compila il bittorrent
|
||||
|
||||
Usa il comando:
|
||||
|
||||
```bash
|
||||
python torrent-client/setup.py build
|
||||
```
|
||||
|
||||
### Compila l'applicazione Electron
|
||||
|
||||
Usa il comando:
|
||||
|
||||
Per Windows:
|
||||
|
||||
```bash
|
||||
yarn build:win
|
||||
```
|
||||
|
||||
Per Linux:
|
||||
|
||||
```bash
|
||||
yarn build:linux
|
||||
```
|
||||
|
||||
## Collaboratori
|
||||
|
||||
<a href="https://github.com/hydralauncher/hydra/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=hydralauncher/hydra" />
|
||||
</a>
|
||||
|
||||
## Licenza
|
||||
|
||||
Hydra è concesso in licenza secondo la [MIT License](LICENSE).
|
14
README.md
14
README.md
@ -5,7 +5,7 @@
|
||||
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site)
|
||||
|
||||
<h1 align="center">Hydra Launcher</h1>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<strong>Hydra is a game launcher with its own embedded bittorrent client.</strong>
|
||||
</p>
|
||||
@ -13,13 +13,15 @@
|
||||
[![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)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.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)
|
||||
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
|
||||
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
|
||||
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
|
||||
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
|
||||
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
|
||||
|
||||
![Hydra Catalogue](./docs/screenshot.png)
|
||||
|
||||
@ -27,11 +29,12 @@
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [About](#about)
|
||||
- [Features](#features)
|
||||
- [Installation](#installation)
|
||||
- [Contributing](#contributing)
|
||||
- [Join our Telegram](#join-our-telegram)
|
||||
- [Contributing](#-contributing)
|
||||
- [Join our Telegram](#-join-our-telegram)
|
||||
- [Fork and clone your repository](#fork-and-clone-your-repository)
|
||||
- [Ways you can contribute](#ways-you-can-contribute)
|
||||
- [Project Structure](#project-structure)
|
||||
@ -47,6 +50,7 @@
|
||||
- [Build the bittorrent client](#build-the-bittorrent-client)
|
||||
- [Build the Electron application](#build-the-electron-application)
|
||||
- [Contributors](#contributors)
|
||||
- [License](#license)
|
||||
|
||||
## About
|
||||
|
||||
|
34
README.pl.md
34
README.pl.md
@ -5,7 +5,7 @@
|
||||
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site)
|
||||
|
||||
<h1 align="center">Hydra Launcher</h1>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<strong>Hydra - to program uruchamiający gry z własnym wbudowanym klientem bittorrent i samodzielnie zarządzanym repackagerem..</strong>
|
||||
</p>
|
||||
@ -13,13 +13,15 @@
|
||||
[![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)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
|
||||
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.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)
|
||||
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
|
||||
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
|
||||
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
|
||||
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
|
||||
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
|
||||
|
||||
![Hydra Catalogue](./docs/screenshot.png)
|
||||
|
||||
@ -27,26 +29,28 @@
|
||||
|
||||
## Zawartość.
|
||||
|
||||
- [Zawartość.](#zawartość)
|
||||
- [O nas](#o-nas)
|
||||
- [Cechy.](#cechy)
|
||||
- [Cechy](#cechy)
|
||||
- [Instalacja](#instalacja)
|
||||
- [Dokonaj wpłaty](#dokonaj-wpłaty)
|
||||
- [Dołącz do naszego kanału Telegram](#dołącz-do-naszego-kanału-telegram)
|
||||
- [Dokonaj wpłaty](#-dokonaj-wpłaty)
|
||||
- [Dołącz do naszego kanału Telegram](#-dołącz-do-naszego-kanału-telegram)
|
||||
- [Rozwidlenie i sklonowanie repozytorium](#rozwidlenie-i-sklonowanie-repozytorium)
|
||||
- [Jak możesz wnieść swój wkład](#jak-możesz-pomóc)
|
||||
- [Jak możesz pomóc](#jak-możesz-pomóc)
|
||||
- [Struktura projektu](#struktura-projektu)
|
||||
- [Utwórz kompilację z kodu źródłowego](#utwórz-kompilację-z-kodu-źródłowego)
|
||||
- [Instalacja Node.js](#zainstaluj-nodejs)
|
||||
- [Instalacja Yarn](#zainstaluj-yarn)
|
||||
- [Instalacja Node zależności](#zainstaluj-zależności-node)
|
||||
- [Instalacja Python 3.9](#zainstaluj-python-39)
|
||||
- [Instalacja Python zależności](#zainstaluj-zależności-pythona)
|
||||
- [Zainstaluj Node.js](#zainstaluj-nodejs)
|
||||
- [Zainstaluj Yarn](#zainstaluj-yarn)
|
||||
- [Zainstaluj zależności Node](#zainstaluj-zależności-node)
|
||||
- [Zainstaluj Python 3.9](#zainstaluj-python-39)
|
||||
- [Zainstaluj zależności Pythona](#zainstaluj-zależności-pythona)
|
||||
- [Zmienne środowiskowe](#zmienne-środowiskowe)
|
||||
- [Uruchomienie](#utwórz-kompilację-z-kodu-źródłowego)
|
||||
- [Run](#run)
|
||||
- [Tworzenie kompilacji](#tworzenie-kompilacji)
|
||||
- [Tworzenie klienta bittorrent](#zbuduj-klienta-bittorrent)
|
||||
- [Tworzenie kompilacji aplikacji Electron](#tworzenie-aplikacji-electron)
|
||||
- [Zbuduj klienta bittorrent](#zbuduj-klienta-bittorrent)
|
||||
- [Tworzenie aplikacji Electron](#tworzenie-aplikacji-electron)
|
||||
- [Współtwórcy](#współtwórcy)
|
||||
- [License](#license)
|
||||
|
||||
## O nas
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site)
|
||||
|
||||
<h1 align="center">Hydra Launcher</h1>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<strong>Hydra é um Launcher de Jogos com seu próprio cliente de bittorrent integrado e um wrapper autogerenciado para busca de repacks.</strong>
|
||||
</p>
|
||||
@ -13,13 +13,15 @@
|
||||
[![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)
|
||||
[![pt-BR](https://img.shields.io/badge/lang-pt--BR-green.svg)](README.pt-BR.md)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.md)
|
||||
[![pl](https://img.shields.io/badge/lang-pl-white)](README.pl.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)
|
||||
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
|
||||
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
|
||||
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
|
||||
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
|
||||
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
|
||||
|
||||
![Hydra Catalogue](./docs/screenshot.png)
|
||||
|
||||
@ -27,26 +29,28 @@
|
||||
|
||||
## Índice
|
||||
|
||||
- [Sobre](#about)
|
||||
- [Recursos](#features)
|
||||
- [Instalação](#installation)
|
||||
- [Contribuindo](#contributing)
|
||||
- [Junte-se ao nosso Telegram](#join-our-telegram)
|
||||
- [Fork e clone seu repositorio](#fork-and-clone-your-repository)
|
||||
- [Como contribuir](#ways-you-can-contribute)
|
||||
- [Estrutura do projeto](#project-structure)
|
||||
- [Compile a partir do código-fonte](#build-from-source)
|
||||
- [Instale Node.js](#install-nodejs)
|
||||
- [Instale Yarn](#install-yarn)
|
||||
- [Instale Node Dependencies](#install-node-dependencies)
|
||||
- [Instale Python 3.9](#install-python-39)
|
||||
- [Instale Python Dependencies](#install-python-dependencies)
|
||||
- [variaveis de ambiente](#environment-variables)
|
||||
- [Rodando o programa](#running)
|
||||
- [Compilando](#build)
|
||||
- [Compile o client bittorrent](#build-the-bittorrent-client)
|
||||
- [Compile a aplicação Electron](#build-the-electron-application)
|
||||
- [Contribuidores](#contributors)
|
||||
- [Índice](#índice)
|
||||
- [Sobre](#-sobre)
|
||||
- [Recursos](#-recursos)
|
||||
- [Instalação](#-instalação)
|
||||
- [Contribuindo](#-contribuindo)
|
||||
- [Junte-se ao nosso Telegram](#-junte-se-ao-nosso-telegram)
|
||||
- [Fork e clone o seu repositório](#-fork-e-clone-o-seu-repositório)
|
||||
- [Formas de contribuir](#-formas-de-contribuir)
|
||||
- [Estrutura do Projeto](#-estrutura-do-projeto)
|
||||
- [Compile a partir do código-fonte](#-compile-a-partir-do-código-fonte)
|
||||
- [Instale Node.js](#-instale-nodejs)
|
||||
- [Instale Yarn](#-instale-yarn)
|
||||
- [Instale Dependencias do Node](#-instale-dependencias-do-node)
|
||||
- [Instale Python 3.9](#-instale-python-39)
|
||||
- [Instale Python Dependencies](#-instale-python-dependencies)
|
||||
- [Environment variables](#-environment-variables)
|
||||
- [Running](#-running)
|
||||
- [Build](#-build)
|
||||
- [Build the bittorrent client](#-build-the-bittorrent-client)
|
||||
- [Build the Electron application](#-build-the-electron-application)
|
||||
- [Contributors](#-contributors)
|
||||
- [Licença](#-licença)
|
||||
|
||||
## <a name="about"> Sobre
|
||||
|
||||
|
16
README.ru.md
16
README.ru.md
@ -5,7 +5,7 @@
|
||||
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site)
|
||||
|
||||
<h1 align="center">Hydra Launcher</h1>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<strong>Hydra - это игровой лаунчер с собственным встроенным клиентом BitTorrent и самостоятельным scraper`ом для репаков.</strong>
|
||||
</p>
|
||||
@ -13,13 +13,15 @@
|
||||
[![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)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.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)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.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)
|
||||
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
|
||||
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
|
||||
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
|
||||
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
|
||||
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
|
||||
|
||||
![Hydra Catalogue](./docs/screenshot.png)
|
||||
|
||||
@ -27,11 +29,12 @@
|
||||
|
||||
## Содержание
|
||||
|
||||
- [Содержание](#содержание)
|
||||
- [Описание](#описание)
|
||||
- [Особенности](#особенности)
|
||||
- [Установка](#установка)
|
||||
- [Вклад](#contributing)
|
||||
- [Присоединяйтесь к нашему Telegram](#join-our-telegram)
|
||||
- [Вклад](#-вклад)
|
||||
- [Присоединяйтесь к нашему Telegram](#-присоединяйтесь-к-нашему-telegram)
|
||||
- [Форк и клонирование репозитория](#форк-и-клонирование-репозитория)
|
||||
- [Способы внести свой вклад](#способы-внести-свой-вклад)
|
||||
- [Структура проекта](#структура-проекта)
|
||||
@ -47,6 +50,7 @@
|
||||
- [Сборка клиента BitTorrent](#сборка-клиента-bittorrent)
|
||||
- [Сборка приложения Electron](#сборка-приложения-electron)
|
||||
- [Участники](#участники)
|
||||
- [License](#license)
|
||||
|
||||
## Описание
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
[<img src="./resources/icon.png" width="144"/>](https://hydralauncher.site)
|
||||
|
||||
<h1 align="center">Hydra Launcher</h1>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<strong>Hydra - це ігровий лаунчер з власним вбудованим bittorrent-клієнтом і самокерованим збирачем репаків.</strong>
|
||||
</p>
|
||||
@ -13,13 +13,15 @@
|
||||
[![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)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.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)
|
||||
[![en](https://img.shields.io/badge/lang-en-red.svg)](README.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)
|
||||
[![be](https://img.shields.io/badge/lang-be-orange)](README.be.md)
|
||||
[![es](https://img.shields.io/badge/lang-es-red)](README.es.md)
|
||||
[![fr](https://img.shields.io/badge/lang-fr-blue)](README.fr.md)
|
||||
[![de](https://img.shields.io/badge/lang-de-black)](README.de.md)
|
||||
[![ita](https://img.shields.io/badge/lang-it-red)](README.it.md)
|
||||
|
||||
![Hydra Catalogue](./docs/screenshot.png)
|
||||
|
||||
@ -27,11 +29,12 @@
|
||||
|
||||
## Зміст
|
||||
|
||||
- [Зміст](#зміст)
|
||||
- [Про нас](#про-нас)
|
||||
- [Функції](#функції)
|
||||
- [Встановлення](#встановлення)
|
||||
- [Зробити свій внесок](#contributing)
|
||||
- [Приєднуйтесь до нашого Telegram](#join-our-telegram)
|
||||
- [Зробити свій внесок](#-зробити-свій-внесок)
|
||||
- [Приєднуйтесь до нашого Telegram](#-приєднуйтесь-до-нашого-telegram)
|
||||
- [Форк і клонування вашого репозиторію](#форк-і-клонування-вашого-репозиторію)
|
||||
- [Як ви можете зробити свій внесок](#як-ви-можете-зробити-свій-внесок)
|
||||
- [Структура проекту](#структура-проекту)
|
||||
@ -47,6 +50,7 @@
|
||||
- [Зробіть білд bittorrent client](#зробіть-білд-bittorrent-client)
|
||||
- [Зробіть білд Electron застосунку](#зробіть-білд-electron-застосунку)
|
||||
- [Контриб'ютори](#контрибютори)
|
||||
- [License](#license)
|
||||
|
||||
## Про нас
|
||||
|
||||
|
@ -44,6 +44,7 @@ linux:
|
||||
- AppImage
|
||||
- snap
|
||||
- deb
|
||||
- pacman
|
||||
- rpm
|
||||
maintainer: electronjs.org
|
||||
category: Game
|
||||
|
@ -42,7 +42,7 @@
|
||||
"@vanilla-extract/dynamic": "^2.1.1",
|
||||
"@vanilla-extract/recipes": "^0.5.2",
|
||||
"auto-launch": "^5.0.6",
|
||||
"axios": "^1.6.8",
|
||||
"axios": "^1.7.7",
|
||||
"better-sqlite3": "^11.2.1",
|
||||
"check-disk-space": "^3.4.0",
|
||||
"classnames": "^2.5.1",
|
||||
@ -58,7 +58,6 @@
|
||||
"i18next": "^23.11.2",
|
||||
"i18next-browser-languagedetector": "^7.2.1",
|
||||
"icojs": "^0.19.3",
|
||||
"iso-639-1": "3.1.2",
|
||||
"jsdom": "^24.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"knex": "^3.1.0",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "اَلْعَرَبِيَّةُ",
|
||||
"home": {
|
||||
"featured": "مميّز",
|
||||
"trending": "شائع",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "беларуская мова",
|
||||
"home": {
|
||||
"featured": "Рэкамэндаванае",
|
||||
"trending": "Актуальнае",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Català",
|
||||
"app": {
|
||||
"successfully_signed_in": "Has entrat correctament"
|
||||
},
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Dansk",
|
||||
"home": {
|
||||
"featured": "Anbefalet",
|
||||
"trending": "Trender",
|
||||
|
277
src/locales/de/translation.json
Normal file
277
src/locales/de/translation.json
Normal file
@ -0,0 +1,277 @@
|
||||
{
|
||||
"language_name": "Deutsch",
|
||||
"app": {
|
||||
"successfully_signed_in": "Erfolgreich angemeldet"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Empfohlen",
|
||||
"trending": "Beliebt",
|
||||
"surprise_me": "Überrasche mich",
|
||||
"no_results": "Keine Ergebnisse gefunden"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "Katalog",
|
||||
"downloads": "Downloads",
|
||||
"settings": "Einstellungen",
|
||||
"my_library": "Meine Bibliothek",
|
||||
"downloading_metadata": "{{title}} (Metadaten werden heruntergeladen…)",
|
||||
"paused": "{{title}} (Pausiert)",
|
||||
"downloading": "{{title}} ({{percentage}} - Wird heruntergeladen…)",
|
||||
"filter": "Bibliothek filtern",
|
||||
"home": "Home",
|
||||
"queued": "{{title}} (In Warteschlange)",
|
||||
"game_has_no_executable": "Spiel hat keine ausführbare Datei gewählt",
|
||||
"sign_in": "Anmelden"
|
||||
},
|
||||
"header": {
|
||||
"search": "Spiele suchen",
|
||||
"home": "Home",
|
||||
"catalogue": "Katalog",
|
||||
"downloads": "Downloads",
|
||||
"search_results": "Suchergebnisse",
|
||||
"settings": "Einstellungen",
|
||||
"version_available_install": "Version {{version}} verfügbar. Klicke hier, um neuzustarten und sie zu installieren.",
|
||||
"version_available_download": "Version {{version}} verfügbar. Klicke hier, um sie herunterzuladen."
|
||||
},
|
||||
"bottom_panel": {
|
||||
"no_downloads_in_progress": "Keine aktive Downloads",
|
||||
"downloading_metadata": "Metadaten von {{title}} werden heruntergeladen…",
|
||||
"downloading": "{{title}} wird heruntergeladen… ({{percentage}} abgeschlossen) - Abschluss {{eta}} - {{speed}}",
|
||||
"calculating_eta": "{{title}} wird heruntergeladen… ({{percentage}} abgeschlossen) - Verbleibende Zeit wird berechnet…",
|
||||
"checking_files": "Prüfe Dateien von {{title}}… ({{percentage}} abgeschlossen)"
|
||||
},
|
||||
"catalogue": {
|
||||
"next_page": "Nächste Seite",
|
||||
"previous_page": "Vorherige Seite"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Download-Optionen öffnen",
|
||||
"download_options_zero": "Keine Download-Optionen",
|
||||
"download_options_one": "{{count}} Download-Option",
|
||||
"download_options_other": "{{count}} Download-Optionen",
|
||||
"updated_at": "Aktualisiert {{updated_at}}",
|
||||
"install": "Installieren",
|
||||
"resume": "Fortfahren",
|
||||
"pause": "Pausieren",
|
||||
"cancel": "Abbrechen",
|
||||
"remove": "Entfernen",
|
||||
"space_left_on_disk": "{{space}} auf Festplatte verfügbar",
|
||||
"eta": "Abschluss {{eta}}",
|
||||
"calculating_eta": "Verbleibende Zeit wird berechnet…",
|
||||
"downloading_metadata": "Metadaten werden heruntergeladen…",
|
||||
"filter": "Repacks filtern",
|
||||
"requirements": "Systemanforderungen",
|
||||
"minimum": "Minimum",
|
||||
"recommended": "Empfohlen",
|
||||
"paused": "Pausiert",
|
||||
"release_date": "Veröffentlicht am {{date}}",
|
||||
"publisher": "Veröffentlicht von {{publisher}}",
|
||||
"hours": "Stunden",
|
||||
"minutes": "Minuten",
|
||||
"amount_hours": "{{amount}} Stunden",
|
||||
"amount_minutes": "{{amount}} Minuten",
|
||||
"accuracy": "{{accuracy}}% Genauigkeit",
|
||||
"add_to_library": "Zu Bibliothek hinzufügen",
|
||||
"remove_from_library": "Von Bibliothek entfernen",
|
||||
"no_downloads": "Keine Downloads verfügbar",
|
||||
"play_time": "{{amount}} lang gespielt",
|
||||
"last_time_played": "Zuletzt gespielt {{period}}",
|
||||
"not_played_yet": "{{title}} wurde noch nicht gespielt",
|
||||
"next_suggestion": "Nächste Empfehlung",
|
||||
"play": "Spielen",
|
||||
"deleting": "Installer wird gelöscht…",
|
||||
"close": "Schließen",
|
||||
"playing_now": "Spielt jetzt",
|
||||
"change": "Ändern",
|
||||
"repacks_modal_description": "Wähle das Repack, das du herunterladen möchtest",
|
||||
"select_folder_hint": "Um das Standardverzeichnis zu ändern, gehe zu den <0>Einstellungen</0>",
|
||||
"download_now": "Jetzt herunterladen",
|
||||
"no_shop_details": "Shop-Details konnten nicht abgerufen werden.",
|
||||
"download_options": "Download-Optionen",
|
||||
"download_path": "Download-Verzeichnis",
|
||||
"previous_screenshot": "Vorheriger Screenshot",
|
||||
"next_screenshot": "Nächster Screenshot",
|
||||
"screenshot": "Screenshot {{number}}",
|
||||
"open_screenshot": "Screenshot {{number}} öffnen",
|
||||
"download_settings": "Download-Einstellungen",
|
||||
"downloader": "Downloader",
|
||||
"select_executable": "Auswählen",
|
||||
"no_executable_selected": "Keine ausführbare Datei gewählt",
|
||||
"open_folder": "Verzeichnis öffnen",
|
||||
"open_download_location": "Heruntergeladene Dateien anzeigen",
|
||||
"create_shortcut": "Desktop-Verknüpfung erstellen",
|
||||
"remove_files": "Dateien entfernen",
|
||||
"remove_from_library_title": "Bist du dir sicher?",
|
||||
"remove_from_library_description": "Dies wird {{game}} aus deiner Bibliothek entfernen",
|
||||
"options": "Optionen",
|
||||
"executable_section_title": "Ausführbare Datei",
|
||||
"executable_section_description": "Pfad der Datei, die bei Klick auf \"Play\" ausgeführt wird",
|
||||
"downloads_secion_title": "Downloads",
|
||||
"downloads_section_description": "Sieh dir Updates oder andere Versionen dieses Spiels an",
|
||||
"danger_zone_section_title": "Gefahrenzone",
|
||||
"danger_zone_section_description": "Entferne dieses Spiel aus deiner Bibliothek oder die von Hydra heruntergeladenen Dateien",
|
||||
"download_in_progress": "Download erfolgt",
|
||||
"download_paused": "Download ist pausiert",
|
||||
"last_downloaded_option": "Letzte Download-Option",
|
||||
"create_shortcut_success": "Verknüpfung erfolgreich erstellt",
|
||||
"create_shortcut_error": "Fehler bei Erstellung von Verknüpfung"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Hydra aktivieren",
|
||||
"installation_id": "Installations ID:",
|
||||
"enter_activation_code": "Aktivierungscode eingeben",
|
||||
"message": "Wenn du nicht weißt wo du fragen musst, solltest du dies nicht haben.",
|
||||
"activate": "Aktivieren",
|
||||
"loading": "Lädt…"
|
||||
},
|
||||
"downloads": {
|
||||
"resume": "Fortfahren",
|
||||
"pause": "Pause",
|
||||
"eta": "Abschluss {{eta}}",
|
||||
"paused": "Pausiert",
|
||||
"verifying": "Verifiziere…",
|
||||
"completed": "Abgeschlossen",
|
||||
"removed": "Nicht heruntergeladen",
|
||||
"cancel": "Abbrechen",
|
||||
"filter": "Heruntergeladene Spiele filtern",
|
||||
"remove": "Entfernen",
|
||||
"downloading_metadata": "Metadaten werden heruntergeladen…",
|
||||
"deleting": "Installer wird entfernt…",
|
||||
"delete": "Installer entfernen",
|
||||
"delete_modal_title": "Bist du dir sicher?",
|
||||
"delete_modal_description": "Dies wird alle Installationsdateien von deinem Computer entfernen",
|
||||
"install": "Installieren",
|
||||
"download_in_progress": "Läuft",
|
||||
"queued_downloads": "Downloads in Warteschlange",
|
||||
"downloads_completed": "Abgeschlossen",
|
||||
"queued": "In Warteschlange",
|
||||
"no_downloads_title": "Welch Leere",
|
||||
"no_downloads_description": "Du hast mit Hydra noch nichts heruntergeladen, aber es ist nie zu spät anzufangen.",
|
||||
"checking_files": "Dateien werden überprüft…"
|
||||
},
|
||||
"settings": {
|
||||
"downloads_path": "Download-Pfad",
|
||||
"change": "Aktualisieren",
|
||||
"notifications": "Benachrichtigungen",
|
||||
"enable_download_notifications": "Wenn ein Download abgeschlossen wird",
|
||||
"enable_repack_list_notifications": "Wenn ein neues Repack hinzugefügt wird",
|
||||
"real_debrid_api_token_label": "Real-Debrid API Token",
|
||||
"quit_app_instead_hiding": "Hydra verlassen statt minimieren beim Schließen",
|
||||
"launch_with_system": "Hydra bei Systemstart starten",
|
||||
"general": "Allgemein",
|
||||
"behavior": "Verhalten",
|
||||
"download_sources": "Download-Quellen",
|
||||
"language": "Sprache",
|
||||
"real_debrid_api_token": "API Token",
|
||||
"enable_real_debrid": "Real-Debrid aktivieren",
|
||||
"real_debrid_description": "Real-Debrid ist ein unrestriktiver Downloader, der es dir ermöglicht Dateien sofort und mit deiner maximalen Internetgeschwindigkeit herunterzuladen.",
|
||||
"real_debrid_invalid_token": "API token nicht gültig",
|
||||
"real_debrid_api_token_hint": "<0>Hier</0> kannst du dir deinen API Token holen",
|
||||
"real_debrid_free_account_error": "Das Konto \"{{username}}\" ist ein gratis account. Bitte abonniere Real-Debrid",
|
||||
"real_debrid_linked_message": "Konto \"{{username}}\" verknüpft",
|
||||
"save_changes": "Änderungen speichern",
|
||||
"changes_saved": "Änderungen erfolgreich gespeichert",
|
||||
"download_sources_description": "Hydra wird die Download-Links von diesen Quellen abrufen. Die Quell-URL muss ein direkter Link zu einer .json Datei, welche die Download-Links enthält, sein.",
|
||||
"validate_download_source": "Validieren",
|
||||
"remove_download_source": "Entfernen",
|
||||
"add_download_source": "Quelle hinzufügen",
|
||||
"download_count_zero": "Keine Download-Option",
|
||||
"download_count_one": "{{countFormatted}} Download-Option",
|
||||
"download_count_other": "{{countFormatted}} Download-Optionen",
|
||||
"download_source_url": "Download Quell-URL",
|
||||
"add_download_source_description": "Füge die URL, welche die .json Datei enthält, ein",
|
||||
"download_source_up_to_date": "Auf aktuellem Stand",
|
||||
"download_source_errored": "Fehlgeschlagen",
|
||||
"sync_download_sources": "Quellen synchronisieren",
|
||||
"removed_download_source": "Download-Quelle entfernt",
|
||||
"added_download_source": "Download-Quelle hinzugefügt",
|
||||
"download_sources_synced": "Alle Download-Quellen sind synchronisiert",
|
||||
"insert_valid_json_url": "Füge eine gültige JSON URL ein",
|
||||
"found_download_option_zero": "Keine Download-Option gefunden",
|
||||
"found_download_option_one": "{{countFormatted}} Download-Option gefunden",
|
||||
"found_download_option_other": "{{countFormatted}} Download-Optionen gefunden",
|
||||
"import": "Importieren"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Download abgeschlossen",
|
||||
"game_ready_to_install": "{{title}} ist bereit zur Installation",
|
||||
"repack_list_updated": "Repack-Liste aktualisiert",
|
||||
"repack_count_one": "{{count}} Repack hinzugefügt",
|
||||
"repack_count_other": "{{count}} Repacks hinzugefügt",
|
||||
"new_update_available": "Version {{version}} verfügbar",
|
||||
"restart_to_install_update": "Um das Update zu installieren, starte Hydra neu"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "Hydra öffnen",
|
||||
"quit": "Schließen"
|
||||
},
|
||||
"game_card": {
|
||||
"no_downloads": "Keine Downloads verfügbar"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "Programme nicht installiert",
|
||||
"description": "Ausführbare Dateien für Wine oder Lutris wurden auf deinem System nicht gefunden",
|
||||
"instructions": "Überprüfe die korrekte Installation dieser für deine Linux-Distro, damit das Spiel normal laufen kann"
|
||||
},
|
||||
"modal": {
|
||||
"close": "Knopf schließen"
|
||||
},
|
||||
"forms": {
|
||||
"toggle_password_visibility": "Sichtbarkeit des Passworts umschalten"
|
||||
},
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} Stunden",
|
||||
"amount_minutes": "{{amount}} Minuten",
|
||||
"last_time_played": "Zuletzt gespielt {{period}}",
|
||||
"activity": "Letzte Aktivität",
|
||||
"library": "Bibliothek",
|
||||
"total_play_time": "Gesamtspielzeit: {{amount}}",
|
||||
"no_recent_activity_title": "Hmmm… hier ist nichts",
|
||||
"no_recent_activity_description": "Du hast in letzter Zeit keine Spiele gespielt. Es wird Zeit das zu ändern!",
|
||||
"display_name": "Anzeigename",
|
||||
"saving": "Speichert",
|
||||
"save": "Speichern",
|
||||
"edit_profile": "Profil Bearbeiten",
|
||||
"saved_successfully": "Erfolgreich gespeichert",
|
||||
"try_again": "Bitte versuche es erneut",
|
||||
"sign_out_modal_title": "Bist du dir sicher?",
|
||||
"cancel": "Abbrechen",
|
||||
"successfully_signed_out": "Erfolgreich abgemeldet",
|
||||
"sign_out": "Abmelden",
|
||||
"playing_for": "Spielt {{amount}} lang",
|
||||
"sign_out_modal_text": "Deine Bibliothek ist mit deinem aktuellen Konto verknüpft. Wenn du dich abmeldest, wird deine Bibliothek nicht mehr sichtbar sein und jeglicher Fortschritt wird nicht gespeichert. Abmelden fortführen?",
|
||||
"add_friends": "Freunde hinzufügen",
|
||||
"add": "Hinzufügen",
|
||||
"friend_code": "Freundescode",
|
||||
"see_profile": "Profil anzeigen",
|
||||
"sending": "Sendet",
|
||||
"friend_request_sent": "Freundschaftsanfrage versendet",
|
||||
"friends": "Freunde",
|
||||
"friends_list": "Freundesliste",
|
||||
"user_not_found": "Nutzer nicht gefunden",
|
||||
"block_user": "Nutzer blockieren",
|
||||
"add_friend": "Freund hinzufügen",
|
||||
"request_sent": "Anfrage versendet",
|
||||
"request_received": "Anfrage erhalten",
|
||||
"accept_request": "Anfrage annehmen",
|
||||
"ignore_request": "Anfrage ignorieren",
|
||||
"cancel_request": "Anfrage zurückziehen",
|
||||
"undo_friendship": "Freundschaft kündigen",
|
||||
"request_accepted": "Anfrage akzeptiert",
|
||||
"user_blocked_successfully": "Nutzer erfolgreich blockiert",
|
||||
"user_block_modal_text": "{{displayName}} wird dadurch blockiert",
|
||||
"settings": "Einstellungen",
|
||||
"public": "Öffentlich",
|
||||
"private": "Privat",
|
||||
"friends_only": "Nur Freunde",
|
||||
"privacy": "Privatsphäre",
|
||||
"blocked_users": "Blockierte Nutzer",
|
||||
"unblock": "Freigeben",
|
||||
"no_friends_added": "Du hast noch keine Freunde hinzugefügt",
|
||||
"pending": "Ausstehend",
|
||||
"no_pending_invites": "Du hast keine ausstehenden Einladungen",
|
||||
"no_blocked_users": "Du hast keine blockierten Nutzer",
|
||||
"friend_code_copied": "Freundescode kopiert",
|
||||
"undo_friendship_modal_text": "Freundschaft mit {{displayName}} wird dadurch gekündigt"
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "English",
|
||||
"app": {
|
||||
"successfully_signed_in": "Successfully signed in"
|
||||
},
|
||||
@ -20,7 +21,8 @@
|
||||
"home": "Home",
|
||||
"queued": "{{title}} (Queued)",
|
||||
"game_has_no_executable": "Game has no executable selected",
|
||||
"sign_in": "Sign in"
|
||||
"sign_in": "Sign in",
|
||||
"friends": "Friends"
|
||||
},
|
||||
"header": {
|
||||
"search": "Search games",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Español",
|
||||
"app": {
|
||||
"successfully_signed_in": "Sesión iniciada correctamente"
|
||||
},
|
||||
@ -270,6 +271,7 @@
|
||||
"pending": "Pendiente",
|
||||
"no_pending_invites": "No tienes invitaciones pendientes",
|
||||
"no_blocked_users": "No has bloqueado a ningún usuario",
|
||||
"friend_code_copied": "Código de amigo copiado"
|
||||
"friend_code_copied": "Código de amigo copiado",
|
||||
"undo_friendship_modal_text": "Esto deshará tu amistad con {{displayName}}"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "فارسی",
|
||||
"home": {
|
||||
"featured": "پیشنهادی",
|
||||
"trending": "پرطرفدار",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Français",
|
||||
"home": {
|
||||
"featured": "En vedette",
|
||||
"trending": "Tendance",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Magyar",
|
||||
"home": {
|
||||
"featured": "Featured",
|
||||
"trending": "Népszerű",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Bahasa Indonesia",
|
||||
"app": {
|
||||
"successfully_signed_in": "Berhasil masuk"
|
||||
},
|
||||
|
@ -1,21 +1,49 @@
|
||||
export { default as en } from "./en/translation.json";
|
||||
export { default as pt } from "./pt/translation.json";
|
||||
export { default as es } from "./es/translation.json";
|
||||
export { default as nl } from "./nl/translation.json";
|
||||
export { default as fr } from "./fr/translation.json";
|
||||
export { default as hu } from "./hu/translation.json";
|
||||
export { default as it } from "./it/translation.json";
|
||||
export { default as pl } from "./pl/translation.json";
|
||||
export { default as ru } from "./ru/translation.json";
|
||||
export { default as tr } from "./tr/translation.json";
|
||||
export { default as be } from "./be/translation.json";
|
||||
export { default as uk } from "./uk/translation.json";
|
||||
export { default as zh } from "./zh/translation.json";
|
||||
export { default as id } from "./id/translation.json";
|
||||
export { default as ko } from "./ko/translation.json";
|
||||
export { default as da } from "./da/translation.json";
|
||||
export { default as ar } from "./ar/translation.json";
|
||||
export { default as fa } from "./fa/translation.json";
|
||||
export { default as ro } from "./ro/translation.json";
|
||||
export { default as ca } from "./ca/translation.json";
|
||||
export { default as kk } from "./kk/translation.json";
|
||||
import en from "./en/translation.json";
|
||||
import ptPT from "./pt-PT/translation.json";
|
||||
import ptBR from "./pt-BR/translation.json";
|
||||
import es from "./es/translation.json";
|
||||
import nl from "./nl/translation.json";
|
||||
import fr from "./fr/translation.json";
|
||||
import hu from "./hu/translation.json";
|
||||
import it from "./it/translation.json";
|
||||
import de from "./de/translation.json";
|
||||
import pl from "./pl/translation.json";
|
||||
import ru from "./ru/translation.json";
|
||||
import tr from "./tr/translation.json";
|
||||
import be from "./be/translation.json";
|
||||
import uk from "./uk/translation.json";
|
||||
import zh from "./zh/translation.json";
|
||||
import id from "./id/translation.json";
|
||||
import ko from "./ko/translation.json";
|
||||
import da from "./da/translation.json";
|
||||
import ar from "./ar/translation.json";
|
||||
import fa from "./fa/translation.json";
|
||||
import ro from "./ro/translation.json";
|
||||
import ca from "./ca/translation.json";
|
||||
import kk from "./kk/translation.json";
|
||||
|
||||
export default {
|
||||
"pt-BR": ptBR,
|
||||
"pt-PT": ptPT,
|
||||
en,
|
||||
de,
|
||||
es,
|
||||
nl,
|
||||
fr,
|
||||
hu,
|
||||
it,
|
||||
pl,
|
||||
ru,
|
||||
tr,
|
||||
be,
|
||||
uk,
|
||||
zh,
|
||||
id,
|
||||
ko,
|
||||
da,
|
||||
ar,
|
||||
fa,
|
||||
ro,
|
||||
ca,
|
||||
kk,
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Italiano",
|
||||
"home": {
|
||||
"featured": "In primo piano",
|
||||
"trending": "Di tendenza",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "қазақ тілі",
|
||||
"app": {
|
||||
"successfully_signed_in": "Сәтті кіру"
|
||||
},
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "한국어",
|
||||
"home": {
|
||||
"featured": "추천",
|
||||
"trending": "인기",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Nederlands",
|
||||
"home": {
|
||||
"featured": "Uitgelicht",
|
||||
"trending": "Trending",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Polski",
|
||||
"home": {
|
||||
"featured": "Wyróżnione",
|
||||
"trending": "Trendujące",
|
||||
|
@ -1,10 +1,13 @@
|
||||
{
|
||||
"language_name": "Português (Brasil)",
|
||||
"app": {
|
||||
"successfully_signed_in": "Autenticado com sucesso"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Destaques",
|
||||
"trending": "Populares",
|
||||
"hot": "🔥 Populares agora",
|
||||
"weekly": "📅 Mais baixados da semana",
|
||||
"surprise_me": "Surpreenda-me",
|
||||
"no_results": "Nenhum resultado encontrado"
|
||||
},
|
||||
@ -20,7 +23,8 @@
|
||||
"home": "Início",
|
||||
"queued": "{{title}} (Na fila)",
|
||||
"game_has_no_executable": "Jogo não possui executável selecionado",
|
||||
"sign_in": "Login"
|
||||
"sign_in": "Login",
|
||||
"friends": "Amigos"
|
||||
},
|
||||
"header": {
|
||||
"search": "Buscar jogos",
|
||||
@ -166,7 +170,7 @@
|
||||
"real_debrid_linked_message": "Conta \"{{username}}\" vinculada",
|
||||
"save_changes": "Salvar mudanças",
|
||||
"changes_saved": "Ajustes salvos com sucesso",
|
||||
"download_sources_description": "Hydra vai buscar links de download em todas as fonte habilitadas. A URL da fonte deve ser um link direto para um arquivo .json contendo uma lista de links.",
|
||||
"download_sources_description": "Hydra vai buscar links de download em todas as fontes habilitadas. A URL da fonte deve ser um link direto para um arquivo .json contendo uma lista de links.",
|
||||
"validate_download_source": "Validar",
|
||||
"remove_download_source": "Remover",
|
||||
"add_download_source": "Adicionar fonte",
|
||||
@ -239,7 +243,7 @@
|
||||
"cancel": "Cancelar",
|
||||
"successfully_signed_out": "Deslogado com sucesso",
|
||||
"sign_out": "Sair da conta",
|
||||
"sign_out_modal_title": "Tem certeza?",
|
||||
"sign_out_modal_title": "Deseja mesmo sair?",
|
||||
"playing_for": "Jogando por {{amount}}",
|
||||
"sign_out_modal_text": "Sua biblioteca de jogos está associada com a sua conta atual. Ao sair, sua biblioteca não aparecerá mais no Hydra e qualquer progresso não será salvo. Deseja continuar?",
|
||||
"add_friends": "Adicionar Amigos",
|
281
src/locales/pt-PT/translation.json
Normal file
281
src/locales/pt-PT/translation.json
Normal file
@ -0,0 +1,281 @@
|
||||
{
|
||||
"language_name": "Português (Portugal)",
|
||||
"app": {
|
||||
"successfully_signed_in": "Sessão iniciada com sucesso"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Destaques",
|
||||
"trending": "Populares",
|
||||
"surprise_me": "Surpreende-me",
|
||||
"no_results": "Nenhum resultado encontrado"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "Catálogo",
|
||||
"downloads": "Transferências",
|
||||
"settings": "Definições",
|
||||
"my_library": "Biblioteca",
|
||||
"downloading_metadata": "{{title}} (A transferir metadados…)",
|
||||
"paused": "{{title}} (Pausado)",
|
||||
"downloading": "{{title}} ({{percentage}} - A transferir…)",
|
||||
"filter": "Procurar",
|
||||
"home": "Início",
|
||||
"queued": "{{title}} (Na fila)",
|
||||
"game_has_no_executable": "Jogo não tem executável selecionado",
|
||||
"sign_in": "Iniciar sessão"
|
||||
},
|
||||
"header": {
|
||||
"search": "Procurar jogos",
|
||||
"catalogue": "Catálogo",
|
||||
"downloads": "Transferências",
|
||||
"search_results": "Resultados da pesquisa",
|
||||
"settings": "Definições",
|
||||
"home": "Início",
|
||||
"version_available_install": "Versão {{version}} disponível. Clique aqui para reiniciar e instalar.",
|
||||
"version_available_download": "Versão {{version}} disponível. Clique aqui para fazer o download."
|
||||
},
|
||||
"bottom_panel": {
|
||||
"no_downloads_in_progress": "Sem transferências em andamento",
|
||||
"downloading_metadata": "A transferir metadados de {{title}}…",
|
||||
"downloading": "A transferir {{title}}… ({{percentage}} concluído) - Conclusão {{eta}} - {{speed}}",
|
||||
"calculating_eta": "A transferir {{title}}… ({{percentage}} concluído) - A calcular tempo restante…",
|
||||
"checking_files": "A verificar ficheiros de {{title}}…"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Ver opções de transferência",
|
||||
"download_options_zero": "Sem opções de transferência",
|
||||
"download_options_one": "{{count}} opção de transferência",
|
||||
"download_options_other": "{{count}} opções de transferência",
|
||||
"updated_at": "Atualizado a {{updated_at}}",
|
||||
"resume": "Retomar",
|
||||
"pause": "Pausar",
|
||||
"cancel": "Cancelar",
|
||||
"remove": "Remover",
|
||||
"space_left_on_disk": "{{space}} livres no disco",
|
||||
"eta": "Conclusão {{eta}}",
|
||||
"calculating_eta": "A calcular tempo restante…",
|
||||
"downloading_metadata": "A transferir metadados…",
|
||||
"filter": "Filtrar repacks",
|
||||
"requirements": "Requisitos do sistema",
|
||||
"minimum": "Mínimos",
|
||||
"recommended": "Recomendados",
|
||||
"paused": "Pausado",
|
||||
"release_date": "Lançado em {{date}}",
|
||||
"publisher": "Publicado por {{publisher}}",
|
||||
"hours": "horas",
|
||||
"minutes": "minutos",
|
||||
"amount_hours": "{{amount}} horas",
|
||||
"amount_minutes": "{{amount}} minutos",
|
||||
"accuracy": "{{accuracy}}% de precisão",
|
||||
"add_to_library": "Adicionar à biblioteca",
|
||||
"remove_from_library": "Remover da biblioteca",
|
||||
"no_downloads": "Nenhuma transferência disponível",
|
||||
"play_time": "Jogou por {{amount}}",
|
||||
"next_suggestion": "Próxima sugestão",
|
||||
"install": "Instalar",
|
||||
"last_time_played": "Última sessão {{period}}",
|
||||
"play": "Jogar",
|
||||
"not_played_yet": "Ainda não jogou {{title}}",
|
||||
"close": "Fechar",
|
||||
"deleting": "A eliminar instalador…",
|
||||
"playing_now": "A jogar agora",
|
||||
"change": "Explorar",
|
||||
"repacks_modal_description": "Escolha o repack do jogo que deseja transferir",
|
||||
"select_folder_hint": "Para trocar o diretório padrão, aceda à <0>Tela de Definições</0>",
|
||||
"download_now": "Iniciar transferência",
|
||||
"no_shop_details": "Não foi possível obter os detalhes da loja.",
|
||||
"download_options": "Opções de transferência",
|
||||
"download_path": "Diretório de transferência",
|
||||
"previous_screenshot": "Captura de ecrã anterior",
|
||||
"next_screenshot": "Próxima captura de ecrã",
|
||||
"screenshot": "Captura de ecrã {{number}}",
|
||||
"open_screenshot": "Ver captura de ecrã {{number}}",
|
||||
"download_settings": "Definições de transferência",
|
||||
"downloader": "Downloader",
|
||||
"select_executable": "Explorar",
|
||||
"no_executable_selected": "Nenhum executável selecionado",
|
||||
"open_folder": "Abrir pasta",
|
||||
"open_download_location": "Ver ficheiros transferidos",
|
||||
"create_shortcut": "Criar atalho no ambiente de trabalho",
|
||||
"remove_files": "Remover ficheiros",
|
||||
"options": "Gerir",
|
||||
"remove_from_library_description": "Isto irá remover {{game}} da sua biblioteca",
|
||||
"remove_from_library_title": "Tem a certeza?",
|
||||
"executable_section_title": "Executável",
|
||||
"executable_section_description": "O caminho do ficheiro que será executado ao clicar em \"Jogar\"",
|
||||
"downloads_secion_title": "Transferências",
|
||||
"downloads_section_description": "Confira atualizações ou versões diferentes para este mesmo título",
|
||||
"danger_zone_section_title": "Zona de perigo",
|
||||
"danger_zone_section_description": "Remova o jogo da sua biblioteca ou os ficheiros que foram transferidos pelo Hydra",
|
||||
"download_in_progress": "Transferência em andamento",
|
||||
"download_paused": "Transferência pausada",
|
||||
"last_downloaded_option": "Última opção transferida",
|
||||
"create_shortcut_success": "Atalho criado com sucesso",
|
||||
"create_shortcut_error": "Erro ao criar atalho"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Ativação",
|
||||
"installation_id": "ID da instalação:",
|
||||
"enter_activation_code": "Insira o seu código de ativação",
|
||||
"message": "Se não sabe onde conseguir o código, talvez não devesse estar aqui.",
|
||||
"activate": "Ativar",
|
||||
"loading": "A carregar…"
|
||||
},
|
||||
"downloads": {
|
||||
"resume": "Retomar",
|
||||
"pause": "Pausar",
|
||||
"eta": "Conclusão {{eta}}",
|
||||
"paused": "Pausado",
|
||||
"verifying": "A verificar…",
|
||||
"completed": "Concluído",
|
||||
"removed": "Cancelado",
|
||||
"cancel": "Cancelar",
|
||||
"filter": "Filtrar jogos transferidos",
|
||||
"remove": "Remover",
|
||||
"downloading_metadata": "A transferir metadados…",
|
||||
"delete": "Remover instalador",
|
||||
"delete_modal_description": "Isto removerá todos os ficheiros de instalação do seu computador",
|
||||
"delete_modal_title": "Tem a certeza?",
|
||||
"deleting": "A eliminar instalador…",
|
||||
"install": "Instalar",
|
||||
"download_in_progress": "A transferir agora",
|
||||
"queued_downloads": "Na fila",
|
||||
"downloads_completed": "Concluído",
|
||||
"queued": "Na fila",
|
||||
"no_downloads_title": "Nada por aqui…",
|
||||
"no_downloads_description": "Ainda não transferiu nada pelo Hydra, mas nunca é tarde para começar.",
|
||||
"checking_files": "A verificar ficheiros…"
|
||||
},
|
||||
"settings": {
|
||||
"downloads_path": "Diretório das transferências",
|
||||
"change": "Explorar...",
|
||||
"notifications": "Notificações",
|
||||
"enable_download_notifications": "Quando uma transferência for concluída",
|
||||
"enable_repack_list_notifications": "Quando a lista de repacks for atualizada",
|
||||
"real_debrid_api_token_label": "Token de API do Real-Debrid",
|
||||
"quit_app_instead_hiding": "Encerrar o Hydra em vez de apenas minimizá-lo ao fechar.",
|
||||
"launch_with_system": "Iniciar o Hydra com o sistema",
|
||||
"general": "Geral",
|
||||
"behavior": "Comportamento",
|
||||
"download_sources": "Fontes de transferência",
|
||||
"language": "Idioma",
|
||||
"real_debrid_api_token": "Token de API",
|
||||
"enable_real_debrid": "Ativar Real-Debrid",
|
||||
"real_debrid_api_token_hint": "Pode obter o seu token de API <0>aqui</0>",
|
||||
"real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite transferir ficheiros instantaneamente e com a melhor velocidade da sua Internet.",
|
||||
"real_debrid_invalid_token": "Token de API inválido",
|
||||
"real_debrid_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, subscreva o Real-Debrid",
|
||||
"real_debrid_linked_message": "Conta \"{{username}}\" vinculada",
|
||||
"save_changes": "Guardar alterações",
|
||||
"changes_saved": "Definições guardadas com sucesso",
|
||||
"download_sources_description": "O Hydra vai procurar links de transferência em todas as fontes ativadas. A URL da página de detalhes da loja não é guardada no seu dispositivo. Utilizamos um sistema de metadados criado pela comunidade para fornecer suporte a mais fontes de transferência de jogos.",
|
||||
"enable_source": "Ativar",
|
||||
"disable_source": "Desativar",
|
||||
"validate_download_source": "Validar",
|
||||
"remove_download_source": "Remover",
|
||||
"add_download_source": "Adicionar fonte",
|
||||
"download_count_zero": "Sem transferências na lista",
|
||||
"download_count_one": "{{countFormatted}} transferência na lista",
|
||||
"download_count_other": "{{countFormatted}} transferências na lista",
|
||||
"download_options_zero": "Sem transferências disponíveis",
|
||||
"download_options_one": "{{countFormatted}} transferência disponível",
|
||||
"download_options_other": "{{countFormatted}} transferências disponíveis",
|
||||
"download_source_url": "URL da fonte",
|
||||
"add_download_source_description": "Insira o URL contendo o arquivo .json",
|
||||
"download_source_up_to_date": "Sincronizada",
|
||||
"download_source_errored": "Falhou",
|
||||
"sync_download_sources": "Sincronizar",
|
||||
"removed_download_source": "Fonte removida",
|
||||
"added_download_source": "Fonte adicionada",
|
||||
"download_sources_synced": "As fontes foram sincronizadas",
|
||||
"insert_valid_json_url": "Insira o URL de um JSON válido",
|
||||
"found_download_option_zero": "Nenhuma opção de transferência encontrada",
|
||||
"found_download_option_one": "{{countFormatted}} opção de transferência encontrada",
|
||||
"found_download_option_other": "{{countFormatted}} opções de transferências encontradas",
|
||||
"import": "Importar"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Transferência concluída",
|
||||
"game_ready_to_install": "{{title}} está pronto para ser descarregado",
|
||||
"repack_list_updated": "Lista de repacks atualizada",
|
||||
"repack_count_one": "{{count}} novo repack",
|
||||
"repack_count_other": "{{count}} novos repacks",
|
||||
"new_update_available": "Versão {{version}} disponível",
|
||||
"restart_to_install_update": "Reinicie o Hydra para instalar a nova versão"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "Abrir Hydra",
|
||||
"quit": "Fechar"
|
||||
},
|
||||
"game_card": {
|
||||
"no_downloads": "Sem transferências disponíveis"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "Programas não instalados",
|
||||
"description": "Os executáveis do Wine ou Lutris não foram encontrados em seu sistema.",
|
||||
"instructions": "Verifique a forma correta de instalar algum deles na sua distro Linux, garantindo assim a execução normal do jogo."
|
||||
},
|
||||
"catalogue": {
|
||||
"next_page": "Próxima página",
|
||||
"previous_page": "Página anterior"
|
||||
},
|
||||
"modal": {
|
||||
"close": "Botão de fechar"
|
||||
},
|
||||
"forms": {
|
||||
"toggle_password_visibility": "Alternar visibilidade da palavra-passe"
|
||||
},
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} horas",
|
||||
"amount_minutes": "{{amount}} minutos",
|
||||
"last_time_played": "Última sessão {{period}}",
|
||||
"activity": "Atividades recentes",
|
||||
"library": "Biblioteca",
|
||||
"total_play_time": "Tempo total de jogo: {{amount}}",
|
||||
"no_recent_activity_title": "Hmmm… nada por aqui",
|
||||
"no_recent_activity_description": "Parece que não jogaste nada recentemente. Que tal começar agora?",
|
||||
"display_name": "Nome de exibição",
|
||||
"saving": "a guardar…",
|
||||
"save": "Guardar",
|
||||
"edit_profile": "Editar perfil",
|
||||
"saved_successfully": "Guardado com sucesso",
|
||||
"try_again": "Por favor, tenta novamente",
|
||||
"cancel": "Cancelar",
|
||||
"successfully_signed_out": "Terminado com sucesso",
|
||||
"sign_out": "Terminar sessão",
|
||||
"sign_out_modal_title": "Tens a certeza?",
|
||||
"playing_for": "A jogar há {{amount}}",
|
||||
"sign_out_modal_text": "A tua biblioteca de jogos está associada com a tua conta atual. Ao sair, a tua biblioteca não aparecerá mais no Hydra e qualquer progresso não será guardado. Desejas continuar?",
|
||||
"add_friends": "Adicionar Amigos",
|
||||
"friend_code": "Código de amigo",
|
||||
"see_profile": "Ver perfil",
|
||||
"friend_request_sent": "Pedido de amizade enviado",
|
||||
"friends": "Amigos",
|
||||
"add": "Adicionar",
|
||||
"sending": "A enviar",
|
||||
"friends_list": "Lista de amigos",
|
||||
"user_not_found": "Utilizador não encontrado",
|
||||
"block_user": "Bloquear",
|
||||
"add_friend": "Adicionar amigo",
|
||||
"request_sent": "Pedido enviado",
|
||||
"request_received": "Pedido recebido",
|
||||
"accept_request": "Aceitar pedido",
|
||||
"ignore_request": "Ignorar pedido",
|
||||
"cancel_request": "Cancelar pedido",
|
||||
"undo_friendship": "Desfazer amizade",
|
||||
"request_accepted": "Pedido de amizade aceito",
|
||||
"user_blocked_successfully": "Utilizador bloqueado com sucesso",
|
||||
"user_block_modal_text": "Bloquear {{displayName}}",
|
||||
"settings": "Definições",
|
||||
"privacy": "Privacidade",
|
||||
"private": "Privado",
|
||||
"friends_only": "Apenas amigos",
|
||||
"public": "Público",
|
||||
"blocked_users": "Utilizadores bloqueados",
|
||||
"unblock": "Desbloquear",
|
||||
"no_friends_added": "Ainda não adicionaste amigos",
|
||||
"pending": "Pendentes",
|
||||
"no_pending_invites": "Não tens convites de amizade pendentes",
|
||||
"no_blocked_users": "Não tens nenhum utilizador bloqueado",
|
||||
"friend_code_copied": "Código de amigo copiado"
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Română",
|
||||
"home": {
|
||||
"featured": "Recomandate",
|
||||
"trending": "Populare",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Русский",
|
||||
"app": {
|
||||
"successfully_signed_in": "Успешный вход"
|
||||
},
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Türkçe",
|
||||
"home": {
|
||||
"featured": "Öne çıkan",
|
||||
"trending": "Popüler",
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "Українська",
|
||||
"app": {
|
||||
"successfully_signed_in": "Успішний вхід в систему"
|
||||
},
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"language_name": "中文",
|
||||
"app": {
|
||||
"successfully_signed_in": "已成功登录"
|
||||
},
|
||||
|
@ -1,35 +1,42 @@
|
||||
import type { CatalogueEntry, GameShop } from "@types";
|
||||
import type { GameShop } from "@types";
|
||||
|
||||
import { registerEvent } from "../register-event";
|
||||
import { RepacksManager, requestSteam250 } from "@main/services";
|
||||
import { formatName, steamUrlBuilder } from "@shared";
|
||||
import { HydraApi, RepacksManager } from "@main/services";
|
||||
import { CatalogueCategory, formatName, steamUrlBuilder } from "@shared";
|
||||
import { steamGamesWorker } from "@main/workers";
|
||||
|
||||
const resultSize = 12;
|
||||
const getCatalogue = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
category: CatalogueCategory
|
||||
) => {
|
||||
const params = new URLSearchParams({
|
||||
take: "12",
|
||||
skip: "0",
|
||||
});
|
||||
|
||||
const getCatalogue = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
const trendingGames = await requestSteam250("/90day");
|
||||
const results: CatalogueEntry[] = [];
|
||||
const response = await HydraApi.get<{ objectId: string; shop: GameShop }[]>(
|
||||
`/games/${category}?${params.toString()}`
|
||||
);
|
||||
|
||||
for (let i = 0; i < resultSize; i++) {
|
||||
if (!trendingGames[i]) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
return Promise.all(
|
||||
response.map(async (game) => {
|
||||
const steamGame = await steamGamesWorker.run(Number(game.objectId), {
|
||||
name: "getById",
|
||||
});
|
||||
|
||||
const { title, objectID } = trendingGames[i]!;
|
||||
const repacks = RepacksManager.search({ query: formatName(title) });
|
||||
const repacks = RepacksManager.search({
|
||||
query: formatName(steamGame.name),
|
||||
});
|
||||
|
||||
const catalogueEntry = {
|
||||
objectID,
|
||||
title,
|
||||
shop: "steam" as GameShop,
|
||||
cover: steamUrlBuilder.library(objectID),
|
||||
};
|
||||
|
||||
results.push({ ...catalogueEntry, repacks });
|
||||
}
|
||||
|
||||
return results;
|
||||
return {
|
||||
title: steamGame.name,
|
||||
shop: game.shop,
|
||||
repacks,
|
||||
cover: steamUrlBuilder.library(game.objectId),
|
||||
objectID: game.objectId,
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
registerEvent("getCatalogue", getCatalogue);
|
||||
|
23
src/main/events/catalogue/get-game-stats.ts
Normal file
23
src/main/events/catalogue/get-game-stats.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import type { GameShop } from "@types";
|
||||
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import type { GameStats } from "@types";
|
||||
|
||||
const getGameStats = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
) => {
|
||||
const params = new URLSearchParams({
|
||||
objectId,
|
||||
shop,
|
||||
});
|
||||
|
||||
const response = await HydraApi.get<GameStats>(
|
||||
`/games/stats?${params.toString()}`
|
||||
);
|
||||
return response;
|
||||
};
|
||||
|
||||
registerEvent("getGameStats", getGameStats);
|
@ -8,6 +8,7 @@ import "./catalogue/get-how-long-to-beat";
|
||||
import "./catalogue/get-random-game";
|
||||
import "./catalogue/search-games";
|
||||
import "./catalogue/search-game-repacks";
|
||||
import "./catalogue/get-game-stats";
|
||||
import "./hardware/get-disk-free-space";
|
||||
import "./library/add-game-to-library";
|
||||
import "./library/create-game-shortcut";
|
||||
|
@ -1,10 +1,27 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import { HydraApi, logger } from "@main/services";
|
||||
import { steamGamesWorker } from "@main/workers";
|
||||
import type { UserProfile } from "@types";
|
||||
import { getUserFriends } from "./get-user-friends";
|
||||
import { steamUrlBuilder } from "@shared";
|
||||
|
||||
const getSteamGame = async (objectId: string) => {
|
||||
try {
|
||||
const steamGame = await steamGamesWorker.run(Number(objectId), {
|
||||
name: "getById",
|
||||
});
|
||||
|
||||
return {
|
||||
title: steamGame.name,
|
||||
iconUrl: steamUrlBuilder.icon(objectId, steamGame.clientIcon),
|
||||
};
|
||||
} catch (err) {
|
||||
logger.error("Failed to get Steam game", err);
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getUser = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
userId: string
|
||||
@ -20,36 +37,51 @@ const getUser = async (
|
||||
if (!profile) return null;
|
||||
|
||||
const recentGames = await Promise.all(
|
||||
profile.recentGames.map(async (game) => {
|
||||
const steamGame = await steamGamesWorker.run(Number(game.objectId), {
|
||||
name: "getById",
|
||||
});
|
||||
profile.recentGames
|
||||
.map(async (game) => {
|
||||
const steamGame = await getSteamGame(game.objectId);
|
||||
|
||||
return {
|
||||
...game,
|
||||
title: steamGame.name,
|
||||
iconUrl: steamUrlBuilder.icon(game.objectId, steamGame.clientIcon),
|
||||
};
|
||||
})
|
||||
return {
|
||||
...game,
|
||||
...steamGame,
|
||||
};
|
||||
})
|
||||
.filter((game) => game)
|
||||
);
|
||||
|
||||
// const libraryGames = await Promise.all(
|
||||
// profile.libraryGames.map(async (game) => {
|
||||
// return getSteamUserGame(game);
|
||||
// })
|
||||
// );
|
||||
const libraryGames = await Promise.all(
|
||||
profile.libraryGames
|
||||
.map(async (game) => {
|
||||
const steamGame = await getSteamGame(game.objectId);
|
||||
|
||||
// const currentGame = await getGameRunning(profile.currentGame);
|
||||
return {
|
||||
...game,
|
||||
...steamGame,
|
||||
};
|
||||
})
|
||||
.filter((game) => game)
|
||||
);
|
||||
|
||||
if (profile.currentGame) {
|
||||
const steamGame = await getSteamGame(profile.currentGame.objectId);
|
||||
|
||||
if (steamGame) {
|
||||
profile.currentGame = {
|
||||
...profile.currentGame,
|
||||
...steamGame,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...profile,
|
||||
// libraryGames,
|
||||
libraryGames,
|
||||
recentGames,
|
||||
friends: friends.friends,
|
||||
totalFriends: friends.totalFriends,
|
||||
// currentGame,
|
||||
};
|
||||
} catch (err) {
|
||||
console.log("err", err);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ import url from "node:url";
|
||||
import { electronApp, optimizer } from "@electron-toolkit/utils";
|
||||
import { logger, PythonInstance, WindowManager } from "@main/services";
|
||||
import { dataSource } from "@main/data-source";
|
||||
import * as resources from "@locales";
|
||||
import resources from "@locales";
|
||||
import { userPreferencesRepository } from "@main/repository";
|
||||
import { knexClient, migrationConfig } from "./knex-client";
|
||||
|
||||
|
@ -3,6 +3,15 @@ import { HydraApi } from "../hydra-api";
|
||||
import { gameRepository } from "@main/repository";
|
||||
|
||||
export const createGame = async (game: Game) => {
|
||||
console.log({ objectId: game.objectID, shop: game.shop });
|
||||
|
||||
HydraApi.post("/games/download", {
|
||||
objectId: game.objectID,
|
||||
shop: game.shop,
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
HydraApi.post(`/profile/games`, {
|
||||
objectId: game.objectID,
|
||||
playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds),
|
||||
|
@ -12,6 +12,7 @@ import type {
|
||||
FriendRequestAction,
|
||||
UpdateProfileRequest,
|
||||
} from "@types";
|
||||
import type { CatalogueCategory } from "@shared";
|
||||
|
||||
contextBridge.exposeInMainWorld("electron", {
|
||||
/* Torrenting */
|
||||
@ -34,7 +35,8 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
|
||||
/* Catalogue */
|
||||
searchGames: (query: string) => ipcRenderer.invoke("searchGames", query),
|
||||
getCatalogue: () => ipcRenderer.invoke("getCatalogue"),
|
||||
getCatalogue: (category: CatalogueCategory) =>
|
||||
ipcRenderer.invoke("getCatalogue", category),
|
||||
getGameShopDetails: (objectID: string, shop: GameShop, language: string) =>
|
||||
ipcRenderer.invoke("getGameShopDetails", objectID, shop, language),
|
||||
getRandomGame: () => ipcRenderer.invoke("getRandomGame"),
|
||||
@ -44,6 +46,8 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
ipcRenderer.invoke("getGames", take, prevCursor),
|
||||
searchGameRepacks: (query: string) =>
|
||||
ipcRenderer.invoke("searchGameRepacks", query),
|
||||
getGameStats: (objectId: string, shop: GameShop) =>
|
||||
ipcRenderer.invoke("getGameStats", objectId, shop),
|
||||
|
||||
/* User preferences */
|
||||
getUserPreferences: () => ipcRenderer.invoke("getUserPreferences"),
|
||||
|
@ -0,0 +1,13 @@
|
||||
import { SPACING_UNIT, vars } from "../../theme.css";
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
export const actions = style({
|
||||
display: "flex",
|
||||
alignSelf: "flex-end",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
});
|
||||
|
||||
export const descriptionText = style({
|
||||
fontSize: "16px",
|
||||
lineHeight: "24px",
|
||||
});
|
@ -0,0 +1,30 @@
|
||||
import { Button } from "../button/button";
|
||||
import { Modal, type ModalProps } from "../modal/modal";
|
||||
|
||||
import * as styles from "./confirmation-modal.css";
|
||||
|
||||
export interface ConfirmationModalProps extends ModalProps {
|
||||
confirmButtonLabel: string;
|
||||
cancelButtonLabel: string;
|
||||
descriptionText: string;
|
||||
}
|
||||
|
||||
export function ConfirmationModal({
|
||||
confirmButtonLabel,
|
||||
cancelButtonLabel,
|
||||
descriptionText,
|
||||
...props
|
||||
}: ConfirmationModalProps) {
|
||||
return (
|
||||
<Modal {...props}>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
||||
<p className={styles.descriptionText}>{descriptionText}</p>
|
||||
|
||||
<div className={styles.actions}>
|
||||
<Button theme="danger">{cancelButtonLabel}</Button>
|
||||
<Button>{confirmButtonLabel}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -1,11 +1,16 @@
|
||||
import { DownloadIcon, FileDirectoryIcon } from "@primer/octicons-react";
|
||||
import type { CatalogueEntry } from "@types";
|
||||
import {
|
||||
DownloadIcon,
|
||||
FileDirectoryIcon,
|
||||
PeopleIcon,
|
||||
} from "@primer/octicons-react";
|
||||
import type { CatalogueEntry, GameStats } from "@types";
|
||||
|
||||
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
||||
|
||||
import * as styles from "./game-card.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Badge } from "../badge/badge";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
|
||||
export interface GameCardProps
|
||||
extends React.DetailedHTMLProps<
|
||||
@ -22,12 +27,35 @@ const shopIcon = {
|
||||
export function GameCard({ game, ...props }: GameCardProps) {
|
||||
const { t } = useTranslation("game_card");
|
||||
|
||||
const [stats, setStats] = useState<GameStats | null>(null);
|
||||
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const uniqueRepackers = Array.from(
|
||||
new Set(game.repacks.map(({ repacker }) => repacker))
|
||||
);
|
||||
|
||||
const handleHover = useCallback(() => {
|
||||
if (!stats) {
|
||||
window.electron.getGameStats(game.objectID, game.shop).then((stats) => {
|
||||
setStats(stats);
|
||||
});
|
||||
}
|
||||
}, [game, stats]);
|
||||
|
||||
const numberFormatter = useMemo(() => {
|
||||
return new Intl.NumberFormat(i18n.language, {
|
||||
maximumFractionDigits: 0,
|
||||
});
|
||||
}, [i18n.language]);
|
||||
|
||||
return (
|
||||
<button {...props} type="button" className={styles.card}>
|
||||
<button
|
||||
{...props}
|
||||
type="button"
|
||||
className={styles.card}
|
||||
onMouseEnter={handleHover}
|
||||
>
|
||||
<div className={styles.backdrop}>
|
||||
<img src={game.cover} alt={game.title} className={styles.cover} />
|
||||
|
||||
@ -48,19 +76,20 @@ export function GameCard({ game, ...props }: GameCardProps) {
|
||||
) : (
|
||||
<p className={styles.noDownloadsLabel}>{t("no_downloads")}</p>
|
||||
)}
|
||||
|
||||
<div className={styles.specifics}>
|
||||
<div className={styles.specificsItem}>
|
||||
<DownloadIcon />
|
||||
<span>{game.repacks.length}</span>
|
||||
<span>
|
||||
{stats ? numberFormatter.format(stats.downloadCount) : "…"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{game.repacks.length > 0 && (
|
||||
<div className={styles.specificsItem}>
|
||||
<FileDirectoryIcon />
|
||||
<span>{game.repacks.at(0)?.fileSize}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.specificsItem}>
|
||||
<PeopleIcon />
|
||||
<span>
|
||||
{stats ? numberFormatter.format(stats?.playerCount) : "…"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,3 +11,4 @@ export * from "./link/link";
|
||||
export * from "./select-field/select-field";
|
||||
export * from "./toast/toast";
|
||||
export * from "./badge/badge";
|
||||
export * from "./confirmation-modal/confirmation-modal";
|
||||
|
@ -34,7 +34,7 @@ export const profileButtonContent = style({
|
||||
export const profileAvatar = style({
|
||||
width: "35px",
|
||||
height: "35px",
|
||||
borderRadius: "50%",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
@ -52,17 +52,6 @@ export const profileButtonInformation = style({
|
||||
minWidth: 0,
|
||||
});
|
||||
|
||||
export const statusBadge = style({
|
||||
width: "9px",
|
||||
height: "9px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: vars.color.danger,
|
||||
position: "absolute",
|
||||
bottom: "-2px",
|
||||
right: "-3px",
|
||||
zIndex: "1",
|
||||
});
|
||||
|
||||
export const profileButtonTitle = style({
|
||||
fontWeight: "bold",
|
||||
fontSize: vars.size.body,
|
||||
@ -85,11 +74,11 @@ export const friendsButton = style({
|
||||
position: "relative",
|
||||
transition: "all ease 0.3s",
|
||||
":hover": {
|
||||
backgroundColor: "#DADBE1",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||
},
|
||||
});
|
||||
|
||||
export const friendsButtonLabel = style({
|
||||
export const friendsButtonBadge = style({
|
||||
backgroundColor: vars.color.success,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
|
@ -2,10 +2,9 @@ import { useNavigate } from "react-router-dom";
|
||||
import { PeopleIcon, PersonIcon } from "@primer/octicons-react";
|
||||
import * as styles from "./sidebar-profile.css";
|
||||
import { useAppSelector, useUserDetails } from "@renderer/hooks";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
||||
import { FriendRequest } from "@types";
|
||||
|
||||
export function SidebarProfile() {
|
||||
const navigate = useNavigate();
|
||||
@ -14,17 +13,13 @@ export function SidebarProfile() {
|
||||
|
||||
const { userDetails, friendRequests, showFriendsModal } = useUserDetails();
|
||||
|
||||
const [receivedRequests, setReceivedRequests] = useState<FriendRequest[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setReceivedRequests(
|
||||
friendRequests.filter((request) => request.type === "RECEIVED")
|
||||
);
|
||||
const receivedRequests = useMemo(() => {
|
||||
return friendRequests.filter((request) => request.type === "RECEIVED");
|
||||
}, [friendRequests]);
|
||||
|
||||
const { gameRunning } = useAppSelector((state) => state.gameRunning);
|
||||
|
||||
const handleButtonClick = () => {
|
||||
const handleProfileClick = () => {
|
||||
if (userDetails === null) {
|
||||
window.electron.openAuthWindow();
|
||||
return;
|
||||
@ -33,12 +28,35 @@ export function SidebarProfile() {
|
||||
navigate(`/profile/${userDetails!.id}`);
|
||||
};
|
||||
|
||||
const friendsButton = useMemo(() => {
|
||||
if (!userDetails) return null;
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={styles.friendsButton}
|
||||
onClick={() =>
|
||||
showFriendsModal(UserFriendModalTab.AddFriend, userDetails.id)
|
||||
}
|
||||
title={t("friends")}
|
||||
>
|
||||
{receivedRequests.length > 0 && (
|
||||
<small className={styles.friendsButtonBadge}>
|
||||
{receivedRequests.length > 99 ? "99+" : receivedRequests.length}
|
||||
</small>
|
||||
)}
|
||||
|
||||
<PeopleIcon size={16} />
|
||||
</button>
|
||||
);
|
||||
}, [userDetails, t, receivedRequests, showFriendsModal]);
|
||||
|
||||
return (
|
||||
<div className={styles.profileContainer}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.profileButton}
|
||||
onClick={handleButtonClick}
|
||||
onClick={handleProfileClick}
|
||||
>
|
||||
<div className={styles.profileButtonContent}>
|
||||
<div className={styles.profileAvatar}>
|
||||
@ -76,17 +94,7 @@ export function SidebarProfile() {
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={styles.friendsButton}
|
||||
onClick={() =>
|
||||
showFriendsModal(UserFriendModalTab.AddFriend, userDetails.id)
|
||||
}
|
||||
>
|
||||
<small className={styles.friendsButtonLabel}>10</small>
|
||||
|
||||
<PeopleIcon size={16} />
|
||||
</button>
|
||||
{friendsButton}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -21,6 +21,11 @@ export const sidebar = recipe({
|
||||
pointerEvents: "none",
|
||||
},
|
||||
},
|
||||
darwin: {
|
||||
true: {
|
||||
paddingTop: `${SPACING_UNIT * 6}px`,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -153,12 +153,14 @@ export function Sidebar() {
|
||||
<>
|
||||
<aside
|
||||
ref={sidebarRef}
|
||||
className={styles.sidebar({ resizing: isResizing })}
|
||||
className={styles.sidebar({
|
||||
resizing: isResizing,
|
||||
darwin: window.electron.platform === "darwin",
|
||||
})}
|
||||
style={{
|
||||
width: sidebarWidth,
|
||||
minWidth: sidebarWidth,
|
||||
maxWidth: sidebarWidth,
|
||||
paddingTop: 8 * 6,
|
||||
}}
|
||||
>
|
||||
<SidebarProfile />
|
||||
@ -180,8 +182,6 @@ export function Sidebar() {
|
||||
>
|
||||
{render(isDownloading)}
|
||||
<span>{t(nameKey)}</span>
|
||||
|
||||
<ChevronDownIcon />
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
|
@ -9,3 +9,5 @@ export const DOWNLOADER_NAME = {
|
||||
[Downloader.PixelDrain]: "PixelDrain",
|
||||
[Downloader.Qiwi]: "Qiwi",
|
||||
};
|
||||
|
||||
export const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120;
|
||||
|
@ -12,6 +12,8 @@ export interface UserProfileContext {
|
||||
heroBackground: string;
|
||||
/* Indicates if the current user is viewing their own profile */
|
||||
isMe: boolean;
|
||||
|
||||
getUserProfile: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const DEFAULT_USER_PROFILE_BACKGROUND = "#151515B3";
|
||||
@ -20,6 +22,7 @@ export const userProfileContext = createContext<UserProfileContext>({
|
||||
userProfile: null,
|
||||
heroBackground: DEFAULT_USER_PROFILE_BACKGROUND,
|
||||
isMe: false,
|
||||
getUserProfile: async () => {},
|
||||
});
|
||||
|
||||
const { Provider } = userProfileContext;
|
||||
@ -47,7 +50,7 @@ export function UserProfileContextProvider({
|
||||
format: "hex",
|
||||
});
|
||||
|
||||
return `linear-gradient(135deg, ${darkenColor(output as string, 0.6)}, ${darkenColor(output as string, 0.8, 0.7)})`;
|
||||
return `linear-gradient(135deg, ${darkenColor(output as string, 0.5)}, ${darkenColor(output as string, 0.6, 0.5)})`;
|
||||
};
|
||||
|
||||
const { t } = useTranslation("user_profile");
|
||||
@ -73,6 +76,9 @@ export function UserProfileContextProvider({
|
||||
}, [navigate, showErrorToast, userId, t]);
|
||||
|
||||
useEffect(() => {
|
||||
setUserProfile(null);
|
||||
setHeroBackground(DEFAULT_USER_PROFILE_BACKGROUND);
|
||||
|
||||
getUserProfile();
|
||||
}, [getUserProfile]);
|
||||
|
||||
@ -82,6 +88,7 @@ export function UserProfileContextProvider({
|
||||
userProfile,
|
||||
heroBackground,
|
||||
isMe: userDetails?.id === userProfile?.id,
|
||||
getUserProfile,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
5
src/renderer/src/declaration.d.ts
vendored
5
src/renderer/src/declaration.d.ts
vendored
@ -1,3 +1,4 @@
|
||||
import type { CatalogueCategory } from "@shared";
|
||||
import type {
|
||||
AppUpdaterEvent,
|
||||
CatalogueEntry,
|
||||
@ -19,6 +20,7 @@ import type {
|
||||
UserFriends,
|
||||
UserBlocks,
|
||||
UpdateProfileRequest,
|
||||
GameStats,
|
||||
} from "@types";
|
||||
import type { DiskSpace } from "check-disk-space";
|
||||
|
||||
@ -40,7 +42,7 @@ declare global {
|
||||
|
||||
/* Catalogue */
|
||||
searchGames: (query: string) => Promise<CatalogueEntry[]>;
|
||||
getCatalogue: () => Promise<CatalogueEntry[]>;
|
||||
getCatalogue: (category: CatalogueCategory) => Promise<CatalogueEntry[]>;
|
||||
getGameShopDetails: (
|
||||
objectID: string,
|
||||
shop: GameShop,
|
||||
@ -57,6 +59,7 @@ declare global {
|
||||
prevCursor?: number
|
||||
) => Promise<{ results: CatalogueEntry[]; cursor: number }>;
|
||||
searchGameRepacks: (query: string) => Promise<GameRepack[]>;
|
||||
getGameStats: (objectId: string, shop: GameShop) => Promise<GameStats>;
|
||||
|
||||
/* Library */
|
||||
addGameToLibrary: (
|
||||
|
@ -27,7 +27,7 @@ import {
|
||||
|
||||
import { store } from "./store";
|
||||
|
||||
import * as resources from "@locales";
|
||||
import resources from "@locales";
|
||||
|
||||
Sentry.init({});
|
||||
|
||||
|
@ -6,8 +6,7 @@ import { useDate, useDownload } from "@renderer/hooks";
|
||||
import { Link } from "@renderer/components";
|
||||
|
||||
import { gameDetailsContext } from "@renderer/context";
|
||||
|
||||
const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120;
|
||||
import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants";
|
||||
|
||||
export function HeroPanelPlaytime() {
|
||||
const [lastTimePlayed, setLastTimePlayed] = useState("");
|
||||
@ -36,7 +35,7 @@ export function HeroPanelPlaytime() {
|
||||
});
|
||||
}, [i18n.language]);
|
||||
|
||||
const formatPlayTime = () => {
|
||||
const formattedPlayTime = useMemo(() => {
|
||||
const milliseconds = game?.playTimeInMilliseconds || 0;
|
||||
const seconds = milliseconds / 1000;
|
||||
const minutes = seconds / 60;
|
||||
@ -49,7 +48,7 @@ export function HeroPanelPlaytime() {
|
||||
|
||||
const hours = minutes / 60;
|
||||
return t("amount_hours", { amount: numberFormatter.format(hours) });
|
||||
};
|
||||
}, [game?.playTimeInMilliseconds, numberFormatter, t]);
|
||||
|
||||
if (!game) return null;
|
||||
|
||||
@ -96,7 +95,7 @@ export function HeroPanelPlaytime() {
|
||||
<>
|
||||
<p>
|
||||
{t("play_time", {
|
||||
amount: formatPlayTime(),
|
||||
amount: formattedPlayTime,
|
||||
})}
|
||||
</p>
|
||||
|
||||
|
@ -60,3 +60,11 @@ export const noResults = style({
|
||||
gap: "16px",
|
||||
gridColumn: "1 / -1",
|
||||
});
|
||||
|
||||
export const buttonsList = style({
|
||||
display: "flex",
|
||||
listStyle: "none",
|
||||
margin: "0",
|
||||
padding: "0",
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
});
|
||||
|
@ -13,6 +13,7 @@ import * as styles from "./home.css";
|
||||
import { vars } from "@renderer/theme.css";
|
||||
import Lottie from "lottie-react";
|
||||
import { buildGameDetailsPath } from "@renderer/helpers";
|
||||
import { CatalogueCategory } from "@shared";
|
||||
|
||||
export function Home() {
|
||||
const { t } = useTranslation("home");
|
||||
@ -21,15 +22,25 @@ export function Home() {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [randomGame, setRandomGame] = useState<Steam250Game | null>(null);
|
||||
|
||||
const [catalogue, setCatalogue] = useState<CatalogueEntry[]>([]);
|
||||
const [currentCatalogueCategory, setCurrentCatalogueCategory] = useState(
|
||||
CatalogueCategory.Hot
|
||||
);
|
||||
|
||||
const getCatalogue = useCallback(() => {
|
||||
const [catalogue, setCatalogue] = useState<
|
||||
Record<CatalogueCategory, CatalogueEntry[]>
|
||||
>({
|
||||
[CatalogueCategory.Hot]: [],
|
||||
[CatalogueCategory.Weekly]: [],
|
||||
});
|
||||
|
||||
const getCatalogue = useCallback((category: CatalogueCategory) => {
|
||||
setCurrentCatalogueCategory(category);
|
||||
setIsLoading(true);
|
||||
|
||||
window.electron
|
||||
.getCatalogue()
|
||||
.getCatalogue(category)
|
||||
.then((catalogue) => {
|
||||
setCatalogue(catalogue);
|
||||
setCatalogue((prev) => ({ ...prev, [category]: catalogue }));
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
@ -58,11 +69,13 @@ export function Home() {
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
getCatalogue();
|
||||
getCatalogue(CatalogueCategory.Hot);
|
||||
|
||||
getRandomGame();
|
||||
}, [getCatalogue, getRandomGame]);
|
||||
|
||||
const categories = Object.values(CatalogueCategory);
|
||||
|
||||
return (
|
||||
<SkeletonTheme baseColor={vars.color.background} highlightColor="#444">
|
||||
<section className={styles.content}>
|
||||
@ -71,7 +84,22 @@ export function Home() {
|
||||
<Hero />
|
||||
|
||||
<section className={styles.homeHeader}>
|
||||
<h2>{t("trending")}</h2>
|
||||
<ul className={styles.buttonsList}>
|
||||
{categories.map((category) => (
|
||||
<li key={category}>
|
||||
<Button
|
||||
theme={
|
||||
category === currentCatalogueCategory
|
||||
? "primary"
|
||||
: "outline"
|
||||
}
|
||||
onClick={() => getCatalogue(category)}
|
||||
>
|
||||
{t(category)}
|
||||
</Button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<Button
|
||||
onClick={handleRandomizerClick}
|
||||
@ -89,12 +117,14 @@ export function Home() {
|
||||
</Button>
|
||||
</section>
|
||||
|
||||
<h2>{t(currentCatalogueCategory)}</h2>
|
||||
|
||||
<section className={styles.cards}>
|
||||
{isLoading
|
||||
? Array.from({ length: 12 }).map((_, index) => (
|
||||
<Skeleton key={index} className={styles.cardSkeleton} />
|
||||
))
|
||||
: catalogue.map((result) => (
|
||||
: catalogue[currentCatalogueCategory].map((result) => (
|
||||
<GameCard
|
||||
key={result.objectID}
|
||||
game={result}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { vars, SPACING_UNIT } from "../../theme.css";
|
||||
import { vars, SPACING_UNIT } from "../../../theme.css";
|
||||
import { globalStyle, style } from "@vanilla-extract/css";
|
||||
|
||||
export const gameCover = style({
|
||||
transition: "all ease 0.2s",
|
||||
boxShadow: "0 8px 10px -2px rgba(0, 0, 0, 0.5)",
|
||||
width: "100%",
|
||||
":before": {
|
||||
content: "",
|
||||
top: "0",
|
||||
@ -60,14 +61,66 @@ export const friend = style({
|
||||
alignItems: "center",
|
||||
});
|
||||
|
||||
export const friendAvatar = style({
|
||||
width: "50px",
|
||||
height: "50px",
|
||||
borderRadius: "4px",
|
||||
});
|
||||
|
||||
export const friendName = style({
|
||||
color: vars.color.muted,
|
||||
fontWeight: "bold",
|
||||
fontSize: vars.size.body,
|
||||
});
|
||||
|
||||
export const rightContent = style({
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
flexDirection: "column",
|
||||
"@media": {
|
||||
"(min-width: 768px)": {
|
||||
width: "100%",
|
||||
maxWidth: "200px",
|
||||
},
|
||||
"(min-width: 1024px)": {
|
||||
maxWidth: "300px",
|
||||
width: "100%",
|
||||
},
|
||||
"(min-width: 1280px)": {
|
||||
width: "100%",
|
||||
maxWidth: "400px",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const listItem = style({
|
||||
display: "flex",
|
||||
cursor: "pointer",
|
||||
transition: "all ease 0.1s",
|
||||
color: vars.color.muted,
|
||||
width: "100%",
|
||||
overflow: "hidden",
|
||||
borderRadius: "4px",
|
||||
padding: `${SPACING_UNIT}px ${SPACING_UNIT}px`,
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
alignItems: "center",
|
||||
":hover": {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||
textDecoration: "none",
|
||||
},
|
||||
});
|
||||
|
||||
export const gamesGrid = style({
|
||||
listStyle: "none",
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
display: "grid",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
"@media": {
|
||||
"(min-width: 768px)": {
|
||||
gridTemplateColumns: "repeat(2, 1fr)",
|
||||
},
|
||||
"(min-width: 1250px)": {
|
||||
gridTemplateColumns: "repeat(3, 1fr)",
|
||||
},
|
||||
"(min-width: 1600px)": {
|
||||
gridTemplateColumns: "repeat(8, 1fr)",
|
||||
},
|
||||
},
|
||||
});
|
@ -1,19 +1,27 @@
|
||||
import { userProfileContext } from "@renderer/context";
|
||||
import { useContext, useEffect, useMemo } from "react";
|
||||
import { ProfileHero } from "./profile-hero/profile-hero";
|
||||
import { useCallback, useContext, useEffect, useMemo } from "react";
|
||||
import { ProfileHero } from "../profile-hero/profile-hero";
|
||||
import { useAppDispatch } from "@renderer/hooks";
|
||||
import { setHeaderTitle } from "@renderer/features";
|
||||
import { steamUrlBuilder } from "@shared";
|
||||
import { SPACING_UNIT } from "@renderer/theme.css";
|
||||
import { SPACING_UNIT, vars } from "@renderer/theme.css";
|
||||
|
||||
import * as styles from "./profile-content.css";
|
||||
import { ClockIcon, PeopleIcon } from "@primer/octicons-react";
|
||||
import { ClockIcon } from "@primer/octicons-react";
|
||||
import { Link } from "@renderer/components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UserGame } from "@types";
|
||||
import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants";
|
||||
import { buildGameDetailsPath } from "@renderer/helpers";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
export function ProfileContent() {
|
||||
const { userProfile } = useContext(userProfileContext);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { i18n, t } = useTranslation("user_profile");
|
||||
|
||||
useEffect(() => {
|
||||
if (userProfile) {
|
||||
dispatch(setHeaderTitle(userProfile.displayName));
|
||||
@ -21,8 +29,42 @@ export function ProfileContent() {
|
||||
}, [userProfile, dispatch]);
|
||||
|
||||
const truncatedGamesList = useMemo(() => {
|
||||
if (!userProfile) return [];
|
||||
return userProfile?.libraryGames.slice(0, 12);
|
||||
}, [userProfile?.libraryGames]);
|
||||
}, [userProfile]);
|
||||
|
||||
const numberFormatter = useMemo(() => {
|
||||
return new Intl.NumberFormat(i18n.language, {
|
||||
maximumFractionDigits: 0,
|
||||
});
|
||||
}, [i18n.language]);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const formatPlayTime = useCallback(
|
||||
(game: UserGame) => {
|
||||
const seconds = game?.playTimeInSeconds || 0;
|
||||
const minutes = seconds / 60;
|
||||
|
||||
if (minutes < MAX_MINUTES_TO_SHOW_IN_PLAYTIME) {
|
||||
return t("amount_minutes", {
|
||||
amount: minutes.toFixed(0),
|
||||
});
|
||||
}
|
||||
|
||||
const hours = minutes / 60;
|
||||
return t("amount_hours", { amount: numberFormatter.format(hours) });
|
||||
},
|
||||
[numberFormatter, t]
|
||||
);
|
||||
|
||||
const buildUserGameDetailsPath = (game: UserGame) =>
|
||||
buildGameDetailsPath({
|
||||
...game,
|
||||
objectID: game.objectId,
|
||||
});
|
||||
|
||||
if (!userProfile) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -35,23 +77,14 @@ export function ProfileContent() {
|
||||
padding: `${SPACING_UNIT * 3}px`,
|
||||
}}
|
||||
>
|
||||
<div style={{}}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className={styles.sectionHeader}>
|
||||
<h2>Library</h2>
|
||||
<h2>{t("library")}</h2>
|
||||
|
||||
<h3>{userProfile?.libraryGames.length}</h3>
|
||||
<h3>{numberFormatter.format(userProfile.libraryGames.length)}</h3>
|
||||
</div>
|
||||
|
||||
<ul
|
||||
style={{
|
||||
listStyle: "none",
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(6, 1fr)",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
}}
|
||||
>
|
||||
<ul className={styles.gamesGrid}>
|
||||
{truncatedGamesList.map((game) => (
|
||||
<li
|
||||
key={game.objectId}
|
||||
@ -64,13 +97,20 @@ export function ProfileContent() {
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
style={{ cursor: "pointer" }}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
className={styles.gameCover}
|
||||
onClick={() => navigate(buildUserGameDetailsPath(game))}
|
||||
>
|
||||
<img
|
||||
src={steamUrlBuilder.cover(game.objectId)}
|
||||
alt={game.title}
|
||||
style={{ width: "100%" }}
|
||||
style={{
|
||||
width: "100%",
|
||||
objectFit: "cover",
|
||||
borderRadius: 4,
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
</li>
|
||||
@ -78,14 +118,7 @@ export function ProfileContent() {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
minWidth: 350,
|
||||
display: "flex",
|
||||
gap: SPACING_UNIT * 2,
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<div className={styles.rightContent}>
|
||||
<div>
|
||||
<div className={styles.sectionHeader}>
|
||||
<h2>Played recently</h2>
|
||||
@ -94,14 +127,10 @@ export function ProfileContent() {
|
||||
<div className={styles.box}>
|
||||
<ul className={styles.list}>
|
||||
{userProfile?.recentGames.map((game) => (
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
}}
|
||||
<li key={`${game.shop}-${game.objectId}`}>
|
||||
<Link
|
||||
to={buildUserGameDetailsPath(game)}
|
||||
className={styles.listItem}
|
||||
>
|
||||
<img
|
||||
src={game.iconUrl}
|
||||
@ -113,21 +142,27 @@ export function ProfileContent() {
|
||||
}}
|
||||
/>
|
||||
|
||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: `${SPACING_UNIT / 2}px`,
|
||||
}}
|
||||
>
|
||||
<span style={{ fontWeight: "bold" }}>{game.title}</span>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: `${SPACING_UNIT / 2}px`,
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
}}
|
||||
>
|
||||
<ClockIcon />
|
||||
<span>{game.playTimeInSeconds}</span>
|
||||
<small>{formatPlayTime(game)}</small>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
@ -136,30 +171,31 @@ export function ProfileContent() {
|
||||
|
||||
<div>
|
||||
<div className={styles.sectionHeader}>
|
||||
<h2>Friends</h2>
|
||||
|
||||
<h2>{t("friends")}</h2>
|
||||
<span>{userProfile?.totalFriends}</span>
|
||||
</div>
|
||||
|
||||
<div className={styles.box}>
|
||||
<ul className={styles.list}>
|
||||
{userProfile?.friends.map((friend) => (
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
style={{ cursor: "pointer" }}
|
||||
className={styles.friend}
|
||||
<li key={friend.id}>
|
||||
<Link
|
||||
to={`/profile/${friend.id}`}
|
||||
className={styles.listItem}
|
||||
>
|
||||
<img
|
||||
src={friend.profileImageUrl}
|
||||
alt={friend.displayName}
|
||||
style={{ width: "100%" }}
|
||||
className={styles.friendAvatar}
|
||||
style={{
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
/>
|
||||
<span className={styles.friendName}>
|
||||
{friend.displayName}
|
||||
</span>
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
@ -6,11 +6,11 @@ export const profileContentBox = style({
|
||||
flexDirection: "column",
|
||||
});
|
||||
|
||||
export const profileAvatarContainer = style({
|
||||
export const profileAvatarButton = style({
|
||||
width: "96px",
|
||||
minWidth: "96px",
|
||||
height: "96px",
|
||||
borderRadius: "50%",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
@ -18,7 +18,11 @@ export const profileAvatarContainer = style({
|
||||
overflow: "hidden",
|
||||
border: `solid 1px ${vars.color.border}`,
|
||||
boxShadow: "0px 0px 5px 0px rgba(0, 0, 0, 0.7)",
|
||||
zIndex: 1,
|
||||
cursor: "pointer",
|
||||
transition: "all ease 0.3s",
|
||||
":hover": {
|
||||
boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.7)",
|
||||
},
|
||||
});
|
||||
|
||||
export const profileAvatar = style({
|
||||
@ -44,3 +48,24 @@ export const profileDisplayName = style({
|
||||
textOverflow: "ellipsis",
|
||||
width: "100%",
|
||||
});
|
||||
|
||||
export const heroPanel = style({
|
||||
width: "100%",
|
||||
height: "72px",
|
||||
minHeight: "72px",
|
||||
padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 3}px`,
|
||||
display: "flex",
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
justifyContent: "space-between",
|
||||
backdropFilter: `blur(10px)`,
|
||||
borderTop: `solid 1px rgba(255, 255, 255, 0.1)`,
|
||||
boxShadow: "0px 0px 15px 0px rgba(0, 0, 0, 0.5)",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.3)",
|
||||
});
|
||||
|
||||
export const userInformation = style({
|
||||
display: "flex",
|
||||
padding: `${SPACING_UNIT * 4}px ${SPACING_UNIT * 3}px`,
|
||||
alignItems: "center",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
});
|
||||
|
@ -1,101 +1,164 @@
|
||||
import { SPACING_UNIT } from "@renderer/theme.css";
|
||||
|
||||
import * as styles from "./profile-hero.css";
|
||||
import { useContext, useMemo } from "react";
|
||||
import { useContext, useMemo, useState } from "react";
|
||||
import { userProfileContext } from "@renderer/context";
|
||||
import {
|
||||
CheckCircleFillIcon,
|
||||
PencilIcon,
|
||||
PersonIcon,
|
||||
SignOutIcon,
|
||||
XCircleFillIcon,
|
||||
} from "@primer/octicons-react";
|
||||
import { buildGameDetailsPath } from "@renderer/helpers";
|
||||
import { Button, Link } from "@renderer/components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDate } from "@renderer/hooks";
|
||||
import { useDate, useToast, useUserDetails } from "@renderer/hooks";
|
||||
import { addSeconds } from "date-fns";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import type { FriendRequestAction } from "@types";
|
||||
import { UserProfileSettingsModal } from "../user-profile-settings-modal";
|
||||
|
||||
type FriendAction =
|
||||
| FriendRequestAction
|
||||
| ("BLOCK" | "UNDO_FRIENDSHIP" | "SEND");
|
||||
|
||||
export function ProfileHero() {
|
||||
const { userProfile, heroBackground, isMe } = useContext(userProfileContext);
|
||||
const [showEditProfileModal, setShowEditProfileModal] = useState(false);
|
||||
|
||||
const context = useContext(userProfileContext);
|
||||
const {
|
||||
signOut,
|
||||
updateFriendRequestState,
|
||||
sendFriendRequest,
|
||||
undoFriendship,
|
||||
blockUser,
|
||||
} = useUserDetails();
|
||||
|
||||
const { isMe, heroBackground, getUserProfile } = context;
|
||||
|
||||
const userProfile = context.userProfile!;
|
||||
const { currentGame } = userProfile;
|
||||
|
||||
const { t } = useTranslation("user_profile");
|
||||
const { formatDistance } = useDate();
|
||||
|
||||
if (!userProfile) return null;
|
||||
const { showSuccessToast, showErrorToast } = useToast();
|
||||
|
||||
const { currentGame } = userProfile;
|
||||
const navigate = useNavigate();
|
||||
|
||||
console.log(userProfile);
|
||||
const handleSignOut = async () => {
|
||||
await signOut();
|
||||
|
||||
showSuccessToast(t("successfully_signed_out"));
|
||||
navigate("/");
|
||||
};
|
||||
|
||||
const handleFriendAction = (userId: string, action: FriendAction) => {
|
||||
try {
|
||||
if (action === "UNDO_FRIENDSHIP") {
|
||||
undoFriendship(userId).then(getUserProfile);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "BLOCK") {
|
||||
blockUser(userId).then(() => {
|
||||
showSuccessToast(t("user_blocked_successfully"));
|
||||
navigate(-1);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "SEND") {
|
||||
sendFriendRequest(userProfile.id).then(getUserProfile);
|
||||
return;
|
||||
}
|
||||
|
||||
updateFriendRequestState(userId, action).then(getUserProfile);
|
||||
} catch (err) {
|
||||
showErrorToast(t("try_again"));
|
||||
}
|
||||
};
|
||||
|
||||
const profileActions = useMemo(() => {
|
||||
if (isMe) {
|
||||
return (
|
||||
<>
|
||||
<Button theme="outline">{t("settings")}</Button>
|
||||
<Button theme="outline" onClick={() => setShowEditProfileModal(true)}>
|
||||
<PencilIcon />
|
||||
{t("edit_profile")}
|
||||
</Button>
|
||||
|
||||
<Button theme="danger">{t("sign_out")}</Button>
|
||||
<Button theme="danger" onClick={handleSignOut}>
|
||||
<SignOutIcon />
|
||||
{t("sign_out")}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// if (userProfile.relation == null) {
|
||||
// return (
|
||||
// <>
|
||||
// <Button
|
||||
// theme="outline"
|
||||
// onClick={() => handleFriendAction(userProfile.id, "SEND")}
|
||||
// >
|
||||
// {t("add_friend")}
|
||||
// </Button>
|
||||
if (userProfile.relation == null) {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
theme="outline"
|
||||
onClick={() => handleFriendAction(userProfile.id, "SEND")}
|
||||
>
|
||||
{t("add_friend")}
|
||||
</Button>
|
||||
|
||||
// <Button theme="danger" onClick={() => setShowUserBlockModal(true)}>
|
||||
// {t("block_user")}
|
||||
// </Button>
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
<Button
|
||||
theme="danger"
|
||||
onClick={() => handleFriendAction(userProfile.id, "BLOCK")}
|
||||
>
|
||||
{t("block_user")}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// if (userProfile.relation.status === "ACCEPTED") {
|
||||
// return (
|
||||
// <>
|
||||
// <Button
|
||||
// theme="outline"
|
||||
// // className={styles.cancelRequestButton}
|
||||
// // onClick={() => setShowUndoFriendshipModal(true)}
|
||||
// >
|
||||
// <XCircleFillIcon size={28} /> {t("undo_friendship")}
|
||||
// </Button>
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
if (userProfile.relation.status === "ACCEPTED") {
|
||||
return (
|
||||
<Button
|
||||
theme="outline"
|
||||
onClick={() => handleFriendAction(userProfile.id, "UNDO_FRIENDSHIP")}
|
||||
>
|
||||
<XCircleFillIcon />
|
||||
{t("undo_friendship")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
// if (userProfile.relation.BId === userProfile.id) {
|
||||
// return (
|
||||
// <Button
|
||||
// theme="outline"
|
||||
// // className={styles.cancelRequestButton}
|
||||
// // onClick={() =>
|
||||
// // handleFriendAction(userProfile.relation!.BId, "CANCEL")
|
||||
// // }
|
||||
// >
|
||||
// <XCircleFillIcon size={28} /> {t("cancel_request")}
|
||||
// </Button>
|
||||
// );
|
||||
// }
|
||||
if (userProfile.relation.BId === userProfile.id) {
|
||||
return (
|
||||
<Button
|
||||
theme="outline"
|
||||
onClick={() =>
|
||||
handleFriendAction(userProfile.relation!.BId, "CANCEL")
|
||||
}
|
||||
>
|
||||
<XCircleFillIcon size={28} /> {t("cancel_request")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
theme="outline"
|
||||
// onClick={() =>
|
||||
// handleFriendAction(userProfile.relation!.AId, "ACCEPTED")
|
||||
// }
|
||||
onClick={() =>
|
||||
handleFriendAction(userProfile.relation!.AId, "ACCEPTED")
|
||||
}
|
||||
>
|
||||
<CheckCircleFillIcon size={28} /> {t("accept_request")}
|
||||
</Button>
|
||||
<Button
|
||||
theme="outline"
|
||||
// onClick={() =>
|
||||
// handleFriendAction(userProfile.relation!.AId, "REFUSED")
|
||||
// }
|
||||
onClick={() =>
|
||||
handleFriendAction(userProfile.relation!.AId, "REFUSED")
|
||||
}
|
||||
>
|
||||
<XCircleFillIcon size={28} /> {t("ignore_request")}
|
||||
</Button>
|
||||
@ -105,19 +168,27 @@ export function ProfileHero() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <ConfirmationModal
|
||||
visible
|
||||
title={t("sign_out_modal_title")}
|
||||
descriptionText={t("sign_out_modal_text")}
|
||||
confirmButtonLabel={t("sign_out")}
|
||||
cancelButtonLabel={t("cancel")}
|
||||
/> */}
|
||||
|
||||
<UserProfileSettingsModal
|
||||
visible={showEditProfileModal}
|
||||
userProfile={userProfile}
|
||||
updateUserProfile={getUserProfile}
|
||||
onClose={() => setShowEditProfileModal(false)}
|
||||
/>
|
||||
|
||||
<section
|
||||
className={styles.profileContentBox}
|
||||
style={{ background: heroBackground }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
padding: `${SPACING_UNIT * 4}px ${SPACING_UNIT * 3}px`,
|
||||
alignItems: "center",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
}}
|
||||
>
|
||||
<div className={styles.profileAvatarContainer}>
|
||||
<div className={styles.userInformation}>
|
||||
<button type="button" className={styles.profileAvatarButton}>
|
||||
{userProfile.profileImageUrl ? (
|
||||
<img
|
||||
className={styles.profileAvatar}
|
||||
@ -127,12 +198,13 @@ export function ProfileHero() {
|
||||
) : (
|
||||
<PersonIcon size={72} />
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className={styles.profileInformation}>
|
||||
<h2 className={styles.profileDisplayName}>
|
||||
{userProfile.displayName}
|
||||
</h2>
|
||||
|
||||
{currentGame && (
|
||||
<div
|
||||
style={{
|
||||
@ -149,14 +221,23 @@ export function ProfileHero() {
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Link to={buildGameDetailsPath(currentGame)}>
|
||||
<Link
|
||||
to={buildGameDetailsPath({
|
||||
...currentGame,
|
||||
objectID: currentGame.objectId,
|
||||
})}
|
||||
>
|
||||
{currentGame.title}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<small>
|
||||
{t("playing_for", {
|
||||
amount: formatDistance(
|
||||
currentGame.sessionDurationInSeconds,
|
||||
addSeconds(
|
||||
new Date(),
|
||||
-currentGame.sessionDurationInSeconds
|
||||
),
|
||||
new Date()
|
||||
),
|
||||
})}
|
||||
@ -166,20 +247,7 @@ export function ProfileHero() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "72px",
|
||||
minHeight: "72px",
|
||||
padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 3}px`,
|
||||
display: "flex",
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
justifyContent: "space-between",
|
||||
backdropFilter: `blur(10px)`,
|
||||
borderTop: `solid 1px rgba(255, 255, 255, 0.1)`,
|
||||
boxShadow: "0px 0px 15px 0px rgba(0, 0, 0, 0.5)",
|
||||
}}
|
||||
>
|
||||
<div className={styles.heroPanel}>
|
||||
<div></div>
|
||||
<div style={{ display: "flex", gap: `${SPACING_UNIT}px` }}>
|
||||
{profileActions}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { SPACING_UNIT, vars } from "../../theme.css";
|
||||
import { SPACING_UNIT } from "../../theme.css";
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
export const wrapper = style({
|
||||
@ -7,297 +7,3 @@ export const wrapper = style({
|
||||
flexDirection: "column",
|
||||
gap: `${SPACING_UNIT * 3}px`,
|
||||
});
|
||||
|
||||
export const profileContentBox = style({
|
||||
display: "flex",
|
||||
cursor: "pointer",
|
||||
gap: `${SPACING_UNIT * 3}px`,
|
||||
alignItems: "center",
|
||||
borderRadius: "4px",
|
||||
border: `solid 1px ${vars.color.border}`,
|
||||
width: "100%",
|
||||
boxShadow: "0px 0px 15px 0px rgba(0, 0, 0, 0.7)",
|
||||
transition: "all ease 0.3s",
|
||||
});
|
||||
|
||||
export const profileAvatarContainer = style({
|
||||
width: "96px",
|
||||
minWidth: "96px",
|
||||
height: "96px",
|
||||
borderRadius: "50%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: vars.color.background,
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
border: `solid 1px ${vars.color.border}`,
|
||||
boxShadow: "0px 0px 5px 0px rgba(0, 0, 0, 0.7)",
|
||||
zIndex: 1,
|
||||
});
|
||||
|
||||
export const friendAvatarContainer = style({
|
||||
width: "35px",
|
||||
minWidth: "35px",
|
||||
height: "35px",
|
||||
borderRadius: "50%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: vars.color.background,
|
||||
overflow: "hidden",
|
||||
border: `solid 1px ${vars.color.border}`,
|
||||
boxShadow: "0px 0px 5px 0px rgba(0, 0, 0, 0.7)",
|
||||
});
|
||||
|
||||
export const friendListDisplayName = style({
|
||||
fontWeight: "bold",
|
||||
fontSize: vars.size.body,
|
||||
textAlign: "left",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
});
|
||||
|
||||
export const profileAvatarEditContainer = style({
|
||||
alignSelf: "center",
|
||||
width: "128px",
|
||||
height: "128px",
|
||||
display: "flex",
|
||||
borderRadius: "50%",
|
||||
color: vars.color.body,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: vars.color.background,
|
||||
position: "relative",
|
||||
border: `solid 1px ${vars.color.border}`,
|
||||
boxShadow: "0px 0px 5px 0px rgba(0, 0, 0, 0.7)",
|
||||
cursor: "pointer",
|
||||
});
|
||||
|
||||
export const profileAvatar = style({
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
objectFit: "cover",
|
||||
borderRadius: "50%",
|
||||
overflow: "hidden",
|
||||
});
|
||||
|
||||
export const profileAvatarEditOverlay = style({
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
backgroundColor: "#00000055",
|
||||
color: vars.color.muted,
|
||||
zIndex: 1,
|
||||
cursor: "pointer",
|
||||
});
|
||||
|
||||
export const profileInformation = style({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
alignItems: "flex-start",
|
||||
color: "#c0c1c7",
|
||||
zIndex: 1,
|
||||
overflow: "hidden",
|
||||
});
|
||||
|
||||
export const profileDisplayName = style({
|
||||
fontWeight: "bold",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
width: "100%",
|
||||
});
|
||||
|
||||
export const profileContent = style({
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
flexDirection: "row",
|
||||
gap: `${SPACING_UNIT * 4}px`,
|
||||
});
|
||||
|
||||
export const profileGameSection = style({
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
});
|
||||
|
||||
export const friendsSection = style({
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
});
|
||||
|
||||
export const friendsSectionHeader = style({
|
||||
fontSize: vars.size.body,
|
||||
color: vars.color.body,
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
":hover": {
|
||||
color: vars.color.muted,
|
||||
},
|
||||
});
|
||||
|
||||
export const contentSidebar = style({
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: `${SPACING_UNIT * 3}px`,
|
||||
"@media": {
|
||||
"(min-width: 768px)": {
|
||||
width: "100%",
|
||||
maxWidth: "150px",
|
||||
},
|
||||
"(min-width: 1024px)": {
|
||||
maxWidth: "250px",
|
||||
width: "100%",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const feedGameIcon = style({
|
||||
height: "100%",
|
||||
});
|
||||
|
||||
export const libraryGameIcon = style({
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
borderRadius: "4px",
|
||||
});
|
||||
|
||||
export const friendProfileIcon = style({
|
||||
height: "100%",
|
||||
});
|
||||
|
||||
export const feedItem = style({
|
||||
color: vars.color.body,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
width: "100%",
|
||||
overflow: "hidden",
|
||||
height: "72px",
|
||||
transition: "all ease 0.2s",
|
||||
cursor: "pointer",
|
||||
zIndex: "1",
|
||||
":hover": {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||
},
|
||||
});
|
||||
|
||||
export const gameListItem = style({
|
||||
color: vars.color.body,
|
||||
transition: "all ease 0.2s",
|
||||
cursor: "pointer",
|
||||
zIndex: "1",
|
||||
overflow: "hidden",
|
||||
padding: `${SPACING_UNIT + SPACING_UNIT / 2}px`,
|
||||
":hover": {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||
},
|
||||
});
|
||||
|
||||
export const friendListContainer = style({
|
||||
color: vars.color.body,
|
||||
width: "100%",
|
||||
height: "54px",
|
||||
padding: `0 ${SPACING_UNIT}px`,
|
||||
gap: `${SPACING_UNIT + SPACING_UNIT / 2}px`,
|
||||
transition: "all ease 0.2s",
|
||||
position: "relative",
|
||||
":hover": {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||
},
|
||||
});
|
||||
|
||||
export const gameInformation = style({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
gap: `${SPACING_UNIT / 2}px`,
|
||||
});
|
||||
|
||||
export const profileHeaderSkeleton = style({
|
||||
height: "144px",
|
||||
});
|
||||
|
||||
export const editProfileImageBadge = style({
|
||||
width: "28px",
|
||||
height: "28px",
|
||||
borderRadius: "50%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
color: vars.color.background,
|
||||
backgroundColor: vars.color.muted,
|
||||
position: "absolute",
|
||||
bottom: "0px",
|
||||
right: "0px",
|
||||
zIndex: "1",
|
||||
});
|
||||
|
||||
export const telescopeIcon = style({
|
||||
width: "60px",
|
||||
height: "60px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.06)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginBottom: `${SPACING_UNIT * 2}px`,
|
||||
});
|
||||
|
||||
export const noDownloads = style({
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
});
|
||||
|
||||
export const signOutModalContent = style({
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
flexDirection: "column",
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
});
|
||||
|
||||
export const signOutModalButtonsContainer = style({
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
justifyContent: "end",
|
||||
alignItems: "center",
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
paddingTop: `${SPACING_UNIT}px`,
|
||||
});
|
||||
|
||||
export const profileBackground = style({
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
position: "absolute",
|
||||
objectFit: "cover",
|
||||
left: "0",
|
||||
top: "0",
|
||||
borderRadius: "4px",
|
||||
});
|
||||
|
||||
export const cancelRequestButton = style({
|
||||
cursor: "pointer",
|
||||
color: vars.color.body,
|
||||
":hover": {
|
||||
color: vars.color.danger,
|
||||
},
|
||||
});
|
||||
|
||||
export const acceptRequestButton = style({
|
||||
cursor: "pointer",
|
||||
color: vars.color.success,
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ProfileSkeleton } from "./profile-skeleton";
|
||||
import { ProfileContent } from "./profile-content";
|
||||
import { ProfileContent } from "./profile-content/profile-content";
|
||||
import { SkeletonTheme } from "react-loading-skeleton";
|
||||
import { vars } from "@renderer/theme.css";
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
import { Button, Modal } from "@renderer/components";
|
||||
import * as styles from "./profile.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export interface UserBlockModalProps {
|
||||
visible: boolean;
|
||||
displayName: string;
|
||||
onConfirm: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const UserBlockModal = ({
|
||||
visible,
|
||||
displayName,
|
||||
onConfirm,
|
||||
onClose,
|
||||
}: UserBlockModalProps) => {
|
||||
const { t } = useTranslation("user_profile");
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
visible={visible}
|
||||
title={t("sign_out_modal_title")}
|
||||
onClose={onClose}
|
||||
>
|
||||
<div className={styles.signOutModalContent}>
|
||||
<p>{t("user_block_modal_text", { displayName })}</p>
|
||||
<div className={styles.signOutModalButtonsContainer}>
|
||||
<Button onClick={onConfirm} theme="danger">
|
||||
{t("block_user")}
|
||||
</Button>
|
||||
|
||||
<Button onClick={onClose} theme="primary">
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,40 +0,0 @@
|
||||
import { Button, Modal } from "@renderer/components";
|
||||
import * as styles from "./profile.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export interface UserConfirmUndoFriendshipModalProps {
|
||||
visible: boolean;
|
||||
displayName: string;
|
||||
onConfirm: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function UserConfirmUndoFriendshipModal({
|
||||
visible,
|
||||
displayName,
|
||||
onConfirm,
|
||||
onClose,
|
||||
}: UserConfirmUndoFriendshipModalProps) {
|
||||
const { t } = useTranslation("user_profile");
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
title={t("sign_out_modal_title")}
|
||||
onClose={onClose}
|
||||
>
|
||||
<div className={styles.signOutModalContent}>
|
||||
<p>{t("undo_friendship_modal_text", { displayName })}</p>
|
||||
<div className={styles.signOutModalButtonsContainer}>
|
||||
<Button onClick={onConfirm} theme="danger">
|
||||
{t("undo_friendship")}
|
||||
</Button>
|
||||
|
||||
<Button onClick={onClose} theme="primary">
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -47,7 +47,7 @@ export const UserEditProfile = ({
|
||||
filters: [
|
||||
{
|
||||
name: "Image",
|
||||
extensions: ["jpg", "jpeg", "png", "webp"],
|
||||
extensions: ["jpg", "jpeg", "png"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
@ -44,7 +44,12 @@ export const UserProfileSettingsModal = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal visible={visible} title={t("settings")} onClose={onClose}>
|
||||
<Modal
|
||||
visible={visible}
|
||||
title={t("settings")}
|
||||
onClose={onClose}
|
||||
clickOutsideToClose={false}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
|
@ -1,40 +0,0 @@
|
||||
import { Button, Modal } from "@renderer/components";
|
||||
import * as styles from "./profile.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export interface UserSignOutModalProps {
|
||||
visible: boolean;
|
||||
onConfirm: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const UserSignOutModal = ({
|
||||
visible,
|
||||
onConfirm,
|
||||
onClose,
|
||||
}: UserSignOutModalProps) => {
|
||||
const { t } = useTranslation("user_profile");
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
visible={visible}
|
||||
title={t("sign_out_modal_title")}
|
||||
onClose={onClose}
|
||||
>
|
||||
<div className={styles.signOutModalContent}>
|
||||
<p>{t("sign_out_modal_text")}</p>
|
||||
<div className={styles.signOutModalButtonsContainer}>
|
||||
<Button onClick={onConfirm} theme="danger">
|
||||
{t("sign_out")}
|
||||
</Button>
|
||||
|
||||
<Button onClick={onClose} theme="primary">
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,6 +1,4 @@
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import ISO6391 from "iso-639-1";
|
||||
|
||||
import {
|
||||
TextField,
|
||||
Button,
|
||||
@ -8,11 +6,9 @@ import {
|
||||
SelectField,
|
||||
} from "@renderer/components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useAppSelector } from "@renderer/hooks";
|
||||
|
||||
import { changeLanguage } from "i18next";
|
||||
import * as languageResources from "@locales";
|
||||
import languageResources from "@locales";
|
||||
import { orderBy } from "lodash-es";
|
||||
import { settingsContext } from "@renderer/context";
|
||||
|
||||
@ -50,9 +46,9 @@ export function SettingsGeneral() {
|
||||
|
||||
setLanguageOptions(
|
||||
orderBy(
|
||||
Object.keys(languageResources).map((language) => {
|
||||
Object.entries(languageResources).map(([language, value]) => {
|
||||
return {
|
||||
nativeName: ISO6391.getNativeName(language),
|
||||
nativeName: value.language_name,
|
||||
option: language,
|
||||
};
|
||||
}),
|
||||
@ -93,8 +89,6 @@ export function SettingsGeneral() {
|
||||
|
||||
function updateFormWithUserPreferences() {
|
||||
if (userPreferences) {
|
||||
const parsedLanguage = userPreferences.language.split("-")[0];
|
||||
|
||||
setForm((prev) => ({
|
||||
...prev,
|
||||
downloadsPath: userPreferences.downloadsPath ?? defaultDownloadsPath,
|
||||
@ -102,7 +96,7 @@ export function SettingsGeneral() {
|
||||
userPreferences.downloadNotificationsEnabled,
|
||||
repackUpdatesNotificationsEnabled:
|
||||
userPreferences.repackUpdatesNotificationsEnabled,
|
||||
language: parsedLanguage,
|
||||
language: userPreferences.language,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -1,106 +1,27 @@
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
import { SPACING_UNIT, vars } from "../../theme.css";
|
||||
import { recipe } from "@vanilla-extract/recipes";
|
||||
|
||||
export const container = style({
|
||||
padding: "24px",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
alignItems: "flex-start",
|
||||
});
|
||||
|
||||
export const content = style({
|
||||
backgroundColor: vars.color.background,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
padding: `${SPACING_UNIT * 3}px`,
|
||||
border: `solid 1px ${vars.color.border}`,
|
||||
boxShadow: "0px 0px 15px 0px rgba(0, 0, 0, 0.5)",
|
||||
borderRadius: "4px",
|
||||
boxShadow: "0px 0px 15px 0px #000000",
|
||||
borderRadius: "8px",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flex: "1",
|
||||
});
|
||||
|
||||
export const sidebar = style({
|
||||
width: "200px",
|
||||
export const settingsCategories = style({
|
||||
display: "flex",
|
||||
border: `solid 1px ${vars.color.border}`,
|
||||
borderRadius: "4px",
|
||||
backgroundColor: vars.color.background,
|
||||
minHeight: "500px",
|
||||
flexDirection: "column",
|
||||
padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT}px`,
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
boxShadow: "0px 0px 15px 0px rgba(0, 0, 0, 0.5)",
|
||||
});
|
||||
|
||||
export const menuGroup = style({
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
});
|
||||
|
||||
export const menu = style({
|
||||
listStyle: "none",
|
||||
margin: "0",
|
||||
padding: "0",
|
||||
gap: `${SPACING_UNIT / 2}px`,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
});
|
||||
|
||||
export const menuItem = recipe({
|
||||
base: {
|
||||
transition: "all ease 0.1s",
|
||||
cursor: "pointer",
|
||||
textWrap: "nowrap",
|
||||
display: "flex",
|
||||
color: vars.color.muted,
|
||||
borderRadius: "4px",
|
||||
":hover": {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||
},
|
||||
},
|
||||
variants: {
|
||||
active: {
|
||||
true: {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
||||
},
|
||||
},
|
||||
muted: {
|
||||
true: {
|
||||
opacity: vars.opacity.disabled,
|
||||
":hover": {
|
||||
opacity: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const menuItemButton = style({
|
||||
color: "inherit",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
cursor: "pointer",
|
||||
overflow: "hidden",
|
||||
width: "100%",
|
||||
padding: `9px ${SPACING_UNIT}px`,
|
||||
});
|
||||
|
||||
export const menuItemButtonLabel = style({
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
});
|
||||
|
||||
export const categoryTitle = style({
|
||||
color: "#ff",
|
||||
fontWeight: "bold",
|
||||
fontSize: "18px",
|
||||
paddingBottom: `${SPACING_UNIT}px`,
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Button } from "@renderer/components";
|
||||
|
||||
import * as styles from "./settings.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SettingsRealDebrid } from "./settings-real-debrid";
|
||||
@ -13,10 +15,12 @@ import {
|
||||
export function Settings() {
|
||||
const { t } = useTranslation("settings");
|
||||
|
||||
const categories = {
|
||||
[t("account")]: [t("my_profile"), t("friends")],
|
||||
Hydra: [t("general"), t("behavior"), t("download_sources"), "Real-Debrid"],
|
||||
};
|
||||
const categories = [
|
||||
t("general"),
|
||||
t("behavior"),
|
||||
t("download_sources"),
|
||||
"Real-Debrid",
|
||||
];
|
||||
|
||||
return (
|
||||
<SettingsContextProvider>
|
||||
@ -40,34 +44,21 @@ export function Settings() {
|
||||
|
||||
return (
|
||||
<section className={styles.container}>
|
||||
<aside className={styles.sidebar}>
|
||||
{Object.entries(categories).map(([category, items]) => (
|
||||
<div key={category} className={styles.menuGroup}>
|
||||
<span className={styles.categoryTitle}>{category}</span>
|
||||
|
||||
<ul className={styles.menu}>
|
||||
{items.map((item, index) => (
|
||||
<li
|
||||
key={`item-${index}`}
|
||||
className={styles.menuItem({
|
||||
active: currentCategoryIndex === index,
|
||||
})}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.menuItemButton}
|
||||
onClick={() => setCurrentCategoryIndex(index)}
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</aside>
|
||||
|
||||
<div className={styles.content}>
|
||||
<section className={styles.settingsCategories}>
|
||||
{categories.map((category, index) => (
|
||||
<Button
|
||||
key={category}
|
||||
theme={
|
||||
currentCategoryIndex === index ? "primary" : "outline"
|
||||
}
|
||||
onClick={() => setCurrentCategoryIndex(index)}
|
||||
>
|
||||
{category}
|
||||
</Button>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<h2>{categories[currentCategoryIndex]}</h2>
|
||||
{renderCategory()}
|
||||
</div>
|
||||
|
@ -11,6 +11,11 @@ export enum DownloadSourceStatus {
|
||||
Errored,
|
||||
}
|
||||
|
||||
export enum CatalogueCategory {
|
||||
Hot = "hot",
|
||||
Weekly = "weekly",
|
||||
}
|
||||
|
||||
export class UserNotLoggedInError extends Error {
|
||||
constructor() {
|
||||
super("user not logged in");
|
||||
|
@ -309,7 +309,8 @@ export interface UserRelation {
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface UserProfileCurrentGame extends GameRunning {
|
||||
export interface UserProfileCurrentGame extends Omit<GameRunning, "objectID"> {
|
||||
objectId: string;
|
||||
sessionDurationInSeconds: number;
|
||||
}
|
||||
|
||||
@ -345,3 +346,8 @@ export interface DownloadSource {
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface GameStats {
|
||||
downloadCount: number;
|
||||
playerCount: number;
|
||||
}
|
||||
|
@ -5242,11 +5242,6 @@ isexe@^2.0.0:
|
||||
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
|
||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||
|
||||
iso-639-1@3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.npmjs.org/iso-639-1/-/iso-639-1-3.1.2.tgz"
|
||||
integrity sha512-Le7BRl3Jt9URvaiEHJCDEdvPZCfhiQoXnFgLAWNRhzFMwRFdWO7/5tLRQbiPzE394I9xd7KdRCM7S6qdOhwG5A==
|
||||
|
||||
iterator.prototype@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user