From 6c6fff71fe145cf68e0817d7c2b8ec2364a611e4 Mon Sep 17 00:00:00 2001
From: Chubby Granny Chaser
Date: Tue, 9 Jul 2024 19:24:02 +0100
Subject: [PATCH 001/108] feat: adding generic http downloads
---
.../events/torrenting/start-game-download.ts | 4 +
src/main/main.ts | 3 +-
.../services/download/download-manager.ts | 56 ++++++---
.../download/generic-http-downloader.ts | 109 ++++++++++++++++++
src/main/services/download/http-download.ts | 10 +-
.../download/real-debrid-downloader.ts | 30 +++--
src/main/services/hosters/gofile.ts | 61 ++++++++++
src/main/services/hosters/index.ts | 1 +
src/renderer/src/constants.ts | 2 +
.../modals/download-settings-modal.tsx | 36 ++++--
.../game-details/modals/repacks-modal.tsx | 6 +-
src/shared/index.ts | 17 +++
12 files changed, 294 insertions(+), 41 deletions(-)
create mode 100644 src/main/services/download/generic-http-downloader.ts
create mode 100644 src/main/services/hosters/gofile.ts
create mode 100644 src/main/services/hosters/index.ts
diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts
index cea41596..aa33c99a 100644
--- a/src/main/events/torrenting/start-game-download.ts
+++ b/src/main/events/torrenting/start-game-download.ts
@@ -44,6 +44,8 @@ const startGameDownload = async (
);
if (game) {
+ console.log("game", game);
+
await gameRepository.update(
{
id: game.id,
@@ -95,6 +97,8 @@ const startGameDownload = async (
},
});
+ console.log(updatedGame);
+
createGame(updatedGame!);
await downloadQueueRepository.delete({ game: { id: updatedGame!.id } });
diff --git a/src/main/main.ts b/src/main/main.ts
index fbabc56c..af594e20 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -22,8 +22,9 @@ const loadState = async (userPreferences: UserPreferences | null) => {
import("./events");
- if (userPreferences?.realDebridApiToken)
+ if (userPreferences?.realDebridApiToken) {
RealDebridClient.authorize(userPreferences?.realDebridApiToken);
+ }
HydraApi.setupApi().then(() => {
uploadGamesBatch();
diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts
index 31f28992..d6542396 100644
--- a/src/main/services/download/download-manager.ts
+++ b/src/main/services/download/download-manager.ts
@@ -6,6 +6,8 @@ import { downloadQueueRepository, gameRepository } from "@main/repository";
import { publishDownloadCompleteNotification } from "../notifications";
import { RealDebridDownloader } from "./real-debrid-downloader";
import type { DownloadProgress } from "@types";
+import { GofileApi } from "../hosters";
+import { GenericHTTPDownloader } from "./generic-http-downloader";
export class DownloadManager {
private static currentDownloader: Downloader | null = null;
@@ -13,10 +15,12 @@ export class DownloadManager {
public static async watchDownloads() {
let status: DownloadProgress | null = null;
- if (this.currentDownloader === Downloader.RealDebrid) {
+ if (this.currentDownloader === Downloader.Torrent) {
+ status = await PythonInstance.getStatus();
+ } else if (this.currentDownloader === Downloader.RealDebrid) {
status = await RealDebridDownloader.getStatus();
} else {
- status = await PythonInstance.getStatus();
+ status = await GenericHTTPDownloader.getStatus();
}
if (status) {
@@ -62,10 +66,12 @@ export class DownloadManager {
}
static async pauseDownload() {
- if (this.currentDownloader === Downloader.RealDebrid) {
+ if (this.currentDownloader === Downloader.Torrent) {
+ await PythonInstance.pauseDownload();
+ } else if (this.currentDownloader === Downloader.RealDebrid) {
await RealDebridDownloader.pauseDownload();
} else {
- await PythonInstance.pauseDownload();
+ await GenericHTTPDownloader.pauseDownload();
}
WindowManager.mainWindow?.setProgressBar(-1);
@@ -73,20 +79,16 @@ export class DownloadManager {
}
static async resumeDownload(game: Game) {
- if (game.downloader === Downloader.RealDebrid) {
- RealDebridDownloader.startDownload(game);
- this.currentDownloader = Downloader.RealDebrid;
- } else {
- PythonInstance.startDownload(game);
- this.currentDownloader = Downloader.Torrent;
- }
+ return this.startDownload(game);
}
static async cancelDownload(gameId: number) {
- if (this.currentDownloader === Downloader.RealDebrid) {
+ if (this.currentDownloader === Downloader.Torrent) {
+ PythonInstance.cancelDownload(gameId);
+ } else if (this.currentDownloader === Downloader.RealDebrid) {
RealDebridDownloader.cancelDownload(gameId);
} else {
- PythonInstance.cancelDownload(gameId);
+ GenericHTTPDownloader.cancelDownload(gameId);
}
WindowManager.mainWindow?.setProgressBar(-1);
@@ -94,12 +96,30 @@ export class DownloadManager {
}
static async startDownload(game: Game) {
- if (game.downloader === Downloader.RealDebrid) {
- RealDebridDownloader.startDownload(game);
- this.currentDownloader = Downloader.RealDebrid;
- } else {
+ if (game.downloader === Downloader.Gofile) {
+ const id = game!.uri!.split("/").pop();
+
+ const token = await GofileApi.authorize();
+ const downloadLink = await GofileApi.getDownloadLink(id!);
+
+ console.log(downloadLink, token, "<<<");
+
+ GenericHTTPDownloader.startDownload(game, downloadLink, [
+ `Cookie: accountToken=${token}`,
+ ]);
+ } else if (game.downloader === Downloader.PixelDrain) {
+ const id = game!.uri!.split("/").pop();
+
+ await GenericHTTPDownloader.startDownload(
+ game,
+ `https://pixeldrain.com/api/file/${id}?download`
+ );
+ } else if (game.downloader === Downloader.Torrent) {
PythonInstance.startDownload(game);
- this.currentDownloader = Downloader.Torrent;
+ } else if (game.downloader === Downloader.RealDebrid) {
+ RealDebridDownloader.startDownload(game);
}
+
+ this.currentDownloader = game.downloader;
}
}
diff --git a/src/main/services/download/generic-http-downloader.ts b/src/main/services/download/generic-http-downloader.ts
new file mode 100644
index 00000000..688769a4
--- /dev/null
+++ b/src/main/services/download/generic-http-downloader.ts
@@ -0,0 +1,109 @@
+import { Game } from "@main/entity";
+import { gameRepository } from "@main/repository";
+import { calculateETA } from "./helpers";
+import { DownloadProgress } from "@types";
+import { HTTPDownload } from "./http-download";
+
+export class GenericHTTPDownloader {
+ private static downloads = new Map();
+ private static downloadingGame: Game | null = null;
+
+ public static async getStatus() {
+ if (this.downloadingGame) {
+ const gid = this.downloads.get(this.downloadingGame.id)!;
+ const status = await HTTPDownload.getStatus(gid);
+
+ if (status) {
+ const progress =
+ Number(status.completedLength) / Number(status.totalLength);
+
+ await gameRepository.update(
+ { id: this.downloadingGame!.id },
+ {
+ bytesDownloaded: Number(status.completedLength),
+ fileSize: Number(status.totalLength),
+ progress,
+ status: "active",
+ }
+ );
+
+ const result = {
+ numPeers: 0,
+ numSeeds: 0,
+ downloadSpeed: Number(status.downloadSpeed),
+ timeRemaining: calculateETA(
+ Number(status.totalLength),
+ Number(status.completedLength),
+ Number(status.downloadSpeed)
+ ),
+ isDownloadingMetadata: false,
+ isCheckingFiles: false,
+ progress,
+ gameId: this.downloadingGame!.id,
+ } as DownloadProgress;
+
+ if (progress === 1) {
+ this.downloads.delete(this.downloadingGame.id);
+ this.downloadingGame = null;
+ }
+
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ static async pauseDownload() {
+ if (this.downloadingGame) {
+ const gid = this.downloads.get(this.downloadingGame!.id!);
+
+ if (gid) {
+ await HTTPDownload.pauseDownload(gid);
+ }
+
+ this.downloadingGame = null;
+ }
+ }
+
+ static async startDownload(
+ game: Game,
+ downloadUrl: string,
+ headers: string[] = []
+ ) {
+ this.downloadingGame = game;
+
+ if (this.downloads.has(game.id)) {
+ await this.resumeDownload(game.id!);
+
+ return;
+ }
+
+ if (downloadUrl) {
+ const gid = await HTTPDownload.startDownload(
+ game.downloadPath!,
+ downloadUrl,
+ headers
+ );
+
+ this.downloads.set(game.id!, gid);
+ }
+ }
+
+ static async cancelDownload(gameId: number) {
+ const gid = this.downloads.get(gameId);
+
+ if (gid) {
+ await HTTPDownload.cancelDownload(gid);
+ this.downloads.delete(gameId);
+ }
+ }
+
+ static async resumeDownload(gameId: number) {
+ const gid = this.downloads.get(gameId);
+
+ if (gid) {
+ await HTTPDownload.resumeDownload(gid);
+ }
+ }
+}
diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts
index 4553a6cb..d147e208 100644
--- a/src/main/services/download/http-download.ts
+++ b/src/main/services/download/http-download.ts
@@ -4,7 +4,7 @@ import { sleep } from "@main/helpers";
import { startAria2 } from "../aria2c";
import Aria2 from "aria2";
-export class HttpDownload {
+export class HTTPDownload {
private static connected = false;
private static aria2c: ChildProcess | null = null;
@@ -56,11 +56,17 @@ export class HttpDownload {
await this.aria2.call("unpause", gid);
}
- static async startDownload(downloadPath: string, downloadUrl: string) {
+ static async startDownload(
+ downloadPath: string,
+ downloadUrl: string,
+ header: string[] = []
+ ) {
+ console.log(header);
if (!this.connected) await this.connect();
const options = {
dir: downloadPath,
+ header,
};
return this.aria2.call("addUri", [downloadUrl], options);
diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts
index 8ead0067..034ffc49 100644
--- a/src/main/services/download/real-debrid-downloader.ts
+++ b/src/main/services/download/real-debrid-downloader.ts
@@ -3,7 +3,7 @@ import { RealDebridClient } from "../real-debrid";
import { gameRepository } from "@main/repository";
import { calculateETA } from "./helpers";
import { DownloadProgress } from "@types";
-import { HttpDownload } from "./http-download";
+import { HTTPDownload } from "./http-download";
export class RealDebridDownloader {
private static downloads = new Map();
@@ -29,6 +29,18 @@ export class RealDebridDownloader {
const { download } = await RealDebridClient.unrestrictLink(link);
return decodeURIComponent(download);
}
+
+ return null;
+ }
+
+ if (this.downloadingGame?.uri) {
+ const { download } = await RealDebridClient.unrestrictLink(
+ this.downloadingGame?.uri
+ );
+
+ console.log("download>>", download);
+
+ return decodeURIComponent(download);
}
return null;
@@ -37,7 +49,7 @@ export class RealDebridDownloader {
public static async getStatus() {
if (this.downloadingGame) {
const gid = this.downloads.get(this.downloadingGame.id)!;
- const status = await HttpDownload.getStatus(gid);
+ const status = await HTTPDownload.getStatus(gid);
if (status) {
const progress =
@@ -111,7 +123,7 @@ export class RealDebridDownloader {
static async pauseDownload() {
const gid = this.downloads.get(this.downloadingGame!.id!);
if (gid) {
- await HttpDownload.pauseDownload(gid);
+ await HTTPDownload.pauseDownload(gid);
}
this.realDebridTorrentId = null;
@@ -127,14 +139,18 @@ export class RealDebridDownloader {
return;
}
- this.realDebridTorrentId = await RealDebridClient.getTorrentId(game!.uri!);
+ if (game.uri?.startsWith("magnet:")) {
+ this.realDebridTorrentId = await RealDebridClient.getTorrentId(
+ game!.uri!
+ );
+ }
const downloadUrl = await this.getRealDebridDownloadUrl();
if (downloadUrl) {
this.realDebridTorrentId = null;
- const gid = await HttpDownload.startDownload(
+ const gid = await HTTPDownload.startDownload(
game.downloadPath!,
downloadUrl
);
@@ -147,7 +163,7 @@ export class RealDebridDownloader {
const gid = this.downloads.get(gameId);
if (gid) {
- await HttpDownload.cancelDownload(gid);
+ await HTTPDownload.cancelDownload(gid);
this.downloads.delete(gameId);
}
}
@@ -156,7 +172,7 @@ export class RealDebridDownloader {
const gid = this.downloads.get(gameId);
if (gid) {
- await HttpDownload.resumeDownload(gid);
+ await HTTPDownload.resumeDownload(gid);
}
}
}
diff --git a/src/main/services/hosters/gofile.ts b/src/main/services/hosters/gofile.ts
new file mode 100644
index 00000000..770bb15f
--- /dev/null
+++ b/src/main/services/hosters/gofile.ts
@@ -0,0 +1,61 @@
+import axios from "axios";
+
+export interface GofileAccountsReponse {
+ id: string;
+ token: string;
+}
+
+export interface GofileContentChild {
+ id: string;
+ link: string;
+}
+
+export interface GofileContentsResponse {
+ id: string;
+ type: string;
+ children: Record;
+}
+
+export class GofileApi {
+ private static token: string;
+
+ public static async authorize() {
+ const response = await axios.post<{
+ status: string;
+ data: GofileAccountsReponse;
+ }>("https://api.gofile.io/accounts");
+
+ if (response.data.status === "ok") {
+ this.token = response.data.data.token;
+ return this.token;
+ }
+
+ throw new Error("Failed to authorize");
+ }
+
+ public static async getDownloadLink(id: string) {
+ const searchParams = new URLSearchParams({
+ wt: "4fd6sg89d7s6",
+ });
+
+ const response = await axios.get<{
+ status: string;
+ data: GofileContentsResponse;
+ }>(`https://api.gofile.io/contents/${id}?${searchParams.toString()}`, {
+ headers: {
+ Authorization: `Bearer ${this.token}`,
+ },
+ });
+
+ if (response.data.status === "ok") {
+ if (response.data.data.type !== "folder") {
+ throw new Error("Only folders are supported");
+ }
+
+ const [firstChild] = Object.values(response.data.data.children);
+ return firstChild.link;
+ }
+
+ throw new Error("Failed to get download link");
+ }
+}
diff --git a/src/main/services/hosters/index.ts b/src/main/services/hosters/index.ts
new file mode 100644
index 00000000..921c45b1
--- /dev/null
+++ b/src/main/services/hosters/index.ts
@@ -0,0 +1 @@
+export * from "./gofile";
diff --git a/src/renderer/src/constants.ts b/src/renderer/src/constants.ts
index 6186bb85..7025df2a 100644
--- a/src/renderer/src/constants.ts
+++ b/src/renderer/src/constants.ts
@@ -5,4 +5,6 @@ export const VERSION_CODENAME = "Leviticus";
export const DOWNLOADER_NAME = {
[Downloader.RealDebrid]: "Real-Debrid",
[Downloader.Torrent]: "Torrent",
+ [Downloader.Gofile]: "Gofile",
+ [Downloader.PixelDrain]: "PixelDrain",
};
diff --git a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx
index ef4ba040..d102d2b2 100644
--- a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx
+++ b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx
@@ -1,11 +1,11 @@
-import { useEffect, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { DiskSpace } from "check-disk-space";
import * as styles from "./download-settings-modal.css";
import { Button, Link, Modal, TextField } from "@renderer/components";
import { CheckCircleFillIcon, DownloadIcon } from "@primer/octicons-react";
-import { Downloader, formatBytes } from "@shared";
+import { Downloader, formatBytes, getDownloadersForUri } from "@shared";
import type { GameRepack } from "@types";
import { SPACING_UNIT } from "@renderer/theme.css";
@@ -23,8 +23,6 @@ export interface DownloadSettingsModalProps {
repack: GameRepack | null;
}
-const downloaders = [Downloader.Torrent, Downloader.RealDebrid];
-
export function DownloadSettingsModal({
visible,
onClose,
@@ -36,9 +34,8 @@ export function DownloadSettingsModal({
const [diskFreeSpace, setDiskFreeSpace] = useState(null);
const [selectedPath, setSelectedPath] = useState("");
const [downloadStarting, setDownloadStarting] = useState(false);
- const [selectedDownloader, setSelectedDownloader] = useState(
- Downloader.Torrent
- );
+ const [selectedDownloader, setSelectedDownloader] =
+ useState(null);
const userPreferences = useAppSelector(
(state) => state.userPreferences.value
@@ -50,6 +47,10 @@ export function DownloadSettingsModal({
}
}, [visible, selectedPath]);
+ const downloaders = useMemo(() => {
+ return getDownloadersForUri(repack?.magnet ?? "");
+ }, [repack?.magnet]);
+
useEffect(() => {
if (userPreferences?.downloadsPath) {
setSelectedPath(userPreferences.downloadsPath);
@@ -59,9 +60,19 @@ export function DownloadSettingsModal({
.then((defaultDownloadsPath) => setSelectedPath(defaultDownloadsPath));
}
- if (userPreferences?.realDebridApiToken)
+ if (
+ userPreferences?.realDebridApiToken &&
+ downloaders.includes(Downloader.RealDebrid)
+ ) {
setSelectedDownloader(Downloader.RealDebrid);
- }, [userPreferences?.downloadsPath, userPreferences?.realDebridApiToken]);
+ } else {
+ setSelectedDownloader(downloaders[0]);
+ }
+ }, [
+ userPreferences?.downloadsPath,
+ downloaders,
+ userPreferences?.realDebridApiToken,
+ ]);
const getDiskFreeSpace = (path: string) => {
window.electron.getDiskFreeSpace(path).then((result) => {
@@ -85,7 +96,7 @@ export function DownloadSettingsModal({
if (repack) {
setDownloadStarting(true);
- startDownload(repack, selectedDownloader, selectedPath).finally(() => {
+ startDownload(repack, selectedDownloader!, selectedPath).finally(() => {
setDownloadStarting(false);
onClose();
});
@@ -167,7 +178,10 @@ export function DownloadSettingsModal({
-
- {userDetails && friendRequests.length > 0 && !gameRunning && (
+ {userDetails && receivedRequests.length > 0 && !gameRunning && (
- {friendRequests.length}
+ {receivedRequests.length}
)}
From 102299e42fad776f55d639cde932be5bd4843b04 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Thu, 25 Jul 2024 19:12:34 -0300
Subject: [PATCH 018/108] feat: remove tab title
---
.../pages/shared-modals/user-friend-modal/user-friend-modal.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx
index 39a82ec4..abc26270 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx
@@ -78,7 +78,6 @@ export const UserFriendModal = ({
})}
)}
- {tabs[currentTab]}
{renderTab()}
From 304aa011ad6b6cb0bff01de3cb83535eb7185602 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Thu, 25 Jul 2024 19:27:03 -0300
Subject: [PATCH 019/108] feat: action buttons on user profile page
---
src/locales/en/translation.json | 3 +-
src/locales/pt/translation.json | 3 +-
src/renderer/index.html | 2 +-
.../components/sidebar/sidebar-profile.tsx | 2 +-
src/renderer/src/hooks/use-user-details.ts | 10 +-
.../user-friend-modal-add-friend.tsx | 16 ++-
src/renderer/src/pages/user/user-content.tsx | 103 ++++++++++++------
src/types/index.ts | 9 ++
8 files changed, 101 insertions(+), 47 deletions(-)
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index 24ba8f0f..dc1bb1d7 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -258,6 +258,7 @@
"accept_request": "Accept request",
"ignore_request": "Ignore request",
"cancel_request": "Cancel request",
- "undo_friendship": "Undo friendship"
+ "undo_friendship": "Undo friendship",
+ "request_accepted": "Request accepted"
}
}
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index ce4efd58..c95ae82f 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -258,6 +258,7 @@
"accept_request": "Aceitar pedido",
"ignore_request": "Ignorar pedido",
"cancel_request": "Cancelar pedido",
- "undo_friendship": "Desfazer amizade"
+ "undo_friendship": "Desfazer amizade",
+ "request_accepted": "Pedido de amizade aceito"
}
}
diff --git a/src/renderer/index.html b/src/renderer/index.html
index 52276268..543b85a9 100644
--- a/src/renderer/index.html
+++ b/src/renderer/index.html
@@ -6,7 +6,7 @@
Hydra
diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx
index b16c2c28..81736e37 100644
--- a/src/renderer/src/components/sidebar/sidebar-profile.tsx
+++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx
@@ -21,7 +21,7 @@ export function SidebarProfile() {
useEffect(() => {
setReceivedRequests(
- receivedRequests.filter((request) => request.type === "RECEIVED")
+ friendRequests.filter((request) => request.type === "RECEIVED")
);
}, [friendRequests]);
diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts
index 401f4684..38f6a8f3 100644
--- a/src/renderer/src/hooks/use-user-details.ts
+++ b/src/renderer/src/hooks/use-user-details.ts
@@ -85,9 +85,13 @@ export function useUserDetails() {
[updateUserDetails]
);
- const fetchFriendRequests = useCallback(async () => {
- const friendRequests = await window.electron.getFriendRequests();
- dispatch(setFriendRequests(friendRequests));
+ const fetchFriendRequests = useCallback(() => {
+ return window.electron
+ .getFriendRequests()
+ .then((friendRequests) => {
+ dispatch(setFriendRequests(friendRequests));
+ })
+ .catch(() => {});
}, [dispatch]);
const showFriendsModal = useCallback(
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx
index f71d4790..0725674e 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx
@@ -23,7 +23,7 @@ export const UserFriendModalAddFriend = ({
const { sendFriendRequest, updateFriendRequestState, friendRequests } =
useUserDetails();
- const { showErrorToast } = useToast();
+ const { showSuccessToast, showErrorToast } = useToast();
const handleClickAddFriend = () => {
setIsAddingFriend(true);
@@ -58,19 +58,23 @@ export const UserFriendModalAddFriend = ({
const handleCancelFriendRequest = (userId: string) => {
updateFriendRequestState(userId, "CANCEL").catch(() => {
- showErrorToast("Falha ao cancelar convite");
+ showErrorToast(t("try_again"));
});
};
const handleAcceptFriendRequest = (userId: string) => {
- updateFriendRequestState(userId, "ACCEPTED").catch(() => {
- showErrorToast("Falha ao aceitar convite");
- });
+ updateFriendRequestState(userId, "ACCEPTED")
+ .then(() => {
+ showSuccessToast(t("request_accepted"));
+ })
+ .catch(() => {
+ showErrorToast(t("try_again"));
+ });
};
const handleRefuseFriendRequest = (userId: string) => {
updateFriendRequestState(userId, "REFUSED").catch(() => {
- showErrorToast("Falha ao recusar convite");
+ showErrorToast(t("try_again"));
});
};
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index 9fb1c8b5..228fdbd9 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -1,4 +1,4 @@
-import { UserGame, UserProfile } from "@types";
+import { UserGame, UserProfile, UserRelation } from "@types";
import cn from "classnames";
import * as styles from "./user.css";
import { SPACING_UNIT, vars } from "@renderer/theme.css";
@@ -45,8 +45,8 @@ export function UserContent({
const {
userDetails,
profileBackground,
- friendRequests,
signOut,
+ sendFriendRequest,
fetchFriendRequests,
showFriendsModal,
updateFriendRequestState,
@@ -124,22 +124,47 @@ export function UserContent({
}
}, [profileBackground, isMe]);
+ const handleUndoFriendship = (userRelation: UserRelation) => {
+ const userId =
+ userRelation.AId === userProfile.id ? userRelation.BId : userRelation.AId;
+
+ updateFriendRequestState(userId, "CANCEL")
+ .then(updateUserProfile)
+ .catch(() => {
+ showErrorToast(t("try_again"));
+ });
+ };
+
+ const handleSendFriendRequest = () => {
+ sendFriendRequest(userProfile.id)
+ .then(updateUserProfile)
+ .catch(() => {
+ showErrorToast(t("try_again"));
+ });
+ };
+
const handleCancelFriendRequest = (userId: string) => {
- updateFriendRequestState(userId, "CANCEL").catch(() => {
- showErrorToast("Falha ao cancelar convite");
- });
+ updateFriendRequestState(userId, "CANCEL")
+ .then(updateUserProfile)
+ .catch(() => {
+ showErrorToast(t("try_again"));
+ });
};
const handleAcceptFriendRequest = (userId: string) => {
- updateFriendRequestState(userId, "ACCEPTED").catch(() => {
- showErrorToast("Falha ao aceitar convite");
- });
+ updateFriendRequestState(userId, "ACCEPTED")
+ .then(updateUserProfile)
+ .catch(() => {
+ showErrorToast(t("try_again"));
+ });
};
const handleRefuseFriendRequest = (userId: string) => {
- updateFriendRequestState(userId, "REFUSED").catch(() => {
- showErrorToast("Falha ao recusar convite");
- });
+ updateFriendRequestState(userId, "REFUSED")
+ .then(updateUserProfile)
+ .catch(() => {
+ showErrorToast(t("try_again"));
+ });
};
const getProfileActions = () => {
@@ -157,14 +182,10 @@ export function UserContent({
);
}
- const friendRequest = friendRequests.find(
- (request) => request.id == userProfile.id
- );
-
- if (!friendRequest) {
+ if (userProfile.relation == null) {
return (
<>
- {}}>
+
{t("add_friend")}
@@ -175,35 +196,49 @@ export function UserContent({
);
}
- if (friendRequest.type === "RECEIVED") {
+ if (userProfile.relation.status === "ACCEPTED") {
return (
<>
- handleAcceptFriendRequest(friendRequest.id)}
- >
- {t("accept_request")}
-
handleRefuseFriendRequest(friendRequest.id)}
+ onClick={() => handleUndoFriendship(userProfile.relation!)}
>
- {t("ignore_request")}
+ {t("undo_friendship")}
>
);
}
+ if (userProfile.relation.BId === userProfile.id) {
+ return (
+ handleCancelFriendRequest(userProfile.relation!.BId)}
+ >
+ {t("cancel_request")}
+
+ );
+ }
+
return (
- handleCancelFriendRequest(friendRequest.id)}
- >
- {t("cancel_request")}
-
+ <>
+ handleAcceptFriendRequest(userProfile.relation!.AId)}
+ >
+ {t("accept_request")}
+
+ handleRefuseFriendRequest(userProfile.relation!.AId)}
+ >
+ {t("ignore_request")}
+
+ >
);
};
diff --git a/src/types/index.ts b/src/types/index.ts
index 1d91af62..033739c4 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -289,6 +289,14 @@ export interface FriendRequest {
type: "SENT" | "RECEIVED";
}
+export interface UserRelation {
+ AId: string;
+ BId: string;
+ status: "ACCEPTED" | "PENDING";
+ createdAt: string;
+ updatedAt: string;
+}
+
export interface UserProfile {
id: string;
displayName: string;
@@ -298,6 +306,7 @@ export interface UserProfile {
libraryGames: UserGame[];
recentGames: UserGame[];
friends: UserFriends;
+ relation: UserRelation | null;
}
export interface DownloadSource {
From edf920fed3228c2a3806e6bf8b21cba2ba81be10 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Thu, 25 Jul 2024 20:08:53 -0300
Subject: [PATCH 020/108] feat: block and unblock events
---
src/locales/en/translation.json | 3 ++-
src/locales/pt/translation.json | 3 ++-
src/main/events/index.ts | 2 ++
src/main/events/user/block-user.ts | 11 +++++++++++
src/main/events/user/unblock-user.ts | 11 +++++++++++
src/preload/index.ts | 2 ++
src/renderer/src/declaration.d.ts | 2 ++
src/renderer/src/hooks/use-user-details.ts | 10 ++++++++++
src/renderer/src/pages/user/user-content.tsx | 14 +++++++++++++-
9 files changed, 55 insertions(+), 3 deletions(-)
create mode 100644 src/main/events/user/block-user.ts
create mode 100644 src/main/events/user/unblock-user.ts
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index dc1bb1d7..3385feb7 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -259,6 +259,7 @@
"ignore_request": "Ignore request",
"cancel_request": "Cancel request",
"undo_friendship": "Undo friendship",
- "request_accepted": "Request accepted"
+ "request_accepted": "Request accepted",
+ "user_blocked_successfully": "User blocked successfully"
}
}
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index c95ae82f..5d58b62f 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -259,6 +259,7 @@
"ignore_request": "Ignorar pedido",
"cancel_request": "Cancelar pedido",
"undo_friendship": "Desfazer amizade",
- "request_accepted": "Pedido de amizade aceito"
+ "request_accepted": "Pedido de amizade aceito",
+ "user_blocked_successfully": "Usuário bloqueado com sucesso"
}
}
diff --git a/src/main/events/index.ts b/src/main/events/index.ts
index 398235f0..5ae0db46 100644
--- a/src/main/events/index.ts
+++ b/src/main/events/index.ts
@@ -43,6 +43,8 @@ import "./auth/sign-out";
import "./auth/open-auth-window";
import "./auth/get-session-hash";
import "./user/get-user";
+import "./user/block-user";
+import "./user/unblock-user";
import "./user/get-user-friends";
import "./profile/get-friend-requests";
import "./profile/get-me";
diff --git a/src/main/events/user/block-user.ts b/src/main/events/user/block-user.ts
new file mode 100644
index 00000000..303a5315
--- /dev/null
+++ b/src/main/events/user/block-user.ts
@@ -0,0 +1,11 @@
+import { registerEvent } from "../register-event";
+import { HydraApi } from "@main/services";
+
+const blockUser = async (
+ _event: Electron.IpcMainInvokeEvent,
+ userId: string
+): Promise => {
+ return HydraApi.get(`/user/${userId}/block`);
+};
+
+registerEvent("block", blockUser);
diff --git a/src/main/events/user/unblock-user.ts b/src/main/events/user/unblock-user.ts
new file mode 100644
index 00000000..c1c8112e
--- /dev/null
+++ b/src/main/events/user/unblock-user.ts
@@ -0,0 +1,11 @@
+import { registerEvent } from "../register-event";
+import { HydraApi } from "@main/services";
+
+const unblockUser = async (
+ _event: Electron.IpcMainInvokeEvent,
+ userId: string
+): Promise => {
+ return HydraApi.get(`/user/${userId}/unblock`);
+};
+
+registerEvent("unblockUser", unblockUser);
diff --git a/src/preload/index.ts b/src/preload/index.ts
index cd3b9686..b7c368fa 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -145,6 +145,8 @@ contextBridge.exposeInMainWorld("electron", {
/* User */
getUser: (userId: string) => ipcRenderer.invoke("getUser", userId),
+ blockUser: (userId: string) => ipcRenderer.invoke("blockUser", userId),
+ unblockUser: (userId: string) => ipcRenderer.invoke("unblockUser", userId),
getUserFriends: (userId: string, take: number, skip: number) =>
ipcRenderer.invoke("getUserFriends", userId, take, skip),
diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts
index ad0b8953..564d7501 100644
--- a/src/renderer/src/declaration.d.ts
+++ b/src/renderer/src/declaration.d.ts
@@ -128,6 +128,8 @@ declare global {
/* User */
getUser: (userId: string) => Promise;
+ blockUser: (userId: string) => Promise;
+ unblockUser: (userId: string) => Promise;
getUserFriends: (
userId: string,
take: number,
diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts
index 38f6a8f3..4b4d5b45 100644
--- a/src/renderer/src/hooks/use-user-details.ts
+++ b/src/renderer/src/hooks/use-user-details.ts
@@ -124,6 +124,14 @@ export function useUserDetails() {
[fetchFriendRequests]
);
+ const blockUser = (userId: string) => {
+ return window.electron.blockUser(userId);
+ };
+
+ const unblockUser = (userId: string) => {
+ return window.electron.unblockUser(userId);
+ };
+
return {
userDetails,
profileBackground,
@@ -141,5 +149,7 @@ export function useUserDetails() {
sendFriendRequest,
fetchFriendRequests,
updateFriendRequestState,
+ blockUser,
+ unblockUser,
};
}
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index 228fdbd9..8224122b 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -50,6 +50,7 @@ export function UserContent({
fetchFriendRequests,
showFriendsModal,
updateFriendRequestState,
+ blockUser,
} = useUserDetails();
const { showSuccessToast, showErrorToast } = useToast();
@@ -143,6 +144,17 @@ export function UserContent({
});
};
+ const handleBlockUser = () => {
+ blockUser(userProfile.id)
+ .then(() => {
+ showSuccessToast(t("user_blocked_successfully"));
+ navigate(-1);
+ })
+ .catch(() => {
+ showErrorToast(t("try_again"));
+ });
+ };
+
const handleCancelFriendRequest = (userId: string) => {
updateFriendRequestState(userId, "CANCEL")
.then(updateUserProfile)
@@ -189,7 +201,7 @@ export function UserContent({
{t("add_friend")}
- {}}>
+
{t("block_user")}
>
From 00c46bc98155989859d6f0d7f3e7c3837ec88ce8 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Thu, 25 Jul 2024 23:03:11 -0300
Subject: [PATCH 021/108] feat: implement undo friendship
---
src/main/events/index.ts | 1 +
src/main/events/profile/undo-friendship.ts | 11 +++++++++++
src/preload/index.ts | 2 ++
src/renderer/src/declaration.d.ts | 1 +
src/renderer/src/hooks/use-user-details.ts | 5 +++++
src/renderer/src/pages/user/user-content.tsx | 7 +++++--
6 files changed, 25 insertions(+), 2 deletions(-)
create mode 100644 src/main/events/profile/undo-friendship.ts
diff --git a/src/main/events/index.ts b/src/main/events/index.ts
index 5ae0db46..57daf51c 100644
--- a/src/main/events/index.ts
+++ b/src/main/events/index.ts
@@ -48,6 +48,7 @@ import "./user/unblock-user";
import "./user/get-user-friends";
import "./profile/get-friend-requests";
import "./profile/get-me";
+import "./profile/undo-friendship";
import "./profile/update-friend-request";
import "./profile/update-profile";
import "./profile/send-friend-request";
diff --git a/src/main/events/profile/undo-friendship.ts b/src/main/events/profile/undo-friendship.ts
new file mode 100644
index 00000000..d6f858f1
--- /dev/null
+++ b/src/main/events/profile/undo-friendship.ts
@@ -0,0 +1,11 @@
+import { registerEvent } from "../register-event";
+import { HydraApi } from "@main/services";
+
+const undoFriendship = async (
+ _event: Electron.IpcMainInvokeEvent,
+ userId: string
+): Promise => {
+ return HydraApi.delete(`/profile/friends/${userId}`);
+};
+
+registerEvent("undoFriendship", undoFriendship);
diff --git a/src/preload/index.ts b/src/preload/index.ts
index b7c368fa..3350a340 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -135,6 +135,8 @@ contextBridge.exposeInMainWorld("electron", {
/* Profile */
getMe: () => ipcRenderer.invoke("getMe"),
+ undoFriendship: (userId: string) =>
+ ipcRenderer.invoke("undoFriendship", userId),
updateProfile: (displayName: string, newProfileImagePath: string | null) =>
ipcRenderer.invoke("updateProfile", displayName, newProfileImagePath),
getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"),
diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts
index 564d7501..e022cffe 100644
--- a/src/renderer/src/declaration.d.ts
+++ b/src/renderer/src/declaration.d.ts
@@ -138,6 +138,7 @@ declare global {
/* Profile */
getMe: () => Promise;
+ undoFriendship: (userId: string) => Promise;
updateProfile: (
displayName: string,
newProfileImagePath: string | null
diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts
index 4b4d5b45..21690e7e 100644
--- a/src/renderer/src/hooks/use-user-details.ts
+++ b/src/renderer/src/hooks/use-user-details.ts
@@ -124,6 +124,10 @@ export function useUserDetails() {
[fetchFriendRequests]
);
+ const undoFriendship = (userId: string) => {
+ return window.electron.undoFriendship(userId);
+ };
+
const blockUser = (userId: string) => {
return window.electron.blockUser(userId);
};
@@ -151,5 +155,6 @@ export function useUserDetails() {
updateFriendRequestState,
blockUser,
unblockUser,
+ undoFriendship,
};
}
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index 8224122b..ad591790 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -50,6 +50,7 @@ export function UserContent({
fetchFriendRequests,
showFriendsModal,
updateFriendRequestState,
+ undoFriendship,
blockUser,
} = useUserDetails();
const { showSuccessToast, showErrorToast } = useToast();
@@ -127,9 +128,11 @@ export function UserContent({
const handleUndoFriendship = (userRelation: UserRelation) => {
const userId =
- userRelation.AId === userProfile.id ? userRelation.BId : userRelation.AId;
+ userRelation.AId === userDetails?.id
+ ? userRelation.BId
+ : userRelation.AId;
- updateFriendRequestState(userId, "CANCEL")
+ undoFriendship(userId)
.then(updateUserProfile)
.catch(() => {
showErrorToast(t("try_again"));
From 15269f3908cec1491641fb119f12418bf72760d3 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Fri, 26 Jul 2024 11:30:01 -0300
Subject: [PATCH 022/108] feat: show confirm modal for block action
---
src/locales/en/translation.json | 3 +-
src/locales/pt/translation.json | 3 +-
src/main/events/user/block-user.ts | 4 +-
src/main/events/user/unblock-user.ts | 2 +-
.../src/pages/user/user-block-modal.tsx | 44 +++++++++++++++++++
src/renderer/src/pages/user/user-content.tsx | 12 ++++-
.../src/pages/user/user-signout-modal.tsx | 4 +-
7 files changed, 64 insertions(+), 8 deletions(-)
create mode 100644 src/renderer/src/pages/user/user-block-modal.tsx
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index 3385feb7..b24509d3 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -260,6 +260,7 @@
"cancel_request": "Cancel request",
"undo_friendship": "Undo friendship",
"request_accepted": "Request accepted",
- "user_blocked_successfully": "User blocked successfully"
+ "user_blocked_successfully": "User blocked successfully",
+ "user_block_modal_text": "This will block {{displayName}}"
}
}
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index 5d58b62f..ef94c31f 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -260,6 +260,7 @@
"cancel_request": "Cancelar pedido",
"undo_friendship": "Desfazer amizade",
"request_accepted": "Pedido de amizade aceito",
- "user_blocked_successfully": "Usuário bloqueado com sucesso"
+ "user_blocked_successfully": "Usuário bloqueado com sucesso",
+ "user_block_modal_text": "Bloquear {{displayName}}"
}
}
diff --git a/src/main/events/user/block-user.ts b/src/main/events/user/block-user.ts
index 303a5315..041b6530 100644
--- a/src/main/events/user/block-user.ts
+++ b/src/main/events/user/block-user.ts
@@ -5,7 +5,7 @@ const blockUser = async (
_event: Electron.IpcMainInvokeEvent,
userId: string
): Promise => {
- return HydraApi.get(`/user/${userId}/block`);
+ return HydraApi.post(`/user/${userId}/block`);
};
-registerEvent("block", blockUser);
+registerEvent("blockUser", blockUser);
diff --git a/src/main/events/user/unblock-user.ts b/src/main/events/user/unblock-user.ts
index c1c8112e..57cdb236 100644
--- a/src/main/events/user/unblock-user.ts
+++ b/src/main/events/user/unblock-user.ts
@@ -5,7 +5,7 @@ const unblockUser = async (
_event: Electron.IpcMainInvokeEvent,
userId: string
): Promise => {
- return HydraApi.get(`/user/${userId}/unblock`);
+ return HydraApi.post(`/user/${userId}/unblock`);
};
registerEvent("unblockUser", unblockUser);
diff --git a/src/renderer/src/pages/user/user-block-modal.tsx b/src/renderer/src/pages/user/user-block-modal.tsx
new file mode 100644
index 00000000..e179e4da
--- /dev/null
+++ b/src/renderer/src/pages/user/user-block-modal.tsx
@@ -0,0 +1,44 @@
+import { Button, Modal } from "@renderer/components";
+import * as styles from "./user.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 (
+ <>
+
+
+
+ {t("user_block_modal_text", { displayName })}
+
+
+
+ {t("block_user")}
+
+
+
+ {t("cancel")}
+
+
+
+
+ >
+ );
+};
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index ad591790..88d9892d 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -28,6 +28,7 @@ import { Button, Link } from "@renderer/components";
import { UserEditProfileModal } from "./user-edit-modal";
import { UserSignOutModal } from "./user-signout-modal";
import { UserFriendModalTab } from "../shared-modals/user-friend-modal";
+import { UserBlockModal } from "./user-block-modal";
const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120;
@@ -59,6 +60,7 @@ export function UserContent({
useState();
const [showEditProfileModal, setShowEditProfileModal] = useState(false);
const [showSignOutModal, setShowSignOutModal] = useState(false);
+ const [showUserBlockModal, setShowUserBlockModal] = useState(false);
const { gameRunning } = useAppSelector((state) => state.gameRunning);
@@ -150,6 +152,7 @@ export function UserContent({
const handleBlockUser = () => {
blockUser(userProfile.id)
.then(() => {
+ setShowUserBlockModal(false);
showSuccessToast(t("user_blocked_successfully"));
navigate(-1);
})
@@ -204,7 +207,7 @@ export function UserContent({
{t("add_friend")}
-
+ setShowUserBlockModal(true)}>
{t("block_user")}
>
@@ -272,6 +275,13 @@ export function UserContent({
onConfirm={handleConfirmSignout}
/>
+ setShowUserBlockModal(false)}
+ onConfirm={handleBlockUser}
+ displayName={userProfile.displayName}
+ />
+
void;
onClose: () => void;
@@ -12,7 +12,7 @@ export const UserSignOutModal = ({
visible,
onConfirm,
onClose,
-}: UserEditProfileModalProps) => {
+}: UserSignOutModalProps) => {
const { t } = useTranslation("user_profile");
return (
From b188e93343581003a66036109a1fbeafb2ab4f0b Mon Sep 17 00:00:00 2001
From: Aniol
Date: Sat, 27 Jul 2024 13:05:15 +0200
Subject: [PATCH 023/108] Update Catalan translation.json
Update Catalan translation.json.
---
src/locales/ca/translation.json | 121 ++++++++++++++++++++++++++++++--
1 file changed, 114 insertions(+), 7 deletions(-)
diff --git a/src/locales/ca/translation.json b/src/locales/ca/translation.json
index c756745a..d3ac5dbe 100644
--- a/src/locales/ca/translation.json
+++ b/src/locales/ca/translation.json
@@ -1,4 +1,7 @@
{
+"app": {
+ "successfully_signed_in": "Has entrat correctament"
+ },
"home": {
"featured": "Destacats",
"trending": "Populars",
@@ -14,7 +17,10 @@
"paused": "{{title}} (Pausat)",
"downloading": "{{title}} ({{percentage}} - S'està baixant…)",
"filter": "Filtra la biblioteca",
- "home": "Inici"
+ "home": "Inici",
+ "queued": "{{title}} (En espera)",
+ "game_has_no_executable": "El joc encara no té un executable seleccionat",
+ "sign_in": "Entra"
},
"header": {
"search": "Cerca jocs",
@@ -29,7 +35,9 @@
"bottom_panel": {
"no_downloads_in_progress": "Cap baixada en curs",
"downloading_metadata": "S'estan baixant les metadades de: {{title}}…",
- "downloading": "S'està baixant: {{title}}… ({{percentage}} complet) - Finalització: {{eta}} - {{speed}}"
+ "downloading": "S'està baixant: {{title}}… ({{percentage}} complet) - Finalització: {{eta}} - {{speed}}",
+ "calculating_eta": "Descarregant {{title}}… ({{percentage}} completat) - Calculant el temps restant…",
+ "checking_files": "Comprovant els fitxers de {{title}}… ({{percentage}} completat)"
},
"catalogue": {
"next_page": "Pàgina següent",
@@ -47,12 +55,14 @@
"cancel": "Cancel·la",
"remove": "Elimina",
"space_left_on_disk": "{{space}} lliures al disc",
- "eta": "Finalització: {{eta}}",
+ "eta": "Finalitza en: {{eta}}",
+ "calculating_eta": "Calculant temps estimat…",
"downloading_metadata": "S'estan baixant les metadades…",
"filter": "Filtra els reempaquetats",
"requirements": "Requisits del sistema",
"minimum": "Mínims",
"recommended": "Recomanats",
+ "paused": "Paused",
"release_date": "Publicat el {{date}}",
"publisher": "Publicat per {{publisher}}",
"hours": "hores",
@@ -81,7 +91,29 @@
"previous_screenshot": "Captura anterior",
"next_screenshot": "Captura següent",
"screenshot": "Captura {{number}}",
- "open_screenshot": "Obre la captura {{number}}"
+ "open_screenshot": "Obre la captura {{number}}",
+ "download_settings": "Configuració de descàrrega",
+ "downloader": "Descarregador",
+ "select_executable": "Selecciona",
+ "no_executable_selected": "No hi ha executable selccionat",
+ "open_folder": "Obre carpeta",
+ "open_download_location": "Visualitzar fitxers descarregats",
+ "create_shortcut": "Crear accés directe a l'escriptori",
+ "remove_files": "Elimina fitxers",
+ "remove_from_library_title": "Segur?",
+ "remove_from_library_description": "Això eliminarà el videojoc {{game}} del teu catàleg",
+ "options": "Opcions",
+ "executable_section_title": "Executable",
+ "executable_section_description": "Directori del fitxer des d'on s'executarà quan es cliqui a \"Executar\"",
+ "downloads_secion_title": "Descàrregues",
+ "downloads_section_description": "Comprova actualitzacions o altres versions del videojoc",
+ "danger_zone_section_title": "Zona de perill",
+ "danger_zone_section_description": "Elimina aquest videojoc del teu catàleg o els fitxers descarregats per Hydra",
+ "download_in_progress": "Descàrrega en progrés",
+ "download_paused": "Descàrrega en pausa",
+ "last_downloaded_option": "Opció de l'última descàrrega",
+ "create_shortcut_success": "Accés directe creat satisfactòriament",
+ "create_shortcut_error": "Error al crear l'accés directe"
},
"activation": {
"title": "Activa l'Hydra",
@@ -98,6 +130,7 @@
"paused": "Pausada",
"verifying": "S'està verificant…",
"completed": "Completada",
+ "removed": "No descarregat",
"cancel": "Cancel·la",
"filter": "Filtra els jocs baixats",
"remove": "Elimina",
@@ -106,7 +139,14 @@
"delete": "Elimina l'instal·lador",
"delete_modal_title": "N'estàs segur?",
"delete_modal_description": "S'eliminaran de l'ordinador tots els fitxers d'instal·lació",
- "install": "Instal·la"
+ "install": "Instal·la",
+ "download_in_progress": "En progrés",
+ "queued_downloads": "Descàrregues en espera",
+ "downloads_completed": "Completat",
+ "queued": "En espera",
+ "no_downloads_title": "Buit",
+ "no_downloads_description": "No has descarregat res amb Hydra encara, però mai és tard per començar a fer-ho.",
+ "checking_files": "Comprovant fitxers…"
},
"settings": {
"downloads_path": "Ruta de baixades",
@@ -119,16 +159,49 @@
"launch_with_system": "Inicia l'Hydra quan s'iniciï el sistema",
"general": "General",
"behavior": "Comportament",
+ "download_sources": "Fonts de descàrrega",
+ "language": "Idioma",
+ "real_debrid_api_token": "Testimoni API",
"enable_real_debrid": "Activa el Real Debrid",
+ "real_debrid_description": "Real-Debrid és un programa de descàrrega sense restriccions que us permet descarregar fitxers a l'instant i al màxim de la vostra velocitat d'Internet.",
+ "real_debrid_invalid_token": "Invalida el testimoni de l'API",
"real_debrid_api_token_hint": "Pots obtenir la teva clau de l'API <0>aquí0>.",
- "save_changes": "Desa els canvis"
+ "real_debrid_free_account_error": "L'usuari \"{{username}}\" és un compte gratuït. Si us plau subscriu-te a Real-Debrid",
+ "real_debrid_linked_message": "Compte \"{{username}}\" vinculat",
+ "save_changes": "Desa els canvis",
+ "changes_saved": "Els canvis s'han desat correctament",
+ "download_sources_description": "Hydra buscarà els enllaços de descàrrega d'aquestes fonts. L'URL d'origen ha de ser un enllaç directe a un fitxer .json que contingui els enllaços de descàrrega.",
+ "validate_download_source": "Valida",
+ "remove_download_source": "Elimina",
+ "add_download_source": "Afegeix font",
+ "download_count_zero": "No hi ha baixades a la llista",
+ "download_count_one": "{{countFormatted}} a la llista de baixades",
+ "download_count_other": "{{countFormatted}} baixades a la llista",
+ "download_options_zero": "No hi ha cap descàrrega disponible",
+ "download_options_one": "{{countFormatted}} descàrrega disponible",
+ "download_options_other": "{{countFormatted}} baixades disponibles",
+ "download_source_url": "Descarrega l'URL de la font",
+ "add_download_source_description": "Inseriu la URL que conté el fitxer .json",
+ "download_source_up_to_date": "Actualitzat",
+ "download_source_errored": "S'ha produït un error",
+ "sync_download_sources": "Sincronitza fonts",
+ "removed_download_source": "S'ha eliminat la font de descàrrega",
+ "added_download_source": "Added download source",
+ "download_sources_synced": "Totes les fonts de descàrrega estan sincronitzades",
+ "insert_valid_json_url": "Insereix una URL JSON vàlida",
+ "found_download_option_zero": "No s'ha trobat cap opció de descàrrega",
+ "found_download_option_one": "S'ha trobat l'opció de baixada de {{countFormatted}}",
+ "found_download_option_other": "S'han trobat {{countFormatted}} opcions de baixada",
+ "import": "Import"
},
"notifications": {
"download_complete": "La baixada ha finalitzat",
"game_ready_to_install": "{{title}} ja es pot instal·lar",
"repack_list_updated": "S'ha actualitzat la llista de reempaquetats",
"repack_count_one": "S'ha afegit {{count}} reempaquetat",
- "repack_count_other": "S'han afegit {{count}} reempaquetats"
+ "repack_count_other": "S'han afegit {{count}} reempaquetats",
+ "new_update_available": "Versió {{version}} disponible",
+ "restart_to_install_update": "Reinicieu Hydra per instal·lar l'actualització"
},
"system_tray": {
"open": "Obre l'Hydra",
@@ -144,5 +217,39 @@
},
"modal": {
"close": "Botó de tancar"
+ },
+ "forms": {
+ "toggle_password_visibility": "Commuta la visibilitat de la contrasenya"
+ },
+ "user_profile": {
+ "amount_hours": "{{amount}} hores",
+ "amount_minutes": "{{amount}} minuts",
+ "last_time_played": "Última partida {{period}}",
+ "activity": "Activitat recent",
+ "library": "Biblioteca",
+ "total_play_time": "Temps total de joc:{{amount}}",
+ "no_recent_activity_title": "Hmmm… encara no res",
+ "no_recent_activity_description": "No has jugat a cap joc recentment. És el moment de canviar-ho!",
+ "display_name": "Nom de visualització",
+ "saving": "Desant",
+ "save": "Desa",
+ "edit_profile": "Edita el Perfil",
+ "saved_successfully": "S'ha desat correctament",
+ "try_again": "Siusplau torna-ho a provar",
+ "sign_out_modal_title": "Segur?",
+ "cancel": "Cancel·la",
+ "successfully_signed_out": "S'ha tancat la sessió correctament",
+ "sign_out": "Tanca sessió",
+ "playing_for": "Jugant per {{amount}}",
+ "sign_out_modal_text": "La vostra biblioteca està enllaçada amb el vostre compte actual. Quan tanqueu la sessió, la vostra biblioteca ja no serà visible i cap progrés no es desarà. Voleu continuar amb tancar la sessió?",
+ "add_friends": "Afegeix amics",
+ "add": "Afegeix",
+ "friend_code": "Codi de l'amic",
+ "see_profile": "Veure Perfil",
+ "sending": "Enviant",
+ "friend_request_sent": "Sol·licitud d'amistat enviada",
+ "friends": "Amistats",
+ "friends_list": "Llista d'amistats",
+ "user_not_found": "Usuari no trobat"
}
}
From d60242a62cb3a72bf7b847d7e4b045e6b9356af1 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 29 Jul 2024 19:10:53 -0300
Subject: [PATCH 024/108] feat: code review
---
src/main/events/user/get-user.ts | 8 +-
src/renderer/src/pages/user/user-content.tsx | 118 ++++++++----------
...nout-modal.tsx => user-sign-out-modal.tsx} | 0
src/types/index.ts | 3 +-
4 files changed, 62 insertions(+), 67 deletions(-)
rename src/renderer/src/pages/user/{user-signout-modal.tsx => user-sign-out-modal.tsx} (100%)
diff --git a/src/main/events/user/get-user.ts b/src/main/events/user/get-user.ts
index 98b98a77..96a93042 100644
--- a/src/main/events/user/get-user.ts
+++ b/src/main/events/user/get-user.ts
@@ -50,7 +50,13 @@ const getUser = async (
})
);
- return { ...profile, libraryGames, recentGames, friends };
+ return {
+ ...profile,
+ libraryGames,
+ recentGames,
+ friends: friends.friends,
+ totalFriends: friends.totalFriends,
+ };
} catch (err) {
return null;
}
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index 88d9892d..81db119d 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -1,4 +1,4 @@
-import { UserGame, UserProfile, UserRelation } from "@types";
+import { FriendRequestAction, UserGame, UserProfile } from "@types";
import cn from "classnames";
import * as styles from "./user.css";
import { SPACING_UNIT, vars } from "@renderer/theme.css";
@@ -26,7 +26,7 @@ import {
} from "@primer/octicons-react";
import { Button, Link } from "@renderer/components";
import { UserEditProfileModal } from "./user-edit-modal";
-import { UserSignOutModal } from "./user-signout-modal";
+import { UserSignOutModal } from "./user-sign-out-modal";
import { UserFriendModalTab } from "../shared-modals/user-friend-modal";
import { UserBlockModal } from "./user-block-modal";
@@ -37,6 +37,8 @@ export interface ProfileContentProps {
updateUserProfile: () => Promise;
}
+type FriendAction = FriendRequestAction | ("BLOCK" | "UNDO" | "SEND");
+
export function UserContent({
userProfile,
updateUserProfile,
@@ -128,62 +130,35 @@ export function UserContent({
}
}, [profileBackground, isMe]);
- const handleUndoFriendship = (userRelation: UserRelation) => {
- const userId =
- userRelation.AId === userDetails?.id
- ? userRelation.BId
- : userRelation.AId;
+ const handleFriendAction = (userId: string, action: FriendAction) => {
+ try {
+ if (action === "UNDO") {
+ undoFriendship(userId).then(updateUserProfile);
+ return;
+ }
- undoFriendship(userId)
- .then(updateUserProfile)
- .catch(() => {
- showErrorToast(t("try_again"));
- });
+ if (action === "BLOCK") {
+ blockUser(userId).then(() => {
+ setShowUserBlockModal(false);
+ showSuccessToast(t("user_blocked_successfully"));
+ navigate(-1);
+ });
+
+ return;
+ }
+
+ if (action === "SEND") {
+ sendFriendRequest(userProfile.id).then(updateUserProfile);
+ return;
+ }
+
+ updateFriendRequestState(userId, action).then(updateUserProfile);
+ } catch (err) {
+ showErrorToast(t("try_again"));
+ }
};
- const handleSendFriendRequest = () => {
- sendFriendRequest(userProfile.id)
- .then(updateUserProfile)
- .catch(() => {
- showErrorToast(t("try_again"));
- });
- };
-
- const handleBlockUser = () => {
- blockUser(userProfile.id)
- .then(() => {
- setShowUserBlockModal(false);
- showSuccessToast(t("user_blocked_successfully"));
- navigate(-1);
- })
- .catch(() => {
- showErrorToast(t("try_again"));
- });
- };
-
- const handleCancelFriendRequest = (userId: string) => {
- updateFriendRequestState(userId, "CANCEL")
- .then(updateUserProfile)
- .catch(() => {
- showErrorToast(t("try_again"));
- });
- };
-
- const handleAcceptFriendRequest = (userId: string) => {
- updateFriendRequestState(userId, "ACCEPTED")
- .then(updateUserProfile)
- .catch(() => {
- showErrorToast(t("try_again"));
- });
- };
-
- const handleRefuseFriendRequest = (userId: string) => {
- updateFriendRequestState(userId, "REFUSED")
- .then(updateUserProfile)
- .catch(() => {
- showErrorToast(t("try_again"));
- });
- };
+ const showFriends = isMe || userProfile.totalFriends > 0;
const getProfileActions = () => {
if (isMe) {
@@ -203,7 +178,10 @@ export function UserContent({
if (userProfile.relation == null) {
return (
<>
-
+ handleFriendAction(userProfile.id, "SEND")}
+ >
{t("add_friend")}
@@ -215,12 +193,17 @@ export function UserContent({
}
if (userProfile.relation.status === "ACCEPTED") {
+ const userId =
+ userProfile.relation.AId === userDetails?.id
+ ? userProfile.relation.BId
+ : userProfile.relation.AId;
+
return (
<>
handleUndoFriendship(userProfile.relation!)}
+ onClick={() => handleFriendAction(userId, "UNDO")}
>
{t("undo_friendship")}
@@ -233,7 +216,9 @@ export function UserContent({
handleCancelFriendRequest(userProfile.relation!.BId)}
+ onClick={() =>
+ handleFriendAction(userProfile.relation!.BId, "CANCEL")
+ }
>
{t("cancel_request")}
@@ -245,14 +230,18 @@ export function UserContent({
handleAcceptFriendRequest(userProfile.relation!.AId)}
+ onClick={() =>
+ handleFriendAction(userProfile.relation!.AId, "ACCEPTED")
+ }
>
{t("accept_request")}
handleRefuseFriendRequest(userProfile.relation!.AId)}
+ onClick={() =>
+ handleFriendAction(userProfile.relation!.AId, "REFUSED")
+ }
>
{t("ignore_request")}
@@ -278,7 +267,7 @@ export function UserContent({
setShowUserBlockModal(false)}
- onConfirm={handleBlockUser}
+ onConfirm={() => handleFriendAction(userProfile.id, "BLOCK")}
displayName={userProfile.displayName}
/>
@@ -479,8 +468,7 @@ export function UserContent({
- {(isMe ||
- (userProfile.friends && userProfile.friends.totalFriends > 0)) && (
+ {showFriends && (
- {userProfile.friends.totalFriends}
+ {userProfile.totalFriends}
@@ -512,7 +500,7 @@ export function UserContent({
gap: `${SPACING_UNIT}px`,
}}
>
- {userProfile.friends.friends.map((friend) => {
+ {userProfile.friends.map((friend) => {
return (
Date: Tue, 30 Jul 2024 15:15:34 -0300
Subject: [PATCH 025/108] feat: add generic to HydraApi calls
---
src/main/services/hydra-api.ts | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts
index 6547ec15..5365bd9e 100644
--- a/src/main/services/hydra-api.ts
+++ b/src/main/services/hydra-api.ts
@@ -196,52 +196,52 @@ export class HydraApi {
throw err;
};
- static async get(url: string, params?: any) {
+ static async get(url: string, params?: any) {
if (!this.isLoggedIn()) throw new UserNotLoggedInError();
await this.revalidateAccessTokenIfExpired();
return this.instance
- .get(url, { params, ...this.getAxiosConfig() })
+ .get(url, { params, ...this.getAxiosConfig() })
.then((response) => response.data)
.catch(this.handleUnauthorizedError);
}
- static async post(url: string, data?: any) {
+ static async post(url: string, data?: any) {
if (!this.isLoggedIn()) throw new UserNotLoggedInError();
await this.revalidateAccessTokenIfExpired();
return this.instance
- .post(url, data, this.getAxiosConfig())
+ .post(url, data, this.getAxiosConfig())
.then((response) => response.data)
.catch(this.handleUnauthorizedError);
}
- static async put(url: string, data?: any) {
+ static async put(url: string, data?: any) {
if (!this.isLoggedIn()) throw new UserNotLoggedInError();
await this.revalidateAccessTokenIfExpired();
return this.instance
- .put(url, data, this.getAxiosConfig())
+ .put(url, data, this.getAxiosConfig())
.then((response) => response.data)
.catch(this.handleUnauthorizedError);
}
- static async patch(url: string, data?: any) {
+ static async patch(url: string, data?: any) {
if (!this.isLoggedIn()) throw new UserNotLoggedInError();
await this.revalidateAccessTokenIfExpired();
return this.instance
- .patch(url, data, this.getAxiosConfig())
+ .patch(url, data, this.getAxiosConfig())
.then((response) => response.data)
.catch(this.handleUnauthorizedError);
}
- static async delete(url: string) {
+ static async delete(url: string) {
if (!this.isLoggedIn()) throw new UserNotLoggedInError();
await this.revalidateAccessTokenIfExpired();
return this.instance
- .delete(url, this.getAxiosConfig())
+ .delete(url, this.getAxiosConfig())
.then((response) => response.data)
.catch(this.handleUnauthorizedError);
}
From 4dcd97bce80aee2b4c7ebbea3e21f4f3de721375 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Tue, 30 Jul 2024 15:15:50 -0300
Subject: [PATCH 026/108] feat: update return types
---
src/main/events/profile/undo-friendship.ts | 4 ++--
src/main/events/user/block-user.ts | 4 ++--
src/main/events/user/unblock-user.ts | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/main/events/profile/undo-friendship.ts b/src/main/events/profile/undo-friendship.ts
index d6f858f1..371bc5cc 100644
--- a/src/main/events/profile/undo-friendship.ts
+++ b/src/main/events/profile/undo-friendship.ts
@@ -4,8 +4,8 @@ import { HydraApi } from "@main/services";
const undoFriendship = async (
_event: Electron.IpcMainInvokeEvent,
userId: string
-): Promise => {
- return HydraApi.delete(`/profile/friends/${userId}`);
+) => {
+ await HydraApi.delete(`/profile/friends/${userId}`);
};
registerEvent("undoFriendship", undoFriendship);
diff --git a/src/main/events/user/block-user.ts b/src/main/events/user/block-user.ts
index 041b6530..8003f478 100644
--- a/src/main/events/user/block-user.ts
+++ b/src/main/events/user/block-user.ts
@@ -4,8 +4,8 @@ import { HydraApi } from "@main/services";
const blockUser = async (
_event: Electron.IpcMainInvokeEvent,
userId: string
-): Promise => {
- return HydraApi.post(`/user/${userId}/block`);
+) => {
+ await HydraApi.post(`/user/${userId}/block`);
};
registerEvent("blockUser", blockUser);
diff --git a/src/main/events/user/unblock-user.ts b/src/main/events/user/unblock-user.ts
index 57cdb236..ac678dbd 100644
--- a/src/main/events/user/unblock-user.ts
+++ b/src/main/events/user/unblock-user.ts
@@ -4,8 +4,8 @@ import { HydraApi } from "@main/services";
const unblockUser = async (
_event: Electron.IpcMainInvokeEvent,
userId: string
-): Promise => {
- return HydraApi.post(`/user/${userId}/unblock`);
+) => {
+ await HydraApi.post(`/user/${userId}/unblock`);
};
registerEvent("unblockUser", unblockUser);
From f631cd3013ef8d699167cdee78f598c919c7fd5c Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Tue, 30 Jul 2024 15:55:51 -0300
Subject: [PATCH 027/108] feat: make getUserFriends throws exception
---
src/main/events/user/get-user-friends.ts | 20 +++++---------------
src/main/events/user/get-user.ts | 4 +++-
2 files changed, 8 insertions(+), 16 deletions(-)
diff --git a/src/main/events/user/get-user-friends.ts b/src/main/events/user/get-user-friends.ts
index a9394d95..28783459 100644
--- a/src/main/events/user/get-user-friends.ts
+++ b/src/main/events/user/get-user-friends.ts
@@ -8,23 +8,13 @@ export const getUserFriends = async (
take: number,
skip: number
): Promise => {
- try {
- const loggedUser = await userAuthRepository.findOne({ where: { id: 1 } });
+ const loggedUser = await userAuthRepository.findOne({ where: { id: 1 } });
- if (loggedUser?.userId == userId) {
- return HydraApi.get(`/profile/friends`, { take, skip }).catch((_err) => {
- return { totalFriends: 0, friends: [] };
- });
- }
-
- return HydraApi.get(`/user/${userId}/friends`, { take, skip }).catch(
- (_err) => {
- return { totalFriends: 0, friends: [] };
- }
- );
- } catch (err) {
- return { totalFriends: 0, friends: [] };
+ if (loggedUser?.userId === userId) {
+ return HydraApi.get(`/profile/friends`, { take, skip });
}
+
+ return HydraApi.get(`/user/${userId}/friends`, { take, skip });
};
const getUserFriendsEvent = async (
diff --git a/src/main/events/user/get-user.ts b/src/main/events/user/get-user.ts
index 96a93042..eb4f0619 100644
--- a/src/main/events/user/get-user.ts
+++ b/src/main/events/user/get-user.ts
@@ -13,7 +13,9 @@ const getUser = async (
try {
const [profile, friends] = await Promise.all([
HydraApi.get(`/user/${userId}`),
- getUserFriends(userId, 12, 0),
+ getUserFriends(userId, 12, 0).catch(() => {
+ return { totalFriends: 0, friends: [] };
+ }),
]);
const recentGames = await Promise.all(
From d9c140b2ab619be2781302d1b79e4ca971f84d2e Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Tue, 30 Jul 2024 16:42:48 -0300
Subject: [PATCH 028/108] feat: implement undo friendship on friendlist modal
---
.../user-friend-modal/user-friend-item.tsx | 61 ++++++++++---------
.../user-friend-modal-list.tsx | 29 +++++++--
src/types/index.ts | 2 +-
3 files changed, 57 insertions(+), 35 deletions(-)
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx
index c7beab23..7c34d040 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx
@@ -8,31 +8,28 @@ import cn from "classnames";
import { SPACING_UNIT } from "@renderer/theme.css";
import { useTranslation } from "react-i18next";
-export interface UserFriendItemProps {
+export type UserFriendItemProps = {
userId: string;
profileImageUrl: string | null;
displayName: string;
- type: "SENT" | "RECEIVED" | "ACCEPTED";
- onClickCancelRequest: (userId: string) => void;
- onClickAcceptRequest: (userId: string) => void;
- onClickRefuseRequest: (userId: string) => void;
onClickItem: (userId: string) => void;
-}
+} & (
+ | { type: "ACCEPTED"; onClickUndoFriendship: (userId: string) => void }
+ | {
+ type: "SENT" | "RECEIVED";
+ onClickCancelRequest: (userId: string) => void;
+ onClickAcceptRequest: (userId: string) => void;
+ onClickRefuseRequest: (userId: string) => void;
+ }
+ | { type: null }
+);
-export const UserFriendItem = ({
- userId,
- profileImageUrl,
- displayName,
- type,
- onClickCancelRequest,
- onClickAcceptRequest,
- onClickRefuseRequest,
- onClickItem,
-}: UserFriendItemProps) => {
+export const UserFriendItem = (props: UserFriendItemProps) => {
const { t } = useTranslation("user_profile");
+ const { userId, profileImageUrl, displayName, type, onClickItem } = props;
const getRequestDescription = () => {
- if (type === null) return null;
+ if (type === "ACCEPTED" || type === null) return null;
return (
@@ -42,11 +39,13 @@ export const UserFriendItem = ({
};
const getRequestActions = () => {
+ if (type === null) return null;
+
if (type === "SENT") {
return (
onClickCancelRequest(userId)}
+ onClick={() => props.onClickCancelRequest(userId)}
title={t("cancel_request")}
>
@@ -59,14 +58,14 @@ export const UserFriendItem = ({
<>
onClickAcceptRequest(userId)}
+ onClick={() => props.onClickAcceptRequest(userId)}
title={t("accept_request")}
>
onClickRefuseRequest(userId)}
+ onClick={() => props.onClickRefuseRequest(userId)}
title={t("ignore_request")}
>
@@ -75,15 +74,19 @@ export const UserFriendItem = ({
);
}
- return (
- onClickCancelRequest(userId)}
- title={t("undo_friendship")}
- >
-
-
- );
+ if (type === "ACCEPTED") {
+ return (
+ props.onClickUndoFriendship(userId)}
+ title={t("undo_friendship")}
+ >
+
+
+ );
+ }
+
+ return null;
};
return (
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx
index 2c639847..52e646e0 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx
@@ -3,6 +3,8 @@ import { UserFriend } from "@types";
import { useEffect, useState } from "react";
import { UserFriendItem } from "./user-friend-item";
import { useNavigate } from "react-router-dom";
+import { useToast, useUserDetails } from "@renderer/hooks";
+import { useTranslation } from "react-i18next";
export interface UserFriendModalListProps {
userId: string;
@@ -15,12 +17,17 @@ export const UserFriendModalList = ({
userId,
closeModal,
}: UserFriendModalListProps) => {
+ const { t } = useTranslation("user_profile");
+ const { showErrorToast } = useToast();
const navigate = useNavigate();
const [page, setPage] = useState(0);
const [maxPage, setMaxPage] = useState(0);
const [friends, setFriends] = useState([]);
+ const { userDetails, undoFriendship } = useUserDetails();
+ const isMe = userDetails?.id == userId;
+
const loadNextPage = () => {
if (page > maxPage) return;
window.electron
@@ -36,11 +43,15 @@ export const UserFriendModalList = ({
.catch(() => {});
};
- useEffect(() => {
+ const reloadList = () => {
setPage(0);
setMaxPage(0);
setFriends([]);
loadNextPage();
+ };
+
+ useEffect(() => {
+ reloadList();
}, [userId]);
const handleClickFriend = (userId: string) => {
@@ -48,6 +59,16 @@ export const UserFriendModalList = ({
navigate(`/user/${userId}`);
};
+ const handleUndoFriendship = (userId: string) => {
+ undoFriendship(userId)
+ .then(() => {
+ reloadList();
+ })
+ .catch(() => {
+ showErrorToast(t("try_again"));
+ });
+ };
+
return (
{}}
- onClickCancelRequest={() => {}}
- onClickRefuseRequest={() => {}}
onClickItem={handleClickFriend}
- type={"ACCEPTED"}
+ onClickUndoFriendship={handleUndoFriendship}
+ type={isMe ? "ACCEPTED" : null}
key={friend.id}
/>
);
diff --git a/src/types/index.ts b/src/types/index.ts
index 0945d410..ac352a91 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -301,7 +301,7 @@ export interface UserProfile {
id: string;
displayName: string;
profileImageUrl: string | null;
- profileVisibility: "PUBLIC" | "PRIVATE" | "FRIEND";
+ profileVisibility: "PUBLIC" | "PRIVATE" | "FRIENDS";
totalPlayTimeInSeconds: number;
libraryGames: UserGame[];
recentGames: UserGame[];
From ba8201dabac169f57dc09e33da2a0841843ba5d6 Mon Sep 17 00:00:00 2001
From: Bayu Wilanda
Date: Sat, 3 Aug 2024 09:50:22 +0700
Subject: [PATCH 029/108] feat: improve Indonesian translations for better
readability and naturalness
---
src/locales/id/translation.json | 247 ++++++++++++++++++++++++--------
1 file changed, 184 insertions(+), 63 deletions(-)
diff --git a/src/locales/id/translation.json b/src/locales/id/translation.json
index bb7f452b..bcf7c32a 100644
--- a/src/locales/id/translation.json
+++ b/src/locales/id/translation.json
@@ -1,134 +1,255 @@
{
+ "app": {
+ "successfully_signed_in": "Berhasil masuk"
+ },
"home": {
"featured": "Unggulan",
- "trending": "Trending",
- "surprise_me": "Kejutkan Saya",
- "no_results": "Tidak ada hasil"
+ "trending": "Sedang Tren",
+ "surprise_me": "Kejutkan saya",
+ "no_results": "Tidak ada hasil ditemukan"
},
"sidebar": {
"catalogue": "Katalog",
"downloads": "Unduhan",
"settings": "Pengaturan",
- "my_library": "Koleksi saya",
+ "my_library": "Perpustakaan saya",
"downloading_metadata": "{{title}} (Mengunduh metadata…)",
- "paused": "{{title}} (Terhenti)",
+ "paused": "{{title}} (Dijeda)",
"downloading": "{{title}} ({{percentage}} - Mengunduh…)",
- "filter": "Filter koleksi",
- "home": "Beranda"
+ "filter": "Filter perpustakaan",
+ "home": "Beranda",
+ "queued": "{{title}} (Antrian)",
+ "game_has_no_executable": "Game tidak punya file eksekusi yang dipilih",
+ "sign_in": "Masuk"
},
"header": {
- "search": "Pencarian",
+ "search": "Cari game",
"home": "Beranda",
"catalogue": "Katalog",
"downloads": "Unduhan",
"search_results": "Hasil pencarian",
- "settings": "Pengaturan"
+ "settings": "Pengaturan",
+ "version_available_install": "Versi {{version}} tersedia. Klik di sini untuk restart dan instal.",
+ "version_available_download": "Versi {{version}} tersedia. Klik di sini untuk unduh."
},
"bottom_panel": {
- "no_downloads_in_progress": "Tidak ada unduhan berjalan",
- "downloading_metadata": "Mengunduh metadata {{title}}...",
- "downloading": "Mengunduh {{title}}… ({{percentage}} selesai) - Perkiraan {{eta}} - {{speed}}"
+ "no_downloads_in_progress": "Tidak ada unduhan yang sedang berjalan",
+ "downloading_metadata": "Mengunduh metadata {{title}}…",
+ "downloading": "Mengunduh {{title}}… ({{percentage}} selesai) - Estimasi selesai {{eta}} - {{speed}}",
+ "calculating_eta": "Mengunduh {{title}}… ({{percentage}} selesai) - Menghitung waktu yang tersisa…",
+ "checking_files": "Memeriksa file {{title}}… ({{percentage}} selesai)"
},
"catalogue": {
- "next_page": "Halaman berikutnya",
- "previous_page": "Halaman sebelumnya"
+ "next_page": "Halaman Berikutnya",
+ "previous_page": "Halaman Sebelumnya"
},
"game_details": {
"open_download_options": "Buka opsi unduhan",
"download_options_zero": "Tidak ada opsi unduhan",
"download_options_one": "{{count}} opsi unduhan",
"download_options_other": "{{count}} opsi unduhan",
- "updated_at": "Diperbarui {{updated_at}}",
- "install": "Install",
+ "updated_at": "Diperbarui pada {{updated_at}}",
+ "install": "Instal",
"resume": "Lanjutkan",
- "pause": "Hentikan sementara",
- "cancel": "Batalkan",
+ "pause": "Jeda",
+ "cancel": "Batal",
"remove": "Hapus",
- "space_left_on_disk": "{{space}} tersisa pada disk",
- "eta": "Perkiraan {{eta}}",
+ "space_left_on_disk": "{{space}} tersisa di disk",
+ "eta": "Estimasi {{eta}}",
+ "calculating_eta": "Menghitung waktu yang tersisa…",
"downloading_metadata": "Mengunduh metadata…",
- "filter": "Saring repacks",
- "requirements": "Keperluan sistem",
+ "filter": "Filter repack",
+ "requirements": "Persyaratan sistem",
"minimum": "Minimum",
- "recommended": "Rekomendasi",
+ "recommended": "Dianjurkan",
+ "paused": "Dijeda",
"release_date": "Dirilis pada {{date}}",
- "publisher": "Dipublikasikan oleh {{publisher}}",
+ "publisher": "Diterbitkan oleh {{publisher}}",
"hours": "jam",
"minutes": "menit",
"amount_hours": "{{amount}} jam",
"amount_minutes": "{{amount}} menit",
"accuracy": "{{accuracy}}% akurasi",
- "add_to_library": "Tambahkan ke koleksi",
- "remove_from_library": "Hapus dari koleksi",
- "no_downloads": "Tidak ada unduhan tersedia",
+ "add_to_library": "Tambah ke perpustakaan",
+ "remove_from_library": "Hapus dari perpustakaan",
+ "no_downloads": "Tidak ada yang bisa diunduh",
"play_time": "Dimainkan selama {{amount}}",
"last_time_played": "Terakhir dimainkan {{period}}",
"not_played_yet": "Kamu belum memainkan {{title}}",
- "next_suggestion": "Rekomendasi berikutnya",
- "play": "Mainkan",
+ "next_suggestion": "Saran berikutnya",
+ "play": "Main",
"deleting": "Menghapus installer…",
"close": "Tutup",
- "playing_now": "Memainkan sekarang",
+ "playing_now": "Sedang dimainkan",
"change": "Ubah",
- "repacks_modal_description": "Pilih repack yang kamu ingin unduh",
- "select_folder_hint": "Untuk merubah folder bawaan, akses melalui",
- "download_now": "Unduh sekarang"
+ "repacks_modal_description": "Pilih repack yang ingin kamu unduh",
+ "select_folder_hint": "Untuk ganti folder default, buka <0>Pengaturan0>",
+ "download_now": "Unduh sekarang",
+ "no_shop_details": "Gagal mendapatkan detail toko.",
+ "download_options": "Opsi unduhan",
+ "download_path": "Path unduhan",
+ "previous_screenshot": "Screenshot sebelumnya",
+ "next_screenshot": "Screenshot berikutnya",
+ "screenshot": "Screenshot {{number}}",
+ "open_screenshot": "Buka screenshot {{number}}",
+ "download_settings": "Pengaturan unduhan",
+ "downloader": "Pengunduh",
+ "select_executable": "Pilih",
+ "no_executable_selected": "Tidak ada file eksekusi yang dipilih",
+ "open_folder": "Buka folder",
+ "open_download_location": "Lihat file yang diunduh",
+ "create_shortcut": "Buat pintasan desktop",
+ "remove_files": "Hapus file",
+ "remove_from_library_title": "Apa kamu yakin?",
+ "remove_from_library_description": "Ini akan menghapus {{game}} dari perpustakaan kamu",
+ "options": "Opsi",
+ "executable_section_title": "Eksekusi",
+ "executable_section_description": "Path file eksekusi saat \"Main\" diklik",
+ "downloads_secion_title": "Unduhan",
+ "downloads_section_description": "Cek update atau versi lain dari game ini",
+ "danger_zone_section_title": "Zona Berbahaya",
+ "danger_zone_section_description": "Hapus game ini dari perpustakaan kamu atau file yang diunduh oleh Hydra",
+ "download_in_progress": "Sedang mengunduh",
+ "download_paused": "Unduhan dijeda",
+ "last_downloaded_option": "Opsi terakhir diunduh",
+ "create_shortcut_success": "Pintasan berhasil dibuat",
+ "create_shortcut_error": "Gagal membuat pintasan"
},
"activation": {
- "title": "Aktivasi Hydra",
- "installation_id": "ID instalasi:",
- "enter_activation_code": "Masukkan kode aktivasi",
- "message": "Jika kamu tidak tau dimana bertanya untuk ini, maka kamu tidak seharusnya memiliki ini.",
+ "title": "Aktifkan Hydra",
+ "installation_id": "ID Instalasi:",
+ "enter_activation_code": "Masukkan kode aktivasi kamu",
+ "message": "Kalau tidak tahu harus tanya ke siapa, berarti kamu tidak perlu ini.",
"activate": "Aktifkan",
"loading": "Memuat…"
},
"downloads": {
"resume": "Lanjutkan",
- "pause": "Hentikan sementara",
- "eta": "Perkiraan {{eta}}",
- "paused": "Terhenti sementara",
- "verifying": "Memeriksa…",
+ "pause": "Jeda",
+ "eta": "Estimasi {{eta}}",
+ "paused": "Dijeda",
+ "verifying": "Verifikasi…",
"completed": "Selesai",
- "cancel": "Batalkan",
- "filter": "Saring game yang diunduh",
+ "removed": "Tidak diunduh",
+ "cancel": "Batal",
+ "filter": "Filter game yang diunduh",
"remove": "Hapus",
"downloading_metadata": "Mengunduh metadata…",
- "deleting": "Menghapus file instalasi…",
- "delete": "Hapus file instalasi",
- "delete_modal_title": "Kamu yakin?",
- "delete_modal_description": "Proses ini akan menghapus semua file instalasi dari komputer kamu",
- "install": "Install"
+ "deleting": "Menghapus installer…",
+ "delete": "Hapus installer",
+ "delete_modal_title": "Apa kamu yakin?",
+ "delete_modal_description": "Ini akan menghapus semua file instalasi dari komputer kamu",
+ "install": "Instal",
+ "download_in_progress": "Sedang berlangsung",
+ "queued_downloads": "Unduhan dalam antrian",
+ "downloads_completed": "Selesai",
+ "queued": "Dalam antrian",
+ "no_downloads_title": "Kosong",
+ "no_downloads_description": "Kamu belum mengunduh apa pun dengan Hydra, tapi belum terlambat untuk mulai.",
+ "checking_files": "Memeriksa file…"
},
"settings": {
- "downloads_path": "Lokasi unduhan",
- "change": "Perbarui",
- "notifications": "Pengingat",
+ "downloads_path": "Path unduhan",
+ "change": "Ganti",
+ "notifications": "Notifikasi",
"enable_download_notifications": "Saat unduhan selesai",
- "enable_repack_list_notifications": "Saat repack terbaru ditambahkan",
+ "enable_repack_list_notifications": "Saat ada repack baru",
+ "real_debrid_api_token_label": "Token API Real-Debrid",
+ "quit_app_instead_hiding": "Jangan sembunyikan Hydra saat ditutup",
+ "launch_with_system": "Jalankan Hydra saat sistem dinyalakan",
+ "general": "Umum",
"behavior": "Perilaku",
- "quit_app_instead_hiding": "Tutup aplikasi alih-alih menyembunyikan aplikasi",
- "launch_with_system": "Jalankan saat memulai sistem"
+ "download_sources": "Sumber unduhan",
+ "language": "Bahasa",
+ "real_debrid_api_token": "Token API",
+ "enable_real_debrid": "Aktifkan Real-Debrid",
+ "real_debrid_description": "Real-Debrid adalah downloader tanpa batas yang memungkinkan kamu untuk mengunduh file dengan cepat dan pada kecepatan terbaik dari Internet kamu.",
+ "real_debrid_invalid_token": "Token API tidak valid",
+ "real_debrid_api_token_hint": "Kamu bisa dapatkan token API di <0>sini0>",
+ "real_debrid_free_account_error": "Akun \"{{username}}\" adalah akun gratis. Silakan berlangganan Real-Debrid",
+ "real_debrid_linked_message": "Akun \"{{username}}\" terhubung",
+ "save_changes": "Simpan perubahan",
+ "changes_saved": "Perubahan disimpan berhasil",
+ "download_sources_description": "Hydra akan mencari link unduhan dari sini. URL harus menuju file .json dengan link unduhan.",
+ "validate_download_source": "Validasi",
+ "remove_download_source": "Hapus",
+ "add_download_source": "Tambahkan sumber",
+ "download_count_zero": "Tidak ada unduhan dalam daftar",
+ "download_count_one": "{{countFormatted}} unduhan dalam daftar",
+ "download_count_other": "{{countFormatted}} unduhan dalam daftar",
+ "download_options_zero": "Tidak ada unduhan tersedia",
+ "download_options_one": "{{countFormatted}} unduhan tersedia",
+ "download_options_other": "{{countFormatted}} unduhan tersedia",
+ "download_source_url": "URL sumber unduhan",
+ "add_download_source_description": "Masukkan URL yang berisi file .json",
+ "download_source_up_to_date": "Terkini",
+ "download_source_errored": "Terjadi kesalahan",
+ "sync_download_sources": "Sinkronkan sumber",
+ "removed_download_source": "Sumber unduhan dihapus",
+ "added_download_source": "Sumber unduhan ditambahkan",
+ "download_sources_synced": "Semua sumber unduhan disinkronkan",
+ "insert_valid_json_url": "Masukkan URL JSON yang valid",
+ "found_download_option_zero": "Tidak ada opsi unduhan ditemukan",
+ "found_download_option_one": "Ditemukan {{countFormatted}} opsi unduhan",
+ "found_download_option_other": "Ditemukan {{countFormatted}} opsi unduhan",
+ "import": "Impor"
},
"notifications": {
"download_complete": "Unduhan selesai",
- "game_ready_to_install": "{{title}} sudah siap untuk instalasi",
+ "game_ready_to_install": "{{title}} siap untuk diinstal",
"repack_list_updated": "Daftar repack diperbarui",
"repack_count_one": "{{count}} repack ditambahkan",
- "repack_count_other": "{{count}} repack ditambahkan"
+ "repack_count_other": "{{count}} repack ditambahkan",
+ "new_update_available": "Versi {{version}} tersedia",
+ "restart_to_install_update": "Restart Hydra untuk instal pembaruan"
},
"system_tray": {
"open": "Buka Hydra",
- "quit": "Tutup"
+ "quit": "Keluar"
},
"game_card": {
- "no_downloads": "Tidak ada unduhan tersedia"
+ "no_downloads": "Tidak ada unduhan yang tersedia"
},
"binary_not_found_modal": {
- "title": "Program tidak terinstal",
- "description": "Wine atau Lutris exe tidak ditemukan pada sistem kamu",
- "instructions": "Periksa cara instalasi yang benar pada Linux distro-mu agar game dapat dimainkan dengan benar"
+ "title": "Program tidak terpasang",
+ "description": "Executable Wine atau Lutris tidak ditemukan di sistem kamu",
+ "instructions": "Cek cara instalasi yang benar di distro Linux kamu agar game bisa jalan normal"
},
"modal": {
- "close": "Tombol tutup"
+ "close": "Tutup"
+ },
+ "forms": {
+ "toggle_password_visibility": "Tampilkan/Sembunyikan kata sandi"
+ },
+ "user_profile": {
+ "amount_hours": "{{amount}} jam",
+ "amount_minutes": "{{amount}} menit",
+ "last_time_played": "Terakhir dimainkan {{period}}",
+ "activity": "Aktivitas terbaru",
+ "library": "Perpustakaan",
+ "total_play_time": "Total waktu bermain: {{amount}}",
+ "no_recent_activity_title": "Hmm… kosong di sini",
+ "no_recent_activity_description": "Kamu belum main game baru-baru ini. Yuk, mulai main!",
+ "display_name": "Nama tampilan",
+ "saving": "Menyimpan",
+ "save": "Simpan",
+ "edit_profile": "Edit Profil",
+ "saved_successfully": "Berhasil disimpan",
+ "try_again": "Coba lagi yuk",
+ "sign_out_modal_title": "Apa kamu yakin?",
+ "cancel": "Batal",
+ "successfully_signed_out": "Berhasil keluar",
+ "sign_out": "Keluar",
+ "playing_for": "Bermain selama {{amount}}",
+ "sign_out_modal_text": "Perpustakaan kamu terhubung dengan akun saat ini. Saat keluar, perpustakaan kamu tidak akan terlihat lagi, dan progres tidak akan disimpan. Lanjutkan keluar?",
+ "add_friends": "Tambah Teman",
+ "add": "Tambah",
+ "friend_code": "Kode teman",
+ "see_profile": "Lihat profil",
+ "sending": "Mengirim",
+ "friend_request_sent": "Permintaan teman terkirim",
+ "friends": "Teman",
+ "friends_list": "Daftar teman",
+ "user_not_found": "Pengguna tidak ditemukan"
}
-}
+}
\ No newline at end of file
From 77280788a7147adb23cdc81f6b06edababe04213 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 5 Aug 2024 10:44:55 -0300
Subject: [PATCH 030/108] Format ca/translation.json
---
src/locales/ca/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/locales/ca/translation.json b/src/locales/ca/translation.json
index d3ac5dbe..9124af79 100644
--- a/src/locales/ca/translation.json
+++ b/src/locales/ca/translation.json
@@ -1,5 +1,5 @@
{
-"app": {
+ "app": {
"successfully_signed_in": "Has entrat correctament"
},
"home": {
From e851600814db1cbc492b16b75d53b0198f7f427d Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 5 Aug 2024 11:07:44 -0300
Subject: [PATCH 031/108] Format id/translation.json
---
src/locales/id/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/locales/id/translation.json b/src/locales/id/translation.json
index bcf7c32a..198aa568 100644
--- a/src/locales/id/translation.json
+++ b/src/locales/id/translation.json
@@ -252,4 +252,4 @@
"friends_list": "Daftar teman",
"user_not_found": "Pengguna tidak ditemukan"
}
-}
\ No newline at end of file
+}
From e1ef8a91934df6a0e0e2908740fc7595ab25baa9 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 5 Aug 2024 15:21:05 -0300
Subject: [PATCH 032/108] feat: pass headers correctly to downloadURL
---
src/main/events/auth/sign-out.ts | 2 ++
src/main/services/download/http-download.ts | 9 ++++-----
src/main/services/hydra-api.ts | 8 ++++++++
yarn.lock | 8 ++++----
4 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/src/main/events/auth/sign-out.ts b/src/main/events/auth/sign-out.ts
index fe640b9d..9998c733 100644
--- a/src/main/events/auth/sign-out.ts
+++ b/src/main/events/auth/sign-out.ts
@@ -26,6 +26,8 @@ const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
/* Disconnects libtorrent */
PythonInstance.killTorrent();
+ HydraApi.handleSignOut();
+
await Promise.all([
databaseOperations,
HydraApi.post("/auth/logout").catch(() => {}),
diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts
index d9c36916..d74e862e 100644
--- a/src/main/services/download/http-download.ts
+++ b/src/main/services/download/http-download.ts
@@ -45,15 +45,14 @@ export class HTTPDownload {
headers?: Record
) {
return new Promise((resolve) => {
- WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, {
- headers,
- });
+ const options = headers ? { headers } : {};
+ WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, options);
+
+ const gid = ++this.id;
WindowManager.mainWindow?.webContents.session.on(
"will-download",
(_event, item, _webContents) => {
- const gid = ++this.id;
-
this.downloads[gid.toString()] = item;
// Set the save path, making Electron not to prompt a save dialog.
diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts
index 5365bd9e..120d27ac 100644
--- a/src/main/services/hydra-api.ts
+++ b/src/main/services/hydra-api.ts
@@ -64,6 +64,14 @@ export class HydraApi {
}
}
+ static handleSignOut() {
+ this.userAuth = {
+ authToken: "",
+ refreshToken: "",
+ expirationTimestamp: 0,
+ };
+ }
+
static async setupApi() {
this.instance = axios.create({
baseURL: import.meta.env.MAIN_VITE_API_URL,
diff --git a/yarn.lock b/yarn.lock
index c2d49574..2dd42cb8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3751,10 +3751,10 @@ electron-vite@^2.0.0:
magic-string "^0.30.5"
picocolors "^1.0.0"
-electron@^30.1.0:
- version "30.3.0"
- resolved "https://registry.yarnpkg.com/electron/-/electron-30.3.0.tgz#9d7af06c11242d5c6ca5b9920c9dc49feab90299"
- integrity sha512-/rWPcpCL4sYCUm1bY8if1dO8nyFTwXlPUP0dpL3ir5iLK/9NshN6lIJ8xceEY8CEYVLMIYRkxXb44Q9cdrjtOQ==
+electron@^30.3.0:
+ version "30.3.1"
+ resolved "https://registry.yarnpkg.com/electron/-/electron-30.3.1.tgz#fe27ca2a4739bec832b2edd6f46140ab46bf53a0"
+ integrity sha512-Ai/OZ7VlbFAVYMn9J5lyvtr+ZWyEbXDVd5wBLb5EVrp4352SRmMAmN5chcIe3n9mjzcgehV9n4Hwy15CJW+YbA==
dependencies:
"@electron/get" "^2.0.0"
"@types/node" "^20.9.0"
From fab248de99c593d2ffbf85c2a7c567061ef6cb5b Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 5 Aug 2024 18:55:35 -0300
Subject: [PATCH 033/108] fix: duplicate download with real debrid
---
src/main/services/download/real-debrid-downloader.ts | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts
index 034ffc49..4877e6e2 100644
--- a/src/main/services/download/real-debrid-downloader.ts
+++ b/src/main/services/download/real-debrid-downloader.ts
@@ -131,11 +131,9 @@ export class RealDebridDownloader {
}
static async startDownload(game: Game) {
- this.downloadingGame = game;
-
if (this.downloads.has(game.id)) {
await this.resumeDownload(game.id!);
-
+ this.downloadingGame = game;
return;
}
@@ -156,6 +154,7 @@ export class RealDebridDownloader {
);
this.downloads.set(game.id!, gid);
+ this.downloadingGame = game;
}
}
From 19b1d29713e57ec3d2229b4f7f11da24c5a44d69 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 5 Aug 2024 19:15:36 -0300
Subject: [PATCH 034/108] fix: reset downloadingGame and torrentId on
cancelDownload
---
src/main/services/download/http-download.ts | 2 +-
src/main/services/download/real-debrid-downloader.ts | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts
index d74e862e..df1151d5 100644
--- a/src/main/services/download/http-download.ts
+++ b/src/main/services/download/http-download.ts
@@ -24,7 +24,7 @@ export class HTTPDownload {
}
static async cancelDownload(gid: string) {
- const downloadItem: DownloadItem = this.downloads[gid];
+ const downloadItem = this.downloads[gid];
downloadItem?.cancel();
delete this.downloads[gid];
}
diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts
index 4877e6e2..ee5ee881 100644
--- a/src/main/services/download/real-debrid-downloader.ts
+++ b/src/main/services/download/real-debrid-downloader.ts
@@ -165,6 +165,9 @@ export class RealDebridDownloader {
await HTTPDownload.cancelDownload(gid);
this.downloads.delete(gameId);
}
+
+ this.realDebridTorrentId = null;
+ this.downloadingGame = null;
}
static async resumeDownload(gameId: number) {
From 4d60317475285a49dea1435761eacc017601f456 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 5 Aug 2024 19:39:55 -0300
Subject: [PATCH 035/108] fix: passing complete download path to setSavePath
---
src/main/services/download/http-download.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts
index df1151d5..81f6b5fd 100644
--- a/src/main/services/download/http-download.ts
+++ b/src/main/services/download/http-download.ts
@@ -1,5 +1,6 @@
import { DownloadItem } from "electron";
import { WindowManager } from "../window-manager";
+import path from "node:path";
export class HTTPDownload {
private static id = 0;
@@ -56,7 +57,7 @@ export class HTTPDownload {
this.downloads[gid.toString()] = item;
// Set the save path, making Electron not to prompt a save dialog.
- item.setSavePath(downloadPath);
+ item.setSavePath(path.join(downloadPath, item.getFilename()));
resolve(gid.toString());
}
From 4b7a0ff4023cc53dc7e80e6cc70d3cd3b6a3308f Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 5 Aug 2024 19:48:43 -0300
Subject: [PATCH 036/108] feat: setting folder name
---
src/main/services/download/generic-http-downloader.ts | 1 +
src/main/services/download/http-download.ts | 2 ++
src/main/services/download/real-debrid-downloader.ts | 1 +
3 files changed, 4 insertions(+)
diff --git a/src/main/services/download/generic-http-downloader.ts b/src/main/services/download/generic-http-downloader.ts
index c078be80..4977d503 100644
--- a/src/main/services/download/generic-http-downloader.ts
+++ b/src/main/services/download/generic-http-downloader.ts
@@ -24,6 +24,7 @@ export class GenericHTTPDownloader {
fileSize: Number(status.totalLength),
progress,
status: "active",
+ folderName: status.folderName,
}
);
diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts
index 81f6b5fd..fb573ff4 100644
--- a/src/main/services/download/http-download.ts
+++ b/src/main/services/download/http-download.ts
@@ -11,6 +11,7 @@ export class HTTPDownload {
completedLength: number;
totalLength: number;
downloadSpeed: number;
+ folderName: string;
} | null {
const downloadItem = this.downloads[gid];
if (downloadItem) {
@@ -18,6 +19,7 @@ export class HTTPDownload {
completedLength: downloadItem.getReceivedBytes(),
totalLength: downloadItem.getTotalBytes(),
downloadSpeed: downloadItem.getCurrentBytesPerSecond(),
+ folderName: downloadItem.getFilename(),
};
}
diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts
index ee5ee881..fc32263c 100644
--- a/src/main/services/download/real-debrid-downloader.ts
+++ b/src/main/services/download/real-debrid-downloader.ts
@@ -62,6 +62,7 @@ export class RealDebridDownloader {
fileSize: Number(status.totalLength),
progress,
status: "active",
+ folderName: status.folderName,
}
);
From f3f78248efc3a67e19b3dcd098fa94b1246fde00 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 5 Aug 2024 19:49:10 -0300
Subject: [PATCH 037/108] fix: start another download after finishing one
---
src/main/services/download/real-debrid-downloader.ts | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts
index fc32263c..f61cb1ff 100644
--- a/src/main/services/download/real-debrid-downloader.ts
+++ b/src/main/services/download/real-debrid-downloader.ts
@@ -122,9 +122,11 @@ export class RealDebridDownloader {
}
static async pauseDownload() {
- const gid = this.downloads.get(this.downloadingGame!.id!);
- if (gid) {
- await HTTPDownload.pauseDownload(gid);
+ if (this.downloadingGame) {
+ const gid = this.downloads.get(this.downloadingGame.id);
+ if (gid) {
+ await HTTPDownload.pauseDownload(gid);
+ }
}
this.realDebridTorrentId = null;
From 4a149aa62d733f85f11343c2cf4da175c38e332a Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 5 Aug 2024 20:47:27 -0300
Subject: [PATCH 038/108] feat: remove aria2 (again)
---
.gitignore | 1 -
electron-builder.yml | 1 -
package.json | 3 +-
postinstall.cjs | 50 -----------------------
src/main/declaration.d.ts | 80 -------------------------------------
src/main/services/aria2c.ts | 20 ----------
yarn.lock | 15 +------
7 files changed, 2 insertions(+), 168 deletions(-)
delete mode 100644 postinstall.cjs
delete mode 100644 src/main/declaration.d.ts
delete mode 100644 src/main/services/aria2c.ts
diff --git a/.gitignore b/.gitignore
index 7a6496a5..fb4badd7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
.vscode
node_modules
hydra-download-manager/
-aria2/
fastlist.exe
__pycache__
dist
diff --git a/electron-builder.yml b/electron-builder.yml
index be300d36..cfdafe7d 100644
--- a/electron-builder.yml
+++ b/electron-builder.yml
@@ -3,7 +3,6 @@ productName: Hydra
directories:
buildResources: build
extraResources:
- - aria2
- hydra-download-manager
- seeds
- from: node_modules/create-desktop-shortcuts/src/windows.vbs
diff --git a/package.json b/package.json
index 4693f65b..54d689b2 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
"start": "electron-vite preview",
"dev": "electron-vite dev",
"build": "npm run typecheck && electron-vite build",
- "postinstall": "electron-builder install-app-deps && node ./postinstall.cjs",
+ "postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir",
"build:win": "electron-vite build && electron-builder --win",
"build:mac": "electron-vite build && electron-builder --mac",
@@ -42,7 +42,6 @@
"@vanilla-extract/css": "^1.14.2",
"@vanilla-extract/dynamic": "^2.1.1",
"@vanilla-extract/recipes": "^0.5.2",
- "aria2": "^4.1.2",
"auto-launch": "^5.0.6",
"axios": "^1.6.8",
"better-sqlite3": "^9.5.0",
diff --git a/postinstall.cjs b/postinstall.cjs
deleted file mode 100644
index 547af988..00000000
--- a/postinstall.cjs
+++ /dev/null
@@ -1,50 +0,0 @@
-const { default: axios } = require("axios");
-const util = require("node:util");
-const fs = require("node:fs");
-
-const exec = util.promisify(require("node:child_process").exec);
-
-const downloadAria2 = async () => {
- if (fs.existsSync("aria2")) {
- console.log("Aria2 already exists, skipping download...");
- return;
- }
-
- const file =
- process.platform === "win32"
- ? "aria2-1.37.0-win-64bit-build1.zip"
- : "aria2-1.37.0-1-x86_64.pkg.tar.zst";
-
- const downloadUrl =
- process.platform === "win32"
- ? `https://github.com/aria2/aria2/releases/download/release-1.37.0/${file}`
- : "https://archlinux.org/packages/extra/x86_64/aria2/download/";
-
- console.log(`Downloading ${file}...`);
-
- const response = await axios.get(downloadUrl, { responseType: "stream" });
-
- const stream = response.data.pipe(fs.createWriteStream(file));
-
- stream.on("finish", async () => {
- console.log(`Downloaded ${file}, extracting...`);
-
- if (process.platform === "win32") {
- await exec(`npx extract-zip ${file}`);
- console.log("Extracted. Renaming folder...");
-
- fs.renameSync(file.replace(".zip", ""), "aria2");
- } else {
- await exec(`tar --zstd -xvf ${file} usr/bin/aria2c`);
- console.log("Extracted. Copying binary file...");
- fs.mkdirSync("aria2");
- fs.copyFileSync("usr/bin/aria2c", "aria2/aria2c");
- fs.rmSync("usr", { recursive: true });
- }
-
- console.log(`Extracted ${file}, removing compressed downloaded file...`);
- fs.rmSync(file);
- });
-};
-
-downloadAria2();
diff --git a/src/main/declaration.d.ts b/src/main/declaration.d.ts
deleted file mode 100644
index ac2675a3..00000000
--- a/src/main/declaration.d.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-declare module "aria2" {
- export type Aria2Status =
- | "active"
- | "waiting"
- | "paused"
- | "error"
- | "complete"
- | "removed";
-
- export interface StatusResponse {
- gid: string;
- status: Aria2Status;
- totalLength: string;
- completedLength: string;
- uploadLength: string;
- bitfield: string;
- downloadSpeed: string;
- uploadSpeed: string;
- infoHash?: string;
- numSeeders?: string;
- seeder?: boolean;
- pieceLength: string;
- numPieces: string;
- connections: string;
- errorCode?: string;
- errorMessage?: string;
- followedBy?: string[];
- following: string;
- belongsTo: string;
- dir: string;
- files: {
- path: string;
- length: string;
- completedLength: string;
- selected: string;
- }[];
- bittorrent?: {
- announceList: string[][];
- comment: string;
- creationDate: string;
- mode: "single" | "multi";
- info: {
- name: string;
- verifiedLength: string;
- verifyIntegrityPending: string;
- };
- };
- }
-
- export default class Aria2 {
- constructor(options: any);
- open: () => Promise;
- call(
- method: "addUri",
- uris: string[],
- options: { dir: string }
- ): Promise;
- call(
- method: "tellStatus",
- gid: string,
- keys?: string[]
- ): Promise;
- call(method: "pause", gid: string): Promise;
- call(method: "forcePause", gid: string): Promise;
- call(method: "unpause", gid: string): Promise;
- call(method: "remove", gid: string): Promise;
- call(method: "forceRemove", gid: string): Promise;
- call(method: "pauseAll"): Promise;
- call(method: "forcePauseAll"): Promise;
- listNotifications: () => [
- "onDownloadStart",
- "onDownloadPause",
- "onDownloadStop",
- "onDownloadComplete",
- "onDownloadError",
- "onBtDownloadComplete",
- ];
- on: (event: string, callback: (params: any) => void) => void;
- }
-}
diff --git a/src/main/services/aria2c.ts b/src/main/services/aria2c.ts
deleted file mode 100644
index b1b1da76..00000000
--- a/src/main/services/aria2c.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import path from "node:path";
-import { spawn } from "node:child_process";
-import { app } from "electron";
-
-export const startAria2 = () => {
- const binaryPath = app.isPackaged
- ? path.join(process.resourcesPath, "aria2", "aria2c")
- : path.join(__dirname, "..", "..", "aria2", "aria2c");
-
- return spawn(
- binaryPath,
- [
- "--enable-rpc",
- "--rpc-listen-all",
- "--file-allocation=none",
- "--allow-overwrite=true",
- ],
- { stdio: "inherit", windowsHide: true }
- );
-};
diff --git a/yarn.lock b/yarn.lock
index 2dd42cb8..cf749216 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2677,14 +2677,6 @@ aria-query@^5.3.0:
dependencies:
dequal "^2.0.3"
-aria2@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/aria2/-/aria2-4.1.2.tgz#0ecbc50beea82856c88b4de71dac336154f67362"
- integrity sha512-qTBr2RY8RZQmiUmbj2KXFvkErNxU4aTHZszszzwhE8svy2PEVX+IYR/c4Rp9Tuw4QkeU8cylGy6McV6Yl8i7Qw==
- dependencies:
- node-fetch "^2.6.1"
- ws "^7.4.0"
-
array-buffer-byte-length@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz"
@@ -5833,7 +5825,7 @@ node-domexception@^1.0.0:
resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz"
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
-node-fetch@^2.6.1, node-fetch@^2.6.7:
+node-fetch@^2.6.7:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
@@ -7629,11 +7621,6 @@ wrappy@1:
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
-ws@^7.4.0:
- version "7.5.10"
- resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9"
- integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==
-
ws@^8.16.0:
version "8.17.0"
resolved "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz"
From e44c15e9e1442d686507d402d0e510aff2b29bf3 Mon Sep 17 00:00:00 2001
From: Bayu Wilanda
Date: Tue, 6 Aug 2024 11:11:05 +0700
Subject: [PATCH 039/108] fix: video won't load due to Content Security Policy
restrictions
---
src/renderer/index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/renderer/index.html b/src/renderer/index.html
index 543b85a9..d7abf3ad 100644
--- a/src/renderer/index.html
+++ b/src/renderer/index.html
@@ -6,7 +6,7 @@
Hydra
From 6806787ca0e2cb4d4eeec2a68930f29ccd06732b Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Tue, 6 Aug 2024 23:17:12 -0300
Subject: [PATCH 040/108] feat: set profile visibility
---
src/locales/en/translation.json | 7 +-
src/locales/pt/translation.json | 7 +-
src/main/events/index.ts | 6 +-
src/main/events/profile/update-profile.ts | 29 ++--
src/main/helpers/index.ts | 3 +
src/preload/index.ts | 5 +-
src/renderer/src/declaration.d.ts | 5 +-
src/renderer/src/hooks/use-user-details.ts | 16 +-
src/renderer/src/pages/user/user-content.tsx | 15 +-
.../src/pages/user/user-edit-modal.tsx | 147 -----------------
.../user-profile-settings-modal/index.tsx | 1 +
.../user-block-list.tsx | 0
.../user-edit-profile.tsx | 150 ++++++++++++++++++
.../user-profile-settings-modal.tsx | 72 +++++++++
src/types/index.ts | 7 +
15 files changed, 276 insertions(+), 194 deletions(-)
delete mode 100644 src/renderer/src/pages/user/user-edit-modal.tsx
create mode 100644 src/renderer/src/pages/user/user-profile-settings-modal/index.tsx
create mode 100644 src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
create mode 100644 src/renderer/src/pages/user/user-profile-settings-modal/user-edit-profile.tsx
create mode 100644 src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index b24509d3..631e50c1 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -261,6 +261,11 @@
"undo_friendship": "Undo friendship",
"request_accepted": "Request accepted",
"user_blocked_successfully": "User blocked successfully",
- "user_block_modal_text": "This will block {{displayName}}"
+ "user_block_modal_text": "This will block {{displayName}}",
+ "settings": "Settings",
+ "public": "Public",
+ "private": "Private",
+ "friends_only": "Friends only",
+ "privacy": "Privacy"
}
}
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index ef94c31f..5b24b574 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -261,6 +261,11 @@
"undo_friendship": "Desfazer amizade",
"request_accepted": "Pedido de amizade aceito",
"user_blocked_successfully": "Usuário bloqueado com sucesso",
- "user_block_modal_text": "Bloquear {{displayName}}"
+ "user_block_modal_text": "Bloquear {{displayName}}",
+ "settings": "Configurações",
+ "privacy": "Privacidade",
+ "private": "Privado",
+ "friends_only": "Apenas amigos",
+ "public": "Público"
}
}
diff --git a/src/main/events/index.ts b/src/main/events/index.ts
index 57daf51c..9cdc58b5 100644
--- a/src/main/events/index.ts
+++ b/src/main/events/index.ts
@@ -52,11 +52,9 @@ import "./profile/undo-friendship";
import "./profile/update-friend-request";
import "./profile/update-profile";
import "./profile/send-friend-request";
+import { isPortableVersion } from "@main/helpers";
ipcMain.handle("ping", () => "pong");
ipcMain.handle("getVersion", () => app.getVersion());
-ipcMain.handle(
- "isPortableVersion",
- () => process.env.PORTABLE_EXECUTABLE_FILE != null
-);
+ipcMain.handle("isPortableVersion", () => isPortableVersion());
ipcMain.handle("getDefaultDownloadsPath", () => defaultDownloadsPath);
diff --git a/src/main/events/profile/update-profile.ts b/src/main/events/profile/update-profile.ts
index 8620eaa1..50d2ab66 100644
--- a/src/main/events/profile/update-profile.ts
+++ b/src/main/events/profile/update-profile.ts
@@ -4,33 +4,22 @@ import axios from "axios";
import fs from "node:fs";
import path from "node:path";
import { fileTypeFromFile } from "file-type";
-import { UserProfile } from "@types";
+import { UpdateProfileProps, UserProfile } from "@types";
-const patchUserProfile = async (
- displayName: string,
- profileImageUrl?: string
-) => {
- if (profileImageUrl) {
- return HydraApi.patch("/profile", {
- displayName,
- profileImageUrl,
- });
- } else {
- return HydraApi.patch("/profile", {
- displayName,
- });
- }
+const patchUserProfile = async (updateProfile: UpdateProfileProps) => {
+ return HydraApi.patch("/profile", updateProfile);
};
const updateProfile = async (
_event: Electron.IpcMainInvokeEvent,
- displayName: string,
- newProfileImagePath: string | null
+ updateProfile: UpdateProfileProps
): Promise => {
- if (!newProfileImagePath) {
- return patchUserProfile(displayName);
+ if (!updateProfile.profileImageUrl) {
+ return patchUserProfile(updateProfile);
}
+ const newProfileImagePath = updateProfile.profileImageUrl;
+
const stats = fs.statSync(newProfileImagePath);
const fileBuffer = fs.readFileSync(newProfileImagePath);
const fileSizeInBytes = stats.size;
@@ -53,7 +42,7 @@ const updateProfile = async (
})
.catch(() => undefined);
- return patchUserProfile(displayName, profileImageUrl);
+ return patchUserProfile({ ...updateProfile, profileImageUrl });
};
registerEvent("updateProfile", updateProfile);
diff --git a/src/main/helpers/index.ts b/src/main/helpers/index.ts
index 902b927d..f2b86e5a 100644
--- a/src/main/helpers/index.ts
+++ b/src/main/helpers/index.ts
@@ -57,4 +57,7 @@ export const requestWebPage = async (url: string) => {
.then((response) => response.data);
};
+export const isPortableVersion = () =>
+ process.env.PORTABLE_EXECUTABLE_FILE != null;
+
export * from "./download-source";
diff --git a/src/preload/index.ts b/src/preload/index.ts
index 3350a340..84100cab 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -10,6 +10,7 @@ import type {
StartGameDownloadPayload,
GameRunning,
FriendRequestAction,
+ UpdateProfileProps,
} from "@types";
contextBridge.exposeInMainWorld("electron", {
@@ -137,8 +138,8 @@ contextBridge.exposeInMainWorld("electron", {
getMe: () => ipcRenderer.invoke("getMe"),
undoFriendship: (userId: string) =>
ipcRenderer.invoke("undoFriendship", userId),
- updateProfile: (displayName: string, newProfileImagePath: string | null) =>
- ipcRenderer.invoke("updateProfile", displayName, newProfileImagePath),
+ updateProfile: (updateProfile: UpdateProfileProps) =>
+ ipcRenderer.invoke("updateProfile", updateProfile),
getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"),
updateFriendRequest: (userId: string, action: FriendRequestAction) =>
ipcRenderer.invoke("updateFriendRequest", userId, action),
diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts
index e022cffe..3e954d40 100644
--- a/src/renderer/src/declaration.d.ts
+++ b/src/renderer/src/declaration.d.ts
@@ -139,10 +139,7 @@ declare global {
/* Profile */
getMe: () => Promise;
undoFriendship: (userId: string) => Promise;
- updateProfile: (
- displayName: string,
- newProfileImagePath: string | null
- ) => Promise;
+ updateProfile: (updateProfile: UpdateProfileProps) => Promise;
getFriendRequests: () => Promise;
updateFriendRequest: (
userId: string,
diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts
index 21690e7e..e33de978 100644
--- a/src/renderer/src/hooks/use-user-details.ts
+++ b/src/renderer/src/hooks/use-user-details.ts
@@ -8,8 +8,9 @@ import {
setFriendsModalHidden,
} from "@renderer/features";
import { profileBackgroundFromProfileImage } from "@renderer/helpers";
-import { FriendRequestAction, UserDetails } from "@types";
+import { FriendRequestAction, UpdateProfileProps, UserDetails } from "@types";
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
+import { logger } from "@renderer/logger";
export function useUserDetails() {
const dispatch = useAppDispatch();
@@ -43,7 +44,10 @@ export function useUserDetails() {
if (userDetails.profileImageUrl) {
const profileBackground = await profileBackgroundFromProfileImage(
userDetails.profileImageUrl
- );
+ ).catch((err) => {
+ logger.error("profileBackgroundFromProfileImage", err);
+ return `#151515B3`;
+ });
dispatch(setProfileBackground(profileBackground));
window.localStorage.setItem(
@@ -74,12 +78,8 @@ export function useUserDetails() {
}, [clearUserDetails]);
const patchUser = useCallback(
- async (displayName: string, imageProfileUrl: string | null) => {
- const response = await window.electron.updateProfile(
- displayName,
- imageProfileUrl
- );
-
+ async (props: UpdateProfileProps) => {
+ const response = await window.electron.updateProfile(props);
return updateUserDetails(response);
},
[updateUserDetails]
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index 81db119d..7367ed5f 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -25,7 +25,7 @@ import {
XCircleIcon,
} from "@primer/octicons-react";
import { Button, Link } from "@renderer/components";
-import { UserEditProfileModal } from "./user-edit-modal";
+import { UserProfileSettingsModal } from "./user-profile-settings-modal";
import { UserSignOutModal } from "./user-sign-out-modal";
import { UserFriendModalTab } from "../shared-modals/user-friend-modal";
import { UserBlockModal } from "./user-block-modal";
@@ -60,7 +60,8 @@ export function UserContent({
const [profileContentBoxBackground, setProfileContentBoxBackground] =
useState();
- const [showEditProfileModal, setShowEditProfileModal] = useState(false);
+ const [showProfileSettingsModal, setShowProfileSettingsModal] =
+ useState(false);
const [showSignOutModal, setShowSignOutModal] = useState(false);
const [showUserBlockModal, setShowUserBlockModal] = useState(false);
@@ -95,7 +96,7 @@ export function UserContent({
};
const handleEditProfile = () => {
- setShowEditProfileModal(true);
+ setShowProfileSettingsModal(true);
};
const handleOnClickFriend = (userId: string) => {
@@ -165,7 +166,7 @@ export function UserContent({
return (
<>
- {t("edit_profile")}
+ {t("settings")}
setShowSignOutModal(true)}>
@@ -251,9 +252,9 @@ export function UserContent({
return (
<>
- setShowEditProfileModal(false)}
+ setShowProfileSettingsModal(false)}
updateUserProfile={updateUserProfile}
userProfile={userProfile}
/>
diff --git a/src/renderer/src/pages/user/user-edit-modal.tsx b/src/renderer/src/pages/user/user-edit-modal.tsx
deleted file mode 100644
index a22650ee..00000000
--- a/src/renderer/src/pages/user/user-edit-modal.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-import { Button, Modal, TextField } from "@renderer/components";
-import { UserProfile } from "@types";
-import * as styles from "./user.css";
-import { DeviceCameraIcon, PersonIcon } from "@primer/octicons-react";
-import { SPACING_UNIT } from "@renderer/theme.css";
-import { useEffect, useMemo, useState } from "react";
-import { useToast, useUserDetails } from "@renderer/hooks";
-import { useTranslation } from "react-i18next";
-
-export interface UserEditProfileModalProps {
- userProfile: UserProfile;
- visible: boolean;
- onClose: () => void;
- updateUserProfile: () => Promise;
-}
-
-export const UserEditProfileModal = ({
- userProfile,
- visible,
- onClose,
- updateUserProfile,
-}: UserEditProfileModalProps) => {
- const { t } = useTranslation("user_profile");
-
- const [displayName, setDisplayName] = useState("");
- const [newImagePath, setNewImagePath] = useState(null);
- const [isSaving, setIsSaving] = useState(false);
-
- const { patchUser } = useUserDetails();
-
- const { showSuccessToast, showErrorToast } = useToast();
-
- useEffect(() => {
- setDisplayName(userProfile.displayName);
- }, [userProfile.displayName]);
-
- const handleChangeProfileAvatar = async () => {
- const { filePaths } = await window.electron.showOpenDialog({
- properties: ["openFile"],
- filters: [
- {
- name: "Image",
- extensions: ["jpg", "jpeg", "png", "webp"],
- },
- ],
- });
-
- if (filePaths && filePaths.length > 0) {
- const path = filePaths[0];
-
- setNewImagePath(path);
- }
- };
-
- const handleSaveProfile: React.FormEventHandler = async (
- event
- ) => {
- event.preventDefault();
- setIsSaving(true);
-
- patchUser(displayName, newImagePath)
- .then(async () => {
- await updateUserProfile();
- showSuccessToast(t("saved_successfully"));
- cleanFormAndClose();
- })
- .catch(() => {
- showErrorToast(t("try_again"));
- })
- .finally(() => {
- setIsSaving(false);
- });
- };
-
- const resetModal = () => {
- setDisplayName(userProfile.displayName);
- setNewImagePath(null);
- };
-
- const cleanFormAndClose = () => {
- resetModal();
- onClose();
- };
-
- const avatarUrl = useMemo(() => {
- if (newImagePath) return `local:${newImagePath}`;
- if (userProfile.profileImageUrl) return userProfile.profileImageUrl;
- return null;
- }, [newImagePath, userProfile.profileImageUrl]);
-
- return (
- <>
-
-
-
- >
- );
-};
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/index.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/index.tsx
new file mode 100644
index 00000000..896d3684
--- /dev/null
+++ b/src/renderer/src/pages/user/user-profile-settings-modal/index.tsx
@@ -0,0 +1 @@
+export * from "./user-profile-settings-modal";
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
new file mode 100644
index 00000000..e69de29b
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/user-edit-profile.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/user-edit-profile.tsx
new file mode 100644
index 00000000..aa78c1ea
--- /dev/null
+++ b/src/renderer/src/pages/user/user-profile-settings-modal/user-edit-profile.tsx
@@ -0,0 +1,150 @@
+import { DeviceCameraIcon, PersonIcon } from "@primer/octicons-react";
+import { Button, SelectField, TextField } from "@renderer/components";
+import { useToast, useUserDetails } from "@renderer/hooks";
+import { UserProfile } from "@types";
+import { useEffect, useMemo, useState } from "react";
+import { useTranslation } from "react-i18next";
+import * as styles from "../user.css";
+import { SPACING_UNIT } from "@renderer/theme.css";
+
+export interface UserEditProfileProps {
+ userProfile: UserProfile;
+ updateUserProfile: () => Promise;
+}
+
+export const UserEditProfile = ({
+ userProfile,
+ updateUserProfile,
+}: UserEditProfileProps) => {
+ const { t } = useTranslation("user_profile");
+
+ const [form, setForm] = useState({
+ displayName: userProfile.displayName,
+ profileVisibility: userProfile.profileVisibility,
+ imageProfileUrl: null as string | null,
+ });
+ const [isSaving, setIsSaving] = useState(false);
+
+ const { patchUser } = useUserDetails();
+
+ const { showSuccessToast, showErrorToast } = useToast();
+
+ const [profileVisibilityOptions, setProfileVisibilityOptions] = useState<
+ { value: string; label: string }[]
+ >([]);
+
+ useEffect(() => {
+ setProfileVisibilityOptions([
+ { value: "PUBLIC", label: t("public") },
+ { value: "FRIENDS", label: t("friends_only") },
+ { value: "PRIVATE", label: t("private") },
+ ]);
+ }, [t]);
+
+ const handleChangeProfileAvatar = async () => {
+ const { filePaths } = await window.electron.showOpenDialog({
+ properties: ["openFile"],
+ filters: [
+ {
+ name: "Image",
+ extensions: ["jpg", "jpeg", "png", "webp"],
+ },
+ ],
+ });
+
+ if (filePaths && filePaths.length > 0) {
+ const path = filePaths[0];
+
+ setForm({ ...form, imageProfileUrl: path });
+ }
+ };
+
+ const handleProfileVisibilityChange = (event) => {
+ setForm({
+ ...form,
+ profileVisibility: event.target.value,
+ });
+ };
+
+ const handleSaveProfile: React.FormEventHandler = async (
+ event
+ ) => {
+ event.preventDefault();
+ setIsSaving(true);
+
+ patchUser(form)
+ .then(async () => {
+ await updateUserProfile();
+ showSuccessToast(t("saved_successfully"));
+ })
+ .catch(() => {
+ showErrorToast(t("try_again"));
+ })
+ .finally(() => {
+ setIsSaving(false);
+ });
+ };
+
+ const avatarUrl = useMemo(() => {
+ if (form.imageProfileUrl) return `local:${form.imageProfileUrl}`;
+ if (userProfile.profileImageUrl) return userProfile.profileImageUrl;
+ return null;
+ }, [form, userProfile]);
+
+ return (
+
+ );
+};
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
new file mode 100644
index 00000000..5615cf12
--- /dev/null
+++ b/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
@@ -0,0 +1,72 @@
+import { Button, Modal } from "@renderer/components";
+import { UserProfile } from "@types";
+import { SPACING_UNIT } from "@renderer/theme.css";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+import { UserEditProfile } from "./user-edit-profile";
+
+export interface UserEditProfileModalProps {
+ userProfile: UserProfile;
+ visible: boolean;
+ onClose: () => void;
+ updateUserProfile: () => Promise;
+}
+
+export const UserProfileSettingsModal = ({
+ userProfile,
+ visible,
+ onClose,
+ updateUserProfile,
+}: UserEditProfileModalProps) => {
+ const { t } = useTranslation("user_profile");
+
+ const tabs = [t("edit_profile"), "Ban list"];
+
+ const [currentTabIndex, setCurrentTabIndex] = useState(0);
+
+ const renderTab = () => {
+ if (currentTabIndex == 0) {
+ return (
+
+ );
+ }
+
+ if (currentTabIndex == 1) {
+ return <>>;
+ }
+
+ return <>>;
+ };
+
+ return (
+ <>
+
+
+
+ {tabs.map((tab, index) => {
+ return (
+ setCurrentTabIndex(index)}
+ >
+ {tab}
+
+ );
+ })}
+
+ {renderTab()}
+
+
+ >
+ );
+};
diff --git a/src/types/index.ts b/src/types/index.ts
index ac352a91..cb2f8c27 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -310,6 +310,13 @@ export interface UserProfile {
relation: UserRelation | null;
}
+export interface UpdateProfileProps {
+ displayName?: string;
+ profileVisibility?: "PUBLIC" | "PRIVATE" | "FRIENDS";
+ profileImageUrl?: string | null;
+ bio?: string;
+}
+
export interface DownloadSource {
id: number;
name: string;
From 51e86819c15df17095fe99a59360d3585b17a3d2 Mon Sep 17 00:00:00 2001
From: Lianela <140931995+Lianela@users.noreply.github.com>
Date: Sun, 11 Aug 2024 15:29:05 -0600
Subject: [PATCH 041/108] Update ES translation.json
---
src/locales/es/translation.json | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json
index fcb2b099..0800b0c9 100644
--- a/src/locales/es/translation.json
+++ b/src/locales/es/translation.json
@@ -250,6 +250,17 @@
"friend_request_sent": "Solicitud de amistad enviada",
"friends": "Amigos",
"friends_list": "Lista de amigos",
- "user_not_found": "Usuario no encontrado"
+ "user_not_found": "Usuario no encontrado",
+ "block_user": "Bloquear usuario",
+ "add_friend": "Añadir amigo",
+ "request_sent": "Solicitud enviada",
+ "request_received": "Solicitud recibida",
+ "accept_request": "Aceptar solicitud",
+ "ignore_request": "Ignorar solicitud",
+ "cancel_request": "Cancelar solicitud",
+ "undo_friendship": "Eliminar amistad",
+ "request_accepted": "Solicitud aceptada",
+ "user_blocked_successfully": "Usuario bloqueado exitosamente",
+ "user_block_modal_text": "Esto va a bloquear a {{displayName}}"
}
}
From 7e6b9ca825684fec61aa56b17fcf183ea85104d4 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Wed, 14 Aug 2024 19:12:26 -0300
Subject: [PATCH 042/108] feat: conditional to show user content section based
on visibility
---
src/locales/en/translation.json | 3 +-
src/locales/pt/translation.json | 3 +-
src/renderer/src/pages/user/user-content.tsx | 324 +++++++++---------
.../user-profile-settings-modal.tsx | 2 +-
4 files changed, 173 insertions(+), 159 deletions(-)
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index 631e50c1..9e190778 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -266,6 +266,7 @@
"public": "Public",
"private": "Private",
"friends_only": "Friends only",
- "privacy": "Privacy"
+ "privacy": "Privacy",
+ "blocked_users": "Blocked users"
}
}
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index 5b24b574..036e2ff0 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -266,6 +266,7 @@
"privacy": "Privacidade",
"private": "Privado",
"friends_only": "Apenas amigos",
- "public": "Público"
+ "public": "Público",
+ "blocked_users": "Usuários bloqueados"
}
}
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index 7367ed5f..b9502231 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -160,6 +160,11 @@ export function UserContent({
};
const showFriends = isMe || userProfile.totalFriends > 0;
+ const showProfileContent =
+ isMe ||
+ userProfile.profileVisibility === "PUBLIC" ||
+ (userProfile.relation?.status === "ACCEPTED" &&
+ userProfile.profileVisibility === "FRIENDS");
const getProfileActions = () => {
if (isMe) {
@@ -362,125 +367,73 @@ export function UserContent({
-
-
-
{t("activity")}
-
- {!userProfile.recentGames.length ? (
-
-
-
-
-
{t("no_recent_activity_title")}
- {isMe && (
-
- {t("no_recent_activity_description")}
-
- )}
-
- ) : (
-
- {userProfile.recentGames.map((game) => (
-
handleGameClick(game)}
- >
-
-
-
{game.title}
-
- {t("last_time_played", {
- period: formatDistance(
- game.lastTimePlayed!,
- new Date(),
- {
- addSuffix: true,
- }
- ),
- })}
-
-
-
- ))}
-
- )}
-
-
-
+ {showProfileContent && (
+
-
-
{t("library")}
+
{t("activity")}
+ {!userProfile.recentGames.length ? (
+
+
+
+
+
{t("no_recent_activity_title")}
+ {isMe && (
+
+ {t("no_recent_activity_description")}
+
+ )}
+
+ ) : (
-
- {userProfile.libraryGames.length}
-
-
-
{t("total_play_time", { amount: formatPlayTime() })}
-
- {userProfile.libraryGames.map((game) => (
-
handleGameClick(game)}
- title={game.title}
- >
- {game.iconUrl ? (
+ >
+ {userProfile.recentGames.map((game) => (
+ handleGameClick(game)}
+ >
- ) : (
-
- )}
-
- ))}
-
+
+
{game.title}
+
+ {t("last_time_played", {
+ period: formatDistance(
+ game.lastTimePlayed!,
+ new Date(),
+ {
+ addSuffix: true,
+ }
+ ),
+ })}
+
+
+
+ ))}
+
+ )}
- {showFriends && (
-
-
- showFriendsModal(
- UserFriendModalTab.FriendsList,
- userProfile.id
- )
- }
+
+
+
-
{t("friends")}
+
{t("library")}
- {userProfile.totalFriends}
+ {userProfile.libraryGames.length}
-
-
+
+
+ {t("total_play_time", { amount: formatPlayTime() })}
+
- {userProfile.friends.map((friend) => {
- return (
-
handleOnClickFriend(friend.id)}
- >
-
- {friend.profileImageUrl ? (
-
- ) : (
-
- )}
-
-
-
- {friend.displayName}
-
-
- );
- })}
-
- {isMe && (
-
- showFriendsModal(
- UserFriendModalTab.AddFriend,
- userProfile.id
- )
- }
+ {userProfile.libraryGames.map((game) => (
+ handleGameClick(game)}
+ title={game.title}
>
- {t("add")}
-
- )}
+ {game.iconUrl ? (
+
+ ) : (
+
+ )}
+
+ ))}
- )}
+
+ {showFriends && (
+
+
+ showFriendsModal(
+ UserFriendModalTab.FriendsList,
+ userProfile.id
+ )
+ }
+ >
+ {t("friends")}
+
+
+
+ {userProfile.totalFriends}
+
+
+
+
+ {userProfile.friends.map((friend) => {
+ return (
+
handleOnClickFriend(friend.id)}
+ >
+
+ {friend.profileImageUrl ? (
+
+ ) : (
+
+ )}
+
+
+
+ {friend.displayName}
+
+
+ );
+ })}
+
+ {isMe && (
+
+ showFriendsModal(
+ UserFriendModalTab.AddFriend,
+ userProfile.id
+ )
+ }
+ >
+ {t("add")}
+
+ )}
+
+
+ )}
+
-
+ )}
>
);
}
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
index 5615cf12..7818b4f8 100644
--- a/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
+++ b/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
@@ -20,7 +20,7 @@ export const UserProfileSettingsModal = ({
}: UserEditProfileModalProps) => {
const { t } = useTranslation("user_profile");
- const tabs = [t("edit_profile"), "Ban list"];
+ const tabs = [t("edit_profile"), t("blocked_users")];
const [currentTabIndex, setCurrentTabIndex] = useState(0);
From fbe3c1973a7d9a2b6995d1d1b3f49d5b4a0ad85b Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Wed, 14 Aug 2024 19:45:48 -0300
Subject: [PATCH 043/108] feat: list blocked users
---
src/main/events/index.ts | 1 +
src/main/events/user/get-user-blocks.ts | 13 +++
src/preload/index.ts | 2 +
src/renderer/src/declaration.d.ts | 2 +
.../user-block-list.tsx | 92 +++++++++++++++++++
.../user-profile-settings-modal.tsx | 7 +-
src/types/index.ts | 5 +
7 files changed, 119 insertions(+), 3 deletions(-)
create mode 100644 src/main/events/user/get-user-blocks.ts
diff --git a/src/main/events/index.ts b/src/main/events/index.ts
index 9cdc58b5..3963e4b0 100644
--- a/src/main/events/index.ts
+++ b/src/main/events/index.ts
@@ -43,6 +43,7 @@ import "./auth/sign-out";
import "./auth/open-auth-window";
import "./auth/get-session-hash";
import "./user/get-user";
+import "./user/get-user-blocks";
import "./user/block-user";
import "./user/unblock-user";
import "./user/get-user-friends";
diff --git a/src/main/events/user/get-user-blocks.ts b/src/main/events/user/get-user-blocks.ts
new file mode 100644
index 00000000..65bb3eb4
--- /dev/null
+++ b/src/main/events/user/get-user-blocks.ts
@@ -0,0 +1,13 @@
+import { registerEvent } from "../register-event";
+import { HydraApi } from "@main/services";
+import { UserBlocks } from "@types";
+
+export const getUserBlocks = async (
+ _event: Electron.IpcMainInvokeEvent,
+ take: number,
+ skip: number
+): Promise
=> {
+ return HydraApi.get(`/profile/blocks`, { take, skip });
+};
+
+registerEvent("getUserBlocks", getUserBlocks);
diff --git a/src/preload/index.ts b/src/preload/index.ts
index 84100cab..087d573a 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -152,6 +152,8 @@ contextBridge.exposeInMainWorld("electron", {
unblockUser: (userId: string) => ipcRenderer.invoke("unblockUser", userId),
getUserFriends: (userId: string, take: number, skip: number) =>
ipcRenderer.invoke("getUserFriends", userId, take, skip),
+ getUserBlocks: (take: number, skip: number) =>
+ ipcRenderer.invoke("getUserBlocks", take, skip),
/* Auth */
signOut: () => ipcRenderer.invoke("signOut"),
diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts
index 3e954d40..29e4dcbb 100644
--- a/src/renderer/src/declaration.d.ts
+++ b/src/renderer/src/declaration.d.ts
@@ -17,6 +17,7 @@ import type {
FriendRequest,
FriendRequestAction,
UserFriends,
+ UserBlocks,
} from "@types";
import type { DiskSpace } from "check-disk-space";
@@ -135,6 +136,7 @@ declare global {
take: number,
skip: number
) => Promise;
+ getUserBlocks: (take: number, skip: number) => Promise;
/* Profile */
getMe: () => Promise;
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
index e69de29b..5b5d0e74 100644
--- a/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
+++ b/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
@@ -0,0 +1,92 @@
+import { SPACING_UNIT } from "@renderer/theme.css";
+import { UserFriend } from "@types";
+import { useEffect, useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { useToast, useUserDetails } from "@renderer/hooks";
+import { useTranslation } from "react-i18next";
+import { UserFriendItem } from "@renderer/pages/shared-modals/user-friend-modal/user-friend-item";
+
+export interface UserEditProfileBlockListProps {
+ closeModal: () => void;
+}
+
+const pageSize = 12;
+
+export const UserEditProfileBlockList = ({
+ closeModal,
+}: UserEditProfileBlockListProps) => {
+ const { t } = useTranslation("user_profile");
+ const { showErrorToast } = useToast();
+ const navigate = useNavigate();
+
+ const [page, setPage] = useState(0);
+ const [maxPage, setMaxPage] = useState(0);
+ const [blocks, setBlocks] = useState([]);
+
+ const { unblockUser } = useUserDetails();
+
+ const loadNextPage = () => {
+ if (page > maxPage) return;
+ window.electron
+ .getUserBlocks(pageSize, page * pageSize)
+ .then((newPage) => {
+ if (page === 0) {
+ setMaxPage(newPage.totalBlocks / pageSize);
+ }
+
+ setBlocks([...blocks, ...newPage.blocks]);
+ setPage(page + 1);
+ })
+ .catch(() => {});
+ };
+
+ const reloadList = () => {
+ setPage(0);
+ setMaxPage(0);
+ setBlocks([]);
+ loadNextPage();
+ };
+
+ useEffect(() => {
+ reloadList();
+ }, []);
+
+ const handleClickBlocked = (userId: string) => {
+ closeModal();
+ navigate(`/user/${userId}`);
+ };
+
+ const handleUnblock = (userId: string) => {
+ unblockUser(userId)
+ .then(() => {
+ reloadList();
+ })
+ .catch(() => {
+ showErrorToast(t("try_again"));
+ });
+ };
+
+ return (
+
+ {blocks.map((friend) => {
+ return (
+
+ );
+ })}
+
+ );
+};
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
index 7818b4f8..a2fcb372 100644
--- a/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
+++ b/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
@@ -4,8 +4,9 @@ import { SPACING_UNIT } from "@renderer/theme.css";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { UserEditProfile } from "./user-edit-profile";
+import { UserEditProfileBlockList } from "./user-block-list";
-export interface UserEditProfileModalProps {
+export interface UserProfileSettingsModalProps {
userProfile: UserProfile;
visible: boolean;
onClose: () => void;
@@ -17,7 +18,7 @@ export const UserProfileSettingsModal = ({
visible,
onClose,
updateUserProfile,
-}: UserEditProfileModalProps) => {
+}: UserProfileSettingsModalProps) => {
const { t } = useTranslation("user_profile");
const tabs = [t("edit_profile"), t("blocked_users")];
@@ -35,7 +36,7 @@ export const UserProfileSettingsModal = ({
}
if (currentTabIndex == 1) {
- return <>>;
+ return ;
}
return <>>;
diff --git a/src/types/index.ts b/src/types/index.ts
index cb2f8c27..6200d825 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -282,6 +282,11 @@ export interface UserFriends {
friends: UserFriend[];
}
+export interface UserBlocks {
+ totalBlocks: number;
+ blocks: UserFriend[];
+}
+
export interface FriendRequest {
id: string;
displayName: string;
From 68b361e60512d5cd2acb30e300a532414986aeb6 Mon Sep 17 00:00:00 2001
From: Chubby Granny Chaser
Date: Thu, 15 Aug 2024 20:46:21 +0100
Subject: [PATCH 044/108] feat: fixing real debrid download
---
package.json | 3 +-
.../events/torrenting/start-game-download.ts | 4 -
.../services/download/download-manager.ts | 2 -
.../download/generic-http-downloader.ts | 25 ++-
src/main/services/download/http-download.ts | 2 +-
.../download/real-debrid-downloader.ts | 58 ++-----
src/main/services/real-debrid.ts | 2 +-
src/renderer/src/app.css.ts | 2 +-
src/renderer/src/components/hero/hero.css.ts | 1 -
.../src/components/modal/modal.css.ts | 1 +
src/renderer/src/main.tsx | 10 +-
.../src/pages/downloads/downloads.tsx | 4 +-
.../description-header/description-header.tsx | 5 +-
.../pages/game-details/game-details.css.ts | 1 -
.../pages/game-details/hero/hero-panel.css.ts | 1 +
.../modals/download-settings-modal.tsx | 24 ++-
.../modals/game-options-modal.css.ts | 1 -
.../pages/game-details/sidebar/sidebar.css.ts | 1 -
.../settings/settings-download-sources.tsx | 4 +-
.../settings/settings-real-debrid.css.ts | 1 -
.../src/pages/user/user-block-modal.tsx | 4 +-
src/renderer/src/pages/user/user-content.tsx | 6 +-
.../src/pages/user/user-sign-out-modal.tsx | 2 +-
src/shared/index.ts | 3 +
torrent-client/downloader.py | 62 -------
torrent-client/main.py | 26 +--
torrent-client/torrent_downloader.py | 158 ++++++++++++++++++
yarn.lock | 13 +-
28 files changed, 241 insertions(+), 185 deletions(-)
delete mode 100644 torrent-client/downloader.py
create mode 100644 torrent-client/torrent_downloader.py
diff --git a/package.json b/package.json
index 54d689b2..aa77084e 100644
--- a/package.json
+++ b/package.json
@@ -34,8 +34,7 @@
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
- "@fontsource/fira-mono": "^5.0.13",
- "@fontsource/fira-sans": "^5.0.20",
+ "@fontsource/noto-sans": "^5.0.22",
"@primer/octicons-react": "^19.9.0",
"@reduxjs/toolkit": "^2.2.3",
"@sentry/electron": "^5.1.0",
diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts
index aa33c99a..cea41596 100644
--- a/src/main/events/torrenting/start-game-download.ts
+++ b/src/main/events/torrenting/start-game-download.ts
@@ -44,8 +44,6 @@ const startGameDownload = async (
);
if (game) {
- console.log("game", game);
-
await gameRepository.update(
{
id: game.id,
@@ -97,8 +95,6 @@ const startGameDownload = async (
},
});
- console.log(updatedGame);
-
createGame(updatedGame!);
await downloadQueueRepository.delete({ game: { id: updatedGame!.id } });
diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts
index b9c9d5a6..52a66693 100644
--- a/src/main/services/download/download-manager.ts
+++ b/src/main/services/download/download-manager.ts
@@ -102,8 +102,6 @@ export class DownloadManager {
const token = await GofileApi.authorize();
const downloadLink = await GofileApi.getDownloadLink(id!);
- console.log(downloadLink, token, "<<<");
-
GenericHTTPDownloader.startDownload(game, downloadLink, {
Cookie: `accountToken=${token}`,
});
diff --git a/src/main/services/download/generic-http-downloader.ts b/src/main/services/download/generic-http-downloader.ts
index 4977d503..8384a5fd 100644
--- a/src/main/services/download/generic-http-downloader.ts
+++ b/src/main/services/download/generic-http-downloader.ts
@@ -2,7 +2,7 @@ import { Game } from "@main/entity";
import { gameRepository } from "@main/repository";
import { calculateETA } from "./helpers";
import { DownloadProgress } from "@types";
-import { HTTPDownload } from "./http-download";
+import { HttpDownload } from "./http-download";
export class GenericHTTPDownloader {
private static downloads = new Map();
@@ -11,7 +11,7 @@ export class GenericHTTPDownloader {
public static async getStatus() {
if (this.downloadingGame) {
const gid = this.downloads.get(this.downloadingGame.id)!;
- const status = await HTTPDownload.getStatus(gid);
+ const status = HttpDownload.getStatus(gid);
if (status) {
const progress =
@@ -60,7 +60,7 @@ export class GenericHTTPDownloader {
const gid = this.downloads.get(this.downloadingGame!.id!);
if (gid) {
- await HTTPDownload.pauseDownload(gid);
+ await HttpDownload.pauseDownload(gid);
}
this.downloadingGame = null;
@@ -76,26 +76,23 @@ export class GenericHTTPDownloader {
if (this.downloads.has(game.id)) {
await this.resumeDownload(game.id!);
-
return;
}
- if (downloadUrl) {
- const gid = await HTTPDownload.startDownload(
- game.downloadPath!,
- downloadUrl,
- headers
- );
+ const gid = await HttpDownload.startDownload(
+ game.downloadPath!,
+ downloadUrl,
+ headers
+ );
- this.downloads.set(game.id!, gid);
- }
+ this.downloads.set(game.id!, gid);
}
static async cancelDownload(gameId: number) {
const gid = this.downloads.get(gameId);
if (gid) {
- await HTTPDownload.cancelDownload(gid);
+ await HttpDownload.cancelDownload(gid);
this.downloads.delete(gameId);
}
}
@@ -104,7 +101,7 @@ export class GenericHTTPDownloader {
const gid = this.downloads.get(gameId);
if (gid) {
- await HTTPDownload.resumeDownload(gid);
+ await HttpDownload.resumeDownload(gid);
}
}
}
diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts
index fb573ff4..cd8cbee5 100644
--- a/src/main/services/download/http-download.ts
+++ b/src/main/services/download/http-download.ts
@@ -2,7 +2,7 @@ import { DownloadItem } from "electron";
import { WindowManager } from "../window-manager";
import path from "node:path";
-export class HTTPDownload {
+export class HttpDownload {
private static id = 0;
private static downloads: Record = {};
diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts
index f61cb1ff..c6925f57 100644
--- a/src/main/services/download/real-debrid-downloader.ts
+++ b/src/main/services/download/real-debrid-downloader.ts
@@ -3,7 +3,7 @@ import { RealDebridClient } from "../real-debrid";
import { gameRepository } from "@main/repository";
import { calculateETA } from "./helpers";
import { DownloadProgress } from "@types";
-import { HTTPDownload } from "./http-download";
+import { HttpDownload } from "./http-download";
export class RealDebridDownloader {
private static downloads = new Map();
@@ -13,19 +13,23 @@ export class RealDebridDownloader {
private static async getRealDebridDownloadUrl() {
if (this.realDebridTorrentId) {
- const torrentInfo = await RealDebridClient.getTorrentInfo(
+ let torrentInfo = await RealDebridClient.getTorrentInfo(
this.realDebridTorrentId
);
- const { status, links } = torrentInfo;
-
- if (status === "waiting_files_selection") {
+ if (torrentInfo.status === "waiting_files_selection") {
await RealDebridClient.selectAllFiles(this.realDebridTorrentId);
- return null;
+
+ torrentInfo = await RealDebridClient.getTorrentInfo(
+ this.realDebridTorrentId
+ );
}
+ const { links, status } = torrentInfo;
+
if (status === "downloaded") {
const [link] = links;
+
const { download } = await RealDebridClient.unrestrictLink(link);
return decodeURIComponent(download);
}
@@ -38,8 +42,6 @@ export class RealDebridDownloader {
this.downloadingGame?.uri
);
- console.log("download>>", download);
-
return decodeURIComponent(download);
}
@@ -49,7 +51,7 @@ export class RealDebridDownloader {
public static async getStatus() {
if (this.downloadingGame) {
const gid = this.downloads.get(this.downloadingGame.id)!;
- const status = await HTTPDownload.getStatus(gid);
+ const status = HttpDownload.getStatus(gid);
if (status) {
const progress =
@@ -91,33 +93,6 @@ export class RealDebridDownloader {
}
}
- if (this.realDebridTorrentId && this.downloadingGame) {
- const torrentInfo = await RealDebridClient.getTorrentInfo(
- this.realDebridTorrentId
- );
-
- const { status } = torrentInfo;
-
- if (status === "downloaded") {
- this.startDownload(this.downloadingGame);
- }
-
- const progress = torrentInfo.progress / 100;
- const totalDownloaded = progress * torrentInfo.bytes;
-
- return {
- numPeers: 0,
- numSeeds: torrentInfo.seeders,
- downloadSpeed: torrentInfo.speed,
- timeRemaining: calculateETA(
- torrentInfo.bytes,
- totalDownloaded,
- torrentInfo.speed
- ),
- isDownloadingMetadata: status === "magnet_conversion",
- } as DownloadProgress;
- }
-
return null;
}
@@ -125,7 +100,7 @@ export class RealDebridDownloader {
if (this.downloadingGame) {
const gid = this.downloads.get(this.downloadingGame.id);
if (gid) {
- await HTTPDownload.pauseDownload(gid);
+ await HttpDownload.pauseDownload(gid);
}
}
@@ -146,18 +121,19 @@ export class RealDebridDownloader {
);
}
+ this.downloadingGame = game;
+
const downloadUrl = await this.getRealDebridDownloadUrl();
if (downloadUrl) {
this.realDebridTorrentId = null;
- const gid = await HTTPDownload.startDownload(
+ const gid = await HttpDownload.startDownload(
game.downloadPath!,
downloadUrl
);
this.downloads.set(game.id!, gid);
- this.downloadingGame = game;
}
}
@@ -165,7 +141,7 @@ export class RealDebridDownloader {
const gid = this.downloads.get(gameId);
if (gid) {
- await HTTPDownload.cancelDownload(gid);
+ await HttpDownload.cancelDownload(gid);
this.downloads.delete(gameId);
}
@@ -177,7 +153,7 @@ export class RealDebridDownloader {
const gid = this.downloads.get(gameId);
if (gid) {
- await HTTPDownload.resumeDownload(gid);
+ await HttpDownload.resumeDownload(gid);
}
}
}
diff --git a/src/main/services/real-debrid.ts b/src/main/services/real-debrid.ts
index 2e0debe6..26ba4c79 100644
--- a/src/main/services/real-debrid.ts
+++ b/src/main/services/real-debrid.ts
@@ -46,7 +46,7 @@ export class RealDebridClient {
static async selectAllFiles(id: string) {
const searchParams = new URLSearchParams({ files: "all" });
- await this.instance.post(
+ return this.instance.post(
`/torrents/selectFiles/${id}`,
searchParams.toString()
);
diff --git a/src/renderer/src/app.css.ts b/src/renderer/src/app.css.ts
index a5f9394b..c829021a 100644
--- a/src/renderer/src/app.css.ts
+++ b/src/renderer/src/app.css.ts
@@ -26,7 +26,7 @@ globalStyle("html, body, #root, main", {
globalStyle("body", {
overflow: "hidden",
userSelect: "none",
- fontFamily: "'Fira Mono', monospace",
+ fontFamily: "Noto Sans, sans-serif",
fontSize: vars.size.body,
background: vars.color.background,
color: vars.color.body,
diff --git a/src/renderer/src/components/hero/hero.css.ts b/src/renderer/src/components/hero/hero.css.ts
index cdb36ee2..eaf0a101 100644
--- a/src/renderer/src/components/hero/hero.css.ts
+++ b/src/renderer/src/components/hero/hero.css.ts
@@ -45,7 +45,6 @@ export const description = style({
maxWidth: "700px",
color: vars.color.muted,
textAlign: "left",
- fontFamily: "'Fira Sans', sans-serif",
lineHeight: "20px",
marginTop: `${SPACING_UNIT * 2}px`,
});
diff --git a/src/renderer/src/components/modal/modal.css.ts b/src/renderer/src/components/modal/modal.css.ts
index 45154015..d9d14fda 100644
--- a/src/renderer/src/components/modal/modal.css.ts
+++ b/src/renderer/src/components/modal/modal.css.ts
@@ -24,6 +24,7 @@ export const modal = recipe({
animation: `${scaleFadeIn} 0.2s cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running`,
backgroundColor: vars.color.background,
borderRadius: "4px",
+ minWidth: "400px",
maxWidth: "600px",
color: vars.color.body,
maxHeight: "100%",
diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx
index f87d66bf..b88348f0 100644
--- a/src/renderer/src/main.tsx
+++ b/src/renderer/src/main.tsx
@@ -8,12 +8,10 @@ import { HashRouter, Route, Routes } from "react-router-dom";
import * as Sentry from "@sentry/electron/renderer";
-import "@fontsource/fira-mono/400.css";
-import "@fontsource/fira-mono/500.css";
-import "@fontsource/fira-mono/700.css";
-import "@fontsource/fira-sans/400.css";
-import "@fontsource/fira-sans/500.css";
-import "@fontsource/fira-sans/700.css";
+import "@fontsource/noto-sans/400.css";
+import "@fontsource/noto-sans/500.css";
+import "@fontsource/noto-sans/700.css";
+
import "react-loading-skeleton/dist/skeleton.css";
import { App } from "./app";
diff --git a/src/renderer/src/pages/downloads/downloads.tsx b/src/renderer/src/pages/downloads/downloads.tsx
index 531bc526..5a9c228c 100644
--- a/src/renderer/src/pages/downloads/downloads.tsx
+++ b/src/renderer/src/pages/downloads/downloads.tsx
@@ -132,9 +132,7 @@ export function Downloads() {
{t("no_downloads_title")}
-
- {t("no_downloads_description")}
-
+ {t("no_downloads_description")}
)}
>
diff --git a/src/renderer/src/pages/game-details/description-header/description-header.tsx b/src/renderer/src/pages/game-details/description-header/description-header.tsx
index e4272534..cd73c52a 100644
--- a/src/renderer/src/pages/game-details/description-header/description-header.tsx
+++ b/src/renderer/src/pages/game-details/description-header/description-header.tsx
@@ -19,7 +19,10 @@ export function DescriptionHeader() {
date: shopDetails?.release_date.date,
})}
- {t("publisher", { publisher: shopDetails.publishers[0] })}
+
+ {Array.isArray(shopDetails.publishers) && (
+ {t("publisher", { publisher: shopDetails.publishers[0] })}
+ )}
);
diff --git a/src/renderer/src/pages/game-details/game-details.css.ts b/src/renderer/src/pages/game-details/game-details.css.ts
index f0bbfd2e..3dc0ec94 100644
--- a/src/renderer/src/pages/game-details/game-details.css.ts
+++ b/src/renderer/src/pages/game-details/game-details.css.ts
@@ -101,7 +101,6 @@ export const descriptionContent = style({
export const description = style({
userSelect: "text",
lineHeight: "22px",
- fontFamily: "'Fira Sans', sans-serif",
fontSize: "16px",
padding: `${SPACING_UNIT * 3}px ${SPACING_UNIT * 2}px`,
"@media": {
diff --git a/src/renderer/src/pages/game-details/hero/hero-panel.css.ts b/src/renderer/src/pages/game-details/hero/hero-panel.css.ts
index e10d55a5..c379c1c3 100644
--- a/src/renderer/src/pages/game-details/hero/hero-panel.css.ts
+++ b/src/renderer/src/pages/game-details/hero/hero-panel.css.ts
@@ -9,6 +9,7 @@ export const panel = recipe({
height: "72px",
minHeight: "72px",
padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 3}px`,
+ backgroundColor: vars.color.darkBackground,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
diff --git a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx
index d102d2b2..dff73ea0 100644
--- a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx
+++ b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx
@@ -60,14 +60,22 @@ export function DownloadSettingsModal({
.then((defaultDownloadsPath) => setSelectedPath(defaultDownloadsPath));
}
- if (
- userPreferences?.realDebridApiToken &&
- downloaders.includes(Downloader.RealDebrid)
- ) {
- setSelectedDownloader(Downloader.RealDebrid);
- } else {
- setSelectedDownloader(downloaders[0]);
- }
+ const filteredDownloaders = downloaders.filter((downloader) => {
+ if (downloader === Downloader.RealDebrid)
+ return userPreferences?.realDebridApiToken;
+ return true;
+ });
+
+ /* Gives preference to Real Debrid */
+ const selectedDownloader = filteredDownloaders.includes(
+ Downloader.RealDebrid
+ )
+ ? Downloader.RealDebrid
+ : filteredDownloaders[0];
+
+ setSelectedDownloader(
+ selectedDownloader === undefined ? null : selectedDownloader
+ );
}, [
userPreferences?.downloadsPath,
downloaders,
diff --git a/src/renderer/src/pages/game-details/modals/game-options-modal.css.ts b/src/renderer/src/pages/game-details/modals/game-options-modal.css.ts
index 8bf0ae7f..f844a686 100644
--- a/src/renderer/src/pages/game-details/modals/game-options-modal.css.ts
+++ b/src/renderer/src/pages/game-details/modals/game-options-modal.css.ts
@@ -15,7 +15,6 @@ export const gameOptionHeader = style({
});
export const gameOptionHeaderDescription = style({
- fontFamily: "'Fira Sans', sans-serif",
fontWeight: "400",
});
diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts
index e6d8b60a..734e6eb3 100644
--- a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts
+++ b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts
@@ -46,7 +46,6 @@ export const requirementButton = style({
export const requirementsDetails = style({
padding: `${SPACING_UNIT * 2}px`,
lineHeight: "22px",
- fontFamily: "'Fira Sans', sans-serif",
fontSize: "16px",
});
diff --git a/src/renderer/src/pages/settings/settings-download-sources.tsx b/src/renderer/src/pages/settings/settings-download-sources.tsx
index 8214117a..64d653b0 100644
--- a/src/renderer/src/pages/settings/settings-download-sources.tsx
+++ b/src/renderer/src/pages/settings/settings-download-sources.tsx
@@ -82,9 +82,7 @@ export function SettingsDownloadSources() {
onAddDownloadSource={handleAddDownloadSource}
/>
-
- {t("download_sources_description")}
-
+ {t("download_sources_description")}
-
- {t("user_block_modal_text", { displayName })}
-
+
{t("user_block_modal_text", { displayName })}
{t("block_user")}
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index 81db119d..7425f102 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -371,11 +371,7 @@ export function UserContent({
{t("no_recent_activity_title")}
- {isMe && (
-
- {t("no_recent_activity_description")}
-
- )}
+ {isMe &&
{t("no_recent_activity_description")}
}
) : (
-
{t("sign_out_modal_text")}
+
{t("sign_out_modal_text")}
{t("sign_out")}
diff --git a/src/shared/index.ts b/src/shared/index.ts
index ba68876f..af4ac17d 100644
--- a/src/shared/index.ts
+++ b/src/shared/index.ts
@@ -53,6 +53,8 @@ export const removeSpecialEditionFromName = (name: string) =>
export const removeDuplicateSpaces = (name: string) =>
name.replace(/\s{2,}/g, " ");
+export const replaceDotsWithSpace = (name: string) => name.replace(/\./g, " ");
+
export const replaceUnderscoreWithSpace = (name: string) =>
name.replace(/_/g, " ");
@@ -60,6 +62,7 @@ export const formatName = pipe(
removeReleaseYearFromName,
removeSpecialEditionFromName,
replaceUnderscoreWithSpace,
+ replaceDotsWithSpace,
(str) => str.replace(/DIRECTOR'S CUT/g, ""),
removeSymbolsFromName,
removeDuplicateSpaces,
diff --git a/torrent-client/downloader.py b/torrent-client/downloader.py
deleted file mode 100644
index 142da020..00000000
--- a/torrent-client/downloader.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import libtorrent as lt
-
-class Downloader:
- def __init__(self, port: str):
- self.torrent_handles = {}
- self.downloading_game_id = -1
- self.session = lt.session({'listen_interfaces': '0.0.0.0:{port}'.format(port=port)})
-
- def start_download(self, game_id: int, magnet: str, save_path: str):
- params = {'url': magnet, 'save_path': save_path}
- torrent_handle = self.session.add_torrent(params)
- self.torrent_handles[game_id] = torrent_handle
- torrent_handle.set_flags(lt.torrent_flags.auto_managed)
- torrent_handle.resume()
-
- self.downloading_game_id = game_id
-
- def pause_download(self, game_id: int):
- torrent_handle = self.torrent_handles.get(game_id)
- if torrent_handle:
- torrent_handle.pause()
- torrent_handle.unset_flags(lt.torrent_flags.auto_managed)
- self.downloading_game_id = -1
-
- def cancel_download(self, game_id: int):
- torrent_handle = self.torrent_handles.get(game_id)
- if torrent_handle:
- torrent_handle.pause()
- self.session.remove_torrent(torrent_handle)
- self.torrent_handles[game_id] = None
- self.downloading_game_id = -1
-
- def abort_session(self):
- for game_id in self.torrent_handles:
- torrent_handle = self.torrent_handles[game_id]
- torrent_handle.pause()
- self.session.remove_torrent(torrent_handle)
-
- self.session.abort()
- self.torrent_handles = {}
- self.downloading_game_id = -1
-
- def get_download_status(self):
- if self.downloading_game_id == -1:
- return None
-
- torrent_handle = self.torrent_handles.get(self.downloading_game_id)
-
- status = torrent_handle.status()
- info = torrent_handle.get_torrent_info()
-
- return {
- 'folderName': info.name() if info else "",
- 'fileSize': info.total_size() if info else 0,
- 'gameId': self.downloading_game_id,
- 'progress': status.progress,
- 'downloadSpeed': status.download_rate,
- 'numPeers': status.num_peers,
- 'numSeeds': status.num_seeds,
- 'status': status.state,
- 'bytesDownloaded': status.progress * info.total_size() if info else status.all_time_download,
- }
diff --git a/torrent-client/main.py b/torrent-client/main.py
index 004cd108..c25f95a4 100644
--- a/torrent-client/main.py
+++ b/torrent-client/main.py
@@ -3,19 +3,19 @@ from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import urllib.parse
import psutil
-from downloader import Downloader
+from torrent_downloader import TorrentDownloader
torrent_port = sys.argv[1]
http_port = sys.argv[2]
rpc_password = sys.argv[3]
start_download_payload = sys.argv[4]
-downloader = None
+torrent_downloader = None
if start_download_payload:
initial_download = json.loads(urllib.parse.unquote(start_download_payload))
- downloader = Downloader(torrent_port)
- downloader.start_download(initial_download['game_id'], initial_download['magnet'], initial_download['save_path'])
+ torrent_downloader = TorrentDownloader(torrent_port)
+ torrent_downloader.start_download(initial_download['game_id'], initial_download['magnet'], initial_download['save_path'])
class Handler(BaseHTTPRequestHandler):
rpc_password_header = 'x-hydra-rpc-password'
@@ -31,7 +31,7 @@ class Handler(BaseHTTPRequestHandler):
self.send_header("Content-type", "application/json")
self.end_headers()
- status = downloader.get_download_status()
+ status = torrent_downloader.get_download_status()
self.wfile.write(json.dumps(status).encode('utf-8'))
@@ -54,7 +54,7 @@ class Handler(BaseHTTPRequestHandler):
self.wfile.write(json.dumps(process_list).encode('utf-8'))
def do_POST(self):
- global downloader
+ global torrent_downloader
if self.path == "/action":
if self.headers.get(self.rpc_password_header) != rpc_password:
@@ -66,18 +66,18 @@ class Handler(BaseHTTPRequestHandler):
post_data = self.rfile.read(content_length)
data = json.loads(post_data.decode('utf-8'))
- if downloader is None:
- downloader = Downloader(torrent_port)
+ if torrent_downloader is None:
+ torrent_downloader = TorrentDownloader(torrent_port)
if data['action'] == 'start':
- downloader.start_download(data['game_id'], data['magnet'], data['save_path'])
+ torrent_downloader.start_download(data['game_id'], data['magnet'], data['save_path'])
elif data['action'] == 'pause':
- downloader.pause_download(data['game_id'])
+ torrent_downloader.pause_download(data['game_id'])
elif data['action'] == 'cancel':
- downloader.cancel_download(data['game_id'])
+ torrent_downloader.cancel_download(data['game_id'])
elif data['action'] == 'kill-torrent':
- downloader.abort_session()
- downloader = None
+ torrent_downloader.abort_session()
+ torrent_downloader = None
self.send_response(200)
self.end_headers()
diff --git a/torrent-client/torrent_downloader.py b/torrent-client/torrent_downloader.py
new file mode 100644
index 00000000..d59cd28b
--- /dev/null
+++ b/torrent-client/torrent_downloader.py
@@ -0,0 +1,158 @@
+import libtorrent as lt
+
+class TorrentDownloader:
+ def __init__(self, port: str):
+ self.torrent_handles = {}
+ self.downloading_game_id = -1
+ self.session = lt.session({'listen_interfaces': '0.0.0.0:{port}'.format(port=port)})
+ self.trackers = [
+ "udp://tracker.opentrackr.org:1337/announce",
+ "http://tracker.opentrackr.org:1337/announce",
+ "udp://open.tracker.cl:1337/announce",
+ "udp://open.demonii.com:1337/announce",
+ "udp://open.stealth.si:80/announce",
+ "udp://tracker.torrent.eu.org:451/announce",
+ "udp://exodus.desync.com:6969/announce",
+ "udp://tracker.theoks.net:6969/announce",
+ "udp://tracker-udp.gbitt.info:80/announce",
+ "udp://explodie.org:6969/announce",
+ "https://tracker.tamersunion.org:443/announce",
+ "udp://tracker2.dler.org:80/announce",
+ "udp://tracker1.myporn.club:9337/announce",
+ "udp://tracker.tiny-vps.com:6969/announce",
+ "udp://tracker.dler.org:6969/announce",
+ "udp://tracker.bittor.pw:1337/announce",
+ "udp://tracker.0x7c0.com:6969/announce",
+ "udp://retracker01-msk-virt.corbina.net:80/announce",
+ "udp://opentracker.io:6969/announce",
+ "udp://open.free-tracker.ga:6969/announce",
+ "udp://new-line.net:6969/announce",
+ "udp://moonburrow.club:6969/announce",
+ "udp://leet-tracker.moe:1337/announce",
+ "udp://bt2.archive.org:6969/announce",
+ "udp://bt1.archive.org:6969/announce",
+ "http://tracker2.dler.org:80/announce",
+ "http://tracker1.bt.moack.co.kr:80/announce",
+ "http://tracker.dler.org:6969/announce",
+ "http://tr.kxmp.cf:80/announce",
+ "udp://u.peer-exchange.download:6969/announce",
+ "udp://ttk2.nbaonlineservice.com:6969/announce",
+ "udp://tracker.tryhackx.org:6969/announce",
+ "udp://tracker.srv00.com:6969/announce",
+ "udp://tracker.skynetcloud.site:6969/announce",
+ "udp://tracker.jamesthebard.net:6969/announce",
+ "udp://tracker.fnix.net:6969/announce",
+ "udp://tracker.filemail.com:6969/announce",
+ "udp://tracker.farted.net:6969/announce",
+ "udp://tracker.edkj.club:6969/announce",
+ "udp://tracker.dump.cl:6969/announce",
+ "udp://tracker.deadorbit.nl:6969/announce",
+ "udp://tracker.darkness.services:6969/announce",
+ "udp://tracker.ccp.ovh:6969/announce",
+ "udp://tamas3.ynh.fr:6969/announce",
+ "udp://ryjer.com:6969/announce",
+ "udp://run.publictracker.xyz:6969/announce",
+ "udp://public.tracker.vraphim.com:6969/announce",
+ "udp://p4p.arenabg.com:1337/announce",
+ "udp://p2p.publictracker.xyz:6969/announce",
+ "udp://open.u-p.pw:6969/announce",
+ "udp://open.publictracker.xyz:6969/announce",
+ "udp://open.dstud.io:6969/announce",
+ "udp://open.demonoid.ch:6969/announce",
+ "udp://odd-hd.fr:6969/announce",
+ "udp://martin-gebhardt.eu:25/announce",
+ "udp://jutone.com:6969/announce",
+ "udp://isk.richardsw.club:6969/announce",
+ "udp://evan.im:6969/announce",
+ "udp://epider.me:6969/announce",
+ "udp://d40969.acod.regrucolo.ru:6969/announce",
+ "udp://bt.rer.lol:6969/announce",
+ "udp://amigacity.xyz:6969/announce",
+ "udp://1c.premierzal.ru:6969/announce",
+ "https://trackers.run:443/announce",
+ "https://tracker.yemekyedim.com:443/announce",
+ "https://tracker.renfei.net:443/announce",
+ "https://tracker.pmman.tech:443/announce",
+ "https://tracker.lilithraws.org:443/announce",
+ "https://tracker.imgoingto.icu:443/announce",
+ "https://tracker.cloudit.top:443/announce",
+ "https://tracker-zhuqiy.dgj055.icu:443/announce",
+ "http://tracker.renfei.net:8080/announce",
+ "http://tracker.mywaifu.best:6969/announce",
+ "http://tracker.ipv6tracker.org:80/announce",
+ "http://tracker.files.fm:6969/announce",
+ "http://tracker.edkj.club:6969/announce",
+ "http://tracker.bt4g.com:2095/announce",
+ "http://tracker-zhuqiy.dgj055.icu:80/announce",
+ "http://t1.aag.moe:17715/announce",
+ "http://t.overflow.biz:6969/announce",
+ "http://bittorrent-tracker.e-n-c-r-y-p-t.net:1337/announce",
+ "udp://torrents.artixlinux.org:6969/announce",
+ "udp://mail.artixlinux.org:6969/announce",
+ "udp://ipv4.rer.lol:2710/announce",
+ "udp://concen.org:6969/announce",
+ "udp://bt.rer.lol:2710/announce",
+ "udp://aegir.sexy:6969/announce",
+ "https://www.peckservers.com:9443/announce",
+ "https://tracker.ipfsscan.io:443/announce",
+ "https://tracker.gcrenwp.top:443/announce",
+ "http://www.peckservers.com:9000/announce",
+ "http://tracker1.itzmx.com:8080/announce",
+ "http://ch3oh.ru:6969/announce",
+ "http://bvarf.tracker.sh:2086/announce",
+ ]
+
+ def start_download(self, game_id: int, magnet: str, save_path: str):
+ params = {'url': magnet, 'save_path': save_path, 'trackers': self.trackers}
+ torrent_handle = self.session.add_torrent(params)
+ self.torrent_handles[game_id] = torrent_handle
+ torrent_handle.set_flags(lt.torrent_flags.auto_managed)
+ torrent_handle.resume()
+
+ self.downloading_game_id = game_id
+
+ def pause_download(self, game_id: int):
+ torrent_handle = self.torrent_handles.get(game_id)
+ if torrent_handle:
+ torrent_handle.pause()
+ torrent_handle.unset_flags(lt.torrent_flags.auto_managed)
+ self.downloading_game_id = -1
+
+ def cancel_download(self, game_id: int):
+ torrent_handle = self.torrent_handles.get(game_id)
+ if torrent_handle:
+ torrent_handle.pause()
+ self.session.remove_torrent(torrent_handle)
+ self.torrent_handles[game_id] = None
+ self.downloading_game_id = -1
+
+ def abort_session(self):
+ for game_id in self.torrent_handles:
+ torrent_handle = self.torrent_handles[game_id]
+ torrent_handle.pause()
+ self.session.remove_torrent(torrent_handle)
+
+ self.session.abort()
+ self.torrent_handles = {}
+ self.downloading_game_id = -1
+
+ def get_download_status(self):
+ if self.downloading_game_id == -1:
+ return None
+
+ torrent_handle = self.torrent_handles.get(self.downloading_game_id)
+
+ status = torrent_handle.status()
+ info = torrent_handle.get_torrent_info()
+
+ return {
+ 'folderName': info.name() if info else "",
+ 'fileSize': info.total_size() if info else 0,
+ 'gameId': self.downloading_game_id,
+ 'progress': status.progress,
+ 'downloadSpeed': status.download_rate,
+ 'numPeers': status.num_peers,
+ 'numSeeds': status.num_seeds,
+ 'status': status.state,
+ 'bytesDownloaded': status.progress * info.total_size() if info else status.all_time_download,
+ }
diff --git a/yarn.lock b/yarn.lock
index cf749216..74b9a8a1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -943,15 +943,10 @@
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz"
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
-"@fontsource/fira-mono@^5.0.13":
- version "5.0.13"
- resolved "https://registry.npmjs.org/@fontsource/fira-mono/-/fira-mono-5.0.13.tgz"
- integrity sha512-fZDjR2BdAqmauEbTjcIT62zYzbOgDa5+IQH34D2k8Pxmy1T815mAqQkZciWZVQ9dc/BgdTtTUV9HJ2ulBNwchg==
-
-"@fontsource/fira-sans@^5.0.20":
- version "5.0.20"
- resolved "https://registry.npmjs.org/@fontsource/fira-sans/-/fira-sans-5.0.20.tgz"
- integrity sha512-inmUjoKPrgnO4uUaZTAgP0b6YdhDfA52axHXvdTwgLvwd2kn3ZgK52UZoxD0VnrvTOjLA/iE4oC0tNtz4nyb5g==
+"@fontsource/noto-sans@^5.0.22":
+ version "5.0.22"
+ resolved "https://registry.yarnpkg.com/@fontsource/noto-sans/-/noto-sans-5.0.22.tgz#2c5249347ba84fef16e71a58e0ec01b460174093"
+ integrity sha512-PwjvKPGFbgpwfKjWZj1zeUvd7ExUW2AqHE9PF9ysAJ2gOuzIHWE6mEVIlchYif7WC2pQhn+g0w6xooCObVi+4A==
"@humanwhocodes/config-array@^0.11.14":
version "0.11.14"
From 80123d67e1e6b9a9991b9dfc2912feeebc86d3b6 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Wed, 14 Aug 2024 22:22:35 -0300
Subject: [PATCH 045/108] feat: adjust ui
---
src/locales/en/translation.json | 3 +-
src/locales/pt/translation.json | 3 +-
src/renderer/src/app.tsx | 10 ++-
src/renderer/src/hooks/use-user-details.ts | 2 +-
.../user-friend-modal/user-friend-item.tsx | 69 +++++++++++++++++--
.../user-block-list.tsx | 20 +-----
.../user-profile-settings-modal.tsx | 2 +-
7 files changed, 81 insertions(+), 28 deletions(-)
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index 9e190778..5dce5893 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -267,6 +267,7 @@
"private": "Private",
"friends_only": "Friends only",
"privacy": "Privacy",
- "blocked_users": "Blocked users"
+ "blocked_users": "Blocked users",
+ "unblock": "Unblock"
}
}
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index 036e2ff0..97c59b47 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -267,6 +267,7 @@
"private": "Privado",
"friends_only": "Apenas amigos",
"public": "Público",
- "blocked_users": "Usuários bloqueados"
+ "blocked_users": "Usuários bloqueados",
+ "unblock": "Desbloquear"
}
}
diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx
index 8c6f7604..2b9ac187 100644
--- a/src/renderer/src/app.tsx
+++ b/src/renderer/src/app.tsx
@@ -108,7 +108,7 @@ export function App() {
fetchFriendRequests();
}
});
- }, [fetchUserDetails, updateUserDetails, dispatch]);
+ }, [fetchUserDetails, fetchFriendRequests, updateUserDetails, dispatch]);
const onSignIn = useCallback(() => {
fetchUserDetails().then((response) => {
@@ -118,7 +118,13 @@ export function App() {
showSuccessToast(t("successfully_signed_in"));
}
});
- }, [fetchUserDetails, t, showSuccessToast, updateUserDetails]);
+ }, [
+ fetchUserDetails,
+ fetchFriendRequests,
+ t,
+ showSuccessToast,
+ updateUserDetails,
+ ]);
useEffect(() => {
const unsubscribe = window.electron.onGamesRunning((gamesRunning) => {
diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts
index e33de978..0cf2a381 100644
--- a/src/renderer/src/hooks/use-user-details.ts
+++ b/src/renderer/src/hooks/use-user-details.ts
@@ -99,7 +99,7 @@ export function useUserDetails() {
dispatch(setFriendsModalVisible({ initialTab, userId }));
fetchFriendRequests();
},
- [dispatch]
+ [dispatch, fetchFriendRequests]
);
const hideFriendsModal = useCallback(() => {
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx
index 7c34d040..2c283ac1 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx
@@ -12,21 +12,26 @@ export type UserFriendItemProps = {
userId: string;
profileImageUrl: string | null;
displayName: string;
- onClickItem: (userId: string) => void;
} & (
- | { type: "ACCEPTED"; onClickUndoFriendship: (userId: string) => void }
+ | {
+ type: "ACCEPTED";
+ onClickUndoFriendship: (userId: string) => void;
+ onClickItem: (userId: string) => void;
+ }
+ | { type: "BLOCKED"; onClickUnblock: (userId: string) => void }
| {
type: "SENT" | "RECEIVED";
onClickCancelRequest: (userId: string) => void;
onClickAcceptRequest: (userId: string) => void;
onClickRefuseRequest: (userId: string) => void;
+ onClickItem: (userId: string) => void;
}
- | { type: null }
+ | { type: null; onClickItem: (userId: string) => void }
);
export const UserFriendItem = (props: UserFriendItemProps) => {
const { t } = useTranslation("user_profile");
- const { userId, profileImageUrl, displayName, type, onClickItem } = props;
+ const { userId, profileImageUrl, displayName, type } = props;
const getRequestDescription = () => {
if (type === "ACCEPTED" || type === null) return null;
@@ -86,15 +91,69 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
);
}
+ if (type === "BLOCKED") {
+ return (
+ props.onClickUnblock(userId)}
+ title={t("unblock")}
+ >
+
+
+ );
+ }
+
return null;
};
+ if (type === "BLOCKED") {
+ return (
+
+
+
+ {profileImageUrl ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ {getRequestActions()}
+
+
+ );
+ }
+
return (
onClickItem(userId)}
+ onClick={() => props.onClickItem(userId)}
>
{profileImageUrl ? (
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
index 5b5d0e74..bad40d75 100644
--- a/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
+++ b/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
@@ -1,23 +1,15 @@
import { SPACING_UNIT } from "@renderer/theme.css";
import { UserFriend } from "@types";
import { useEffect, useState } from "react";
-import { useNavigate } from "react-router-dom";
import { useToast, useUserDetails } from "@renderer/hooks";
import { useTranslation } from "react-i18next";
import { UserFriendItem } from "@renderer/pages/shared-modals/user-friend-modal/user-friend-item";
-export interface UserEditProfileBlockListProps {
- closeModal: () => void;
-}
-
const pageSize = 12;
-export const UserEditProfileBlockList = ({
- closeModal,
-}: UserEditProfileBlockListProps) => {
+export const UserEditProfileBlockList = () => {
const { t } = useTranslation("user_profile");
const { showErrorToast } = useToast();
- const navigate = useNavigate();
const [page, setPage] = useState(0);
const [maxPage, setMaxPage] = useState(0);
@@ -51,11 +43,6 @@ export const UserEditProfileBlockList = ({
reloadList();
}, []);
- const handleClickBlocked = (userId: string) => {
- closeModal();
- navigate(`/user/${userId}`);
- };
-
const handleUnblock = (userId: string) => {
unblockUser(userId)
.then(() => {
@@ -80,9 +67,8 @@ export const UserEditProfileBlockList = ({
userId={friend.id}
displayName={friend.displayName}
profileImageUrl={friend.profileImageUrl}
- onClickItem={handleClickBlocked}
- onClickUndoFriendship={handleUnblock}
- type={"ACCEPTED"}
+ onClickUnblock={handleUnblock}
+ type={"BLOCKED"}
key={friend.id}
/>
);
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
index a2fcb372..d71b1bd7 100644
--- a/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
+++ b/src/renderer/src/pages/user/user-profile-settings-modal/user-profile-settings-modal.tsx
@@ -36,7 +36,7 @@ export const UserProfileSettingsModal = ({
}
if (currentTabIndex == 1) {
- return
;
+ return
;
}
return <>>;
From b4c260233f8685f3e1aad6e4d125867c41148f13 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Thu, 15 Aug 2024 18:40:48 -0300
Subject: [PATCH 046/108] chore: lint
---
src/renderer/src/pages/user/user-content.tsx | 4 ++--
src/renderer/src/pages/user/user.tsx | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index 2946523d..b16c49bb 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -115,7 +115,7 @@ export function UserContent({
useEffect(() => {
if (isMe) fetchFriendRequests();
- }, [isMe]);
+ }, [isMe, fetchFriendRequests]);
useEffect(() => {
if (isMe && profileBackground) {
@@ -129,7 +129,7 @@ export function UserContent({
}
);
}
- }, [profileBackground, isMe]);
+ }, [profileBackground, isMe, userProfile.profileImageUrl]);
const handleFriendAction = (userId: string, action: FriendAction) => {
try {
diff --git a/src/renderer/src/pages/user/user.tsx b/src/renderer/src/pages/user/user.tsx
index 4c45f789..565d412a 100644
--- a/src/renderer/src/pages/user/user.tsx
+++ b/src/renderer/src/pages/user/user.tsx
@@ -31,7 +31,7 @@ export const User = () => {
navigate(-1);
}
});
- }, [dispatch, userId, t]);
+ }, [dispatch, navigate, showErrorToast, userId, t]);
useEffect(() => {
getUserProfile();
From 5cb57bd5b3f17a5c0439e3abe97bf19d0d3c49e1 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Fri, 16 Aug 2024 12:16:22 -0300
Subject: [PATCH 047/108] feat: show friend code
---
.../user-friend-modal/user-friend-modal.tsx | 64 ++++++++++++++-----
1 file changed, 48 insertions(+), 16 deletions(-)
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx
index abc26270..452191aa 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx
@@ -1,10 +1,11 @@
import { Button, Modal } from "@renderer/components";
-import { SPACING_UNIT } from "@renderer/theme.css";
-import { useEffect, useState } from "react";
+import { SPACING_UNIT, vars } from "@renderer/theme.css";
+import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { UserFriendModalAddFriend } from "./user-friend-modal-add-friend";
-import { useUserDetails } from "@renderer/hooks";
+import { useToast, useUserDetails } from "@renderer/hooks";
import { UserFriendModalList } from "./user-friend-modal-list";
+import { CopyIcon } from "@primer/octicons-react";
export enum UserFriendModalTab {
FriendsList,
@@ -32,6 +33,8 @@ export const UserFriendModal = ({
initialTab || UserFriendModalTab.FriendsList
);
+ const { showSuccessToast } = useToast();
+
const { userDetails } = useUserDetails();
const isMe = userDetails?.id == userId;
@@ -53,6 +56,11 @@ export const UserFriendModal = ({
return <>>;
};
+ const copyToClipboard = useCallback(() => {
+ navigator.clipboard.writeText(userDetails!.id);
+ showSuccessToast("Código de amigo copiado");
+ }, [userDetails, showSuccessToast]);
+
return (
{isMe && (
-
- {tabs.map((tab, index) => {
- return (
- setCurrentTab(index)}
- >
- {tab}
-
- );
- })}
-
+ <>
+
+
Meu código de amigo:
+
+ {userDetails.id}
+
+
+
+
+ {tabs.map((tab, index) => {
+ return (
+ setCurrentTab(index)}
+ >
+ {tab}
+
+ );
+ })}
+
+ >
)}
{renderTab()}
From f3276dd8fe6637bb3f8ccc1a2fd35a1f91a792c1 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Fri, 16 Aug 2024 13:21:12 -0300
Subject: [PATCH 048/108] feat: add texts for no invites, no friends and no
blocks
---
src/locales/en/translation.json | 6 +++++-
src/locales/pt/translation.json | 6 +++++-
.../user-friend-modal-add-friend.tsx | 17 +++++++----------
.../user-friend-modal-list.tsx | 1 +
.../user-friend-modal/user-friend-modal.css.ts | 12 ++++++++++++
.../user-friend-modal/user-friend-modal.tsx | 13 ++++---------
.../user-block-list.tsx | 1 +
7 files changed, 35 insertions(+), 21 deletions(-)
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index 5dce5893..db64ed3c 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -268,6 +268,10 @@
"friends_only": "Friends only",
"privacy": "Privacy",
"blocked_users": "Blocked users",
- "unblock": "Unblock"
+ "unblock": "Unblock",
+ "no_friends_added": "You still don't have added friends",
+ "pending": "Pending",
+ "no_pending_invites": "You have no pending invites",
+ "no_blocked_users": "You have no blocked users"
}
}
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index 97c59b47..5cb6fd2f 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -268,6 +268,10 @@
"friends_only": "Apenas amigos",
"public": "Público",
"blocked_users": "Usuários bloqueados",
- "unblock": "Desbloquear"
+ "unblock": "Desbloquear",
+ "no_friends_added": "Você ainda não possui amigos adicionados",
+ "pending": "Pendentes",
+ "no_pending_invites": "Você não possui convites de amizade pendentes",
+ "no_blocked_users": "Você não tem nenhum usuário bloqueado"
}
}
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx
index 0725674e..b6e6aaea 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx
@@ -40,20 +40,16 @@ export const UserFriendModalAddFriend = ({
});
};
- const resetAndClose = () => {
- setFriendCode("");
- closeModal();
- };
-
const handleClickRequest = (userId: string) => {
- resetAndClose();
+ closeModal();
navigate(`/user/${userId}`);
};
const handleClickSeeProfile = () => {
- resetAndClose();
- // TODO: add validation for this input?
- navigate(`/user/${friendCode}`);
+ closeModal();
+ if (friendCode.length === 8) {
+ navigate(`/user/${friendCode}`);
+ }
};
const handleCancelFriendRequest = (userId: string) => {
@@ -122,7 +118,8 @@ export const UserFriendModalAddFriend = ({
gap: `${SPACING_UNIT * 2}px`,
}}
>
- Pendentes
+ {t("pending")}
+ {friendRequests.length === 0 && {t("no_pending_invites")}
}
{friendRequests.map((request) => {
return (
+ {friends.length === 0 && {t("no_friends_added")}
}
{friends.map((friend) => {
return (
- Meu código de amigo:
+ Seu código de amigo:
{userDetails.id}
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
index bad40d75..0029fc9e 100644
--- a/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
+++ b/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
@@ -61,6 +61,7 @@ export const UserEditProfileBlockList = () => {
gap: `${SPACING_UNIT * 2}px`,
}}
>
+ {blocks.length === 0 && {t("no_blocked_users")}
}
{blocks.map((friend) => {
return (
Date: Fri, 16 Aug 2024 13:28:20 -0300
Subject: [PATCH 049/108] feat: i18n toast friend code copied
---
src/locales/en/translation.json | 3 ++-
src/locales/pt/translation.json | 3 ++-
.../shared-modals/user-friend-modal/user-friend-modal.tsx | 4 ++--
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index db64ed3c..57511227 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -272,6 +272,7 @@
"no_friends_added": "You still don't have added friends",
"pending": "Pending",
"no_pending_invites": "You have no pending invites",
- "no_blocked_users": "You have no blocked users"
+ "no_blocked_users": "You have no blocked users",
+ "friend_code_copied": "Friend code copied"
}
}
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index 5cb6fd2f..36d38c96 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -272,6 +272,7 @@
"no_friends_added": "Você ainda não possui amigos adicionados",
"pending": "Pendentes",
"no_pending_invites": "Você não possui convites de amizade pendentes",
- "no_blocked_users": "Você não tem nenhum usuário bloqueado"
+ "no_blocked_users": "Você não tem nenhum usuário bloqueado",
+ "friend_code_copied": "Código de amigo copiado"
}
}
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx
index b3d46335..26c63abc 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx
@@ -59,8 +59,8 @@ export const UserFriendModal = ({
const copyToClipboard = useCallback(() => {
navigator.clipboard.writeText(userDetails!.id);
- showSuccessToast("Código de amigo copiado");
- }, [userDetails, showSuccessToast]);
+ showSuccessToast(t("friend_code_copied"));
+ }, [userDetails, showSuccessToast, t]);
return (
From 515a08a3a6026ed7e86454f4a727ed0d85eaa333 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Fri, 16 Aug 2024 13:55:52 -0300
Subject: [PATCH 050/108] feat: infinite scroll friend list
---
.../user-friend-modal/user-friend-item.tsx | 5 +-
.../user-friend-modal-list.tsx | 88 ++++++++++++++-----
.../user-friend-modal.css.ts | 17 ++--
3 files changed, 72 insertions(+), 38 deletions(-)
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx
index 2c283ac1..3ca837fa 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-item.tsx
@@ -4,7 +4,6 @@ import {
XCircleIcon,
} from "@primer/octicons-react";
import * as styles from "./user-friend-modal.css";
-import cn from "classnames";
import { SPACING_UNIT } from "@renderer/theme.css";
import { useTranslation } from "react-i18next";
@@ -108,7 +107,7 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
if (type === "BLOCKED") {
return (
-
+
{profileImageUrl ? (
@@ -149,7 +148,7 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
}
return (
-
+
([]);
+ const listContainer = useRef(null);
const { userDetails, undoFriendship } = useUserDetails();
const isMe = userDetails?.id == userId;
const loadNextPage = () => {
if (page > maxPage) return;
+ setIsLoading(true);
+ console.log("loading next page");
window.electron
.getUserFriends(userId, pageSize, page * pageSize)
.then((newPage) => {
@@ -40,9 +45,29 @@ export const UserFriendModalList = ({
setFriends([...friends, ...newPage.friends]);
setPage(page + 1);
})
- .catch(() => {});
+ .catch(() => {})
+ .finally(() => setIsLoading(false));
};
+ const handleScroll = () => {
+ const scrollTop = listContainer.current?.scrollTop || 0;
+ const scrollHeight = listContainer.current?.scrollHeight || 0;
+ const clientHeight = listContainer.current?.clientHeight || 0;
+ const maxScrollTop = scrollHeight - clientHeight;
+
+ if (scrollTop < maxScrollTop * 0.9 || isLoading) {
+ return;
+ }
+
+ loadNextPage();
+ };
+
+ useEffect(() => {
+ listContainer.current?.addEventListener("scroll", handleScroll);
+ return () =>
+ listContainer.current?.removeEventListener("scroll", handleScroll);
+ }, [isLoading]);
+
const reloadList = () => {
setPage(0);
setMaxPage(0);
@@ -70,27 +95,42 @@ export const UserFriendModalList = ({
};
return (
-
- {friends.length === 0 &&
{t("no_friends_added")}
}
- {friends.map((friend) => {
- return (
-
+
+ {friends.length === 0 &&
{t("no_friends_added")}
}
+ {friends.map((friend) => {
+ return (
+
+ );
+ })}
+ {isLoading && (
+
- );
- })}
-
+ )}
+
+
);
};
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.css.ts b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.css.ts
index 5f752fc7..41ab4156 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.css.ts
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.css.ts
@@ -1,17 +1,6 @@
import { SPACING_UNIT, vars } from "../../../theme.css";
import { style } from "@vanilla-extract/css";
-export const profileContentBox = style({
- display: "flex",
- 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 friendAvatarContainer = style({
width: "35px",
minWidth: "35px",
@@ -42,8 +31,14 @@ export const profileAvatar = style({
});
export const friendListContainer = style({
+ display: "flex",
+ gap: `${SPACING_UNIT * 3}px`,
+ alignItems: "center",
+ borderRadius: "4px",
+ border: `solid 1px ${vars.color.border}`,
width: "100%",
height: "54px",
+ minHeight: "54px",
transition: "all ease 0.2s",
position: "relative",
":hover": {
From e31655a96edd50cdb51512430b0bda23579a2ab3 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Fri, 16 Aug 2024 15:56:03 -0300
Subject: [PATCH 051/108] feat: infinite scroll block list
---
.../user-friend-modal-list.tsx | 3 +-
.../user-block-list.tsx | 85 ++++++++++++++-----
2 files changed, 63 insertions(+), 25 deletions(-)
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx
index b649c45f..b760351c 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx
@@ -34,7 +34,6 @@ export const UserFriendModalList = ({
const loadNextPage = () => {
if (page > maxPage) return;
setIsLoading(true);
- console.log("loading next page");
window.electron
.getUserFriends(userId, pageSize, page * pageSize)
.then((newPage) => {
@@ -106,7 +105,7 @@ export const UserFriendModalList = ({
overflowY: "scroll",
}}
>
- {friends.length === 0 && {t("no_friends_added")}
}
+ {!isLoading && friends.length === 0 && {t("no_friends_added")}
}
{friends.map((friend) => {
return (
{
const { showErrorToast } = useToast();
const [page, setPage] = useState(0);
+ const [isLoading, setIsLoading] = useState(false);
const [maxPage, setMaxPage] = useState(0);
const [blocks, setBlocks] = useState([]);
+ const listContainer = useRef(null);
const { unblockUser } = useUserDetails();
const loadNextPage = () => {
if (page > maxPage) return;
+ setIsLoading(true);
window.electron
.getUserBlocks(pageSize, page * pageSize)
.then((newPage) => {
@@ -29,9 +33,29 @@ export const UserEditProfileBlockList = () => {
setBlocks([...blocks, ...newPage.blocks]);
setPage(page + 1);
})
- .catch(() => {});
+ .catch(() => {})
+ .finally(() => setIsLoading(false));
};
+ const handleScroll = () => {
+ const scrollTop = listContainer.current?.scrollTop || 0;
+ const scrollHeight = listContainer.current?.scrollHeight || 0;
+ const clientHeight = listContainer.current?.clientHeight || 0;
+ const maxScrollTop = scrollHeight - clientHeight;
+
+ if (scrollTop < maxScrollTop * 0.9 || isLoading) {
+ return;
+ }
+
+ loadNextPage();
+ };
+
+ useEffect(() => {
+ listContainer.current?.addEventListener("scroll", handleScroll);
+ return () =>
+ listContainer.current?.removeEventListener("scroll", handleScroll);
+ }, [isLoading]);
+
const reloadList = () => {
setPage(0);
setMaxPage(0);
@@ -54,26 +78,41 @@ export const UserEditProfileBlockList = () => {
};
return (
-
- {blocks.length === 0 &&
{t("no_blocked_users")}
}
- {blocks.map((friend) => {
- return (
-
+
+ {!isLoading && blocks.length === 0 &&
{t("no_blocked_users")}
}
+ {blocks.map((friend) => {
+ return (
+
+ );
+ })}
+ {isLoading && (
+
- );
- })}
-
+ )}
+
+
);
};
From 3febe9b418d54b3fe385e03b94b7a8f813b23e99 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Fri, 16 Aug 2024 16:18:45 -0300
Subject: [PATCH 052/108] feat: adjust edit profile modal
---
.../pages/user/user-profile-settings-modal/user-edit-profile.tsx | 1 -
src/renderer/src/pages/user/user.css.ts | 1 +
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/user-edit-profile.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/user-edit-profile.tsx
index aa78c1ea..f6a430ba 100644
--- a/src/renderer/src/pages/user/user-profile-settings-modal/user-edit-profile.tsx
+++ b/src/renderer/src/pages/user/user-profile-settings-modal/user-edit-profile.tsx
@@ -98,7 +98,6 @@ export const UserEditProfile = ({
display: "flex",
flexDirection: "column",
justifyContent: "center",
- alignItems: "center",
gap: `${SPACING_UNIT * 3}px`,
width: "350px",
}}
diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts
index f9b1b09a..4e1c2139 100644
--- a/src/renderer/src/pages/user/user.css.ts
+++ b/src/renderer/src/pages/user/user.css.ts
@@ -60,6 +60,7 @@ export const friendListDisplayName = style({
});
export const profileAvatarEditContainer = style({
+ alignSelf: "center",
width: "128px",
height: "128px",
display: "flex",
From bbe68a0affecab5d35c4ac4bc800b1cfc2656dd1 Mon Sep 17 00:00:00 2001
From: Chubby Granny Chaser
Date: Sat, 17 Aug 2024 21:43:56 +0100
Subject: [PATCH 053/108] feat: reducing http downloads duplicate
---
src/main/index.ts | 3 -
.../services/download/download-manager.ts | 12 +--
.../download/generic-http-downloader.ts | 34 ++++---
src/main/services/download/http-download.ts | 77 ++++++---------
.../download/real-debrid-downloader.ts | 97 +------------------
src/main/services/hosters/gofile.ts | 4 +-
6 files changed, 64 insertions(+), 163 deletions(-)
diff --git a/src/main/index.ts b/src/main/index.ts
index 9ff74bf6..e288302b 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -20,8 +20,6 @@ autoUpdater.setFeedURL({
autoUpdater.logger = logger;
-logger.log("Init Hydra");
-
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) app.quit();
@@ -123,7 +121,6 @@ app.on("window-all-closed", () => {
app.on("before-quit", () => {
/* Disconnects libtorrent */
PythonInstance.kill();
- logger.log("Quit Hydra");
});
app.on("activate", () => {
diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts
index 52a66693..f97af659 100644
--- a/src/main/services/download/download-manager.ts
+++ b/src/main/services/download/download-manager.ts
@@ -7,7 +7,7 @@ import { publishDownloadCompleteNotification } from "../notifications";
import { RealDebridDownloader } from "./real-debrid-downloader";
import type { DownloadProgress } from "@types";
import { GofileApi } from "../hosters";
-import { GenericHTTPDownloader } from "./generic-http-downloader";
+import { GenericHttpDownloader } from "./generic-http-downloader";
export class DownloadManager {
private static currentDownloader: Downloader | null = null;
@@ -20,7 +20,7 @@ export class DownloadManager {
} else if (this.currentDownloader === Downloader.RealDebrid) {
status = await RealDebridDownloader.getStatus();
} else {
- status = await GenericHTTPDownloader.getStatus();
+ status = await GenericHttpDownloader.getStatus();
}
if (status) {
@@ -71,7 +71,7 @@ export class DownloadManager {
} else if (this.currentDownloader === Downloader.RealDebrid) {
await RealDebridDownloader.pauseDownload();
} else {
- await GenericHTTPDownloader.pauseDownload();
+ await GenericHttpDownloader.pauseDownload();
}
WindowManager.mainWindow?.setProgressBar(-1);
@@ -88,7 +88,7 @@ export class DownloadManager {
} else if (this.currentDownloader === Downloader.RealDebrid) {
RealDebridDownloader.cancelDownload(gameId);
} else {
- GenericHTTPDownloader.cancelDownload(gameId);
+ GenericHttpDownloader.cancelDownload(gameId);
}
WindowManager.mainWindow?.setProgressBar(-1);
@@ -102,13 +102,13 @@ export class DownloadManager {
const token = await GofileApi.authorize();
const downloadLink = await GofileApi.getDownloadLink(id!);
- GenericHTTPDownloader.startDownload(game, downloadLink, {
+ GenericHttpDownloader.startDownload(game, downloadLink, {
Cookie: `accountToken=${token}`,
});
} else if (game.downloader === Downloader.PixelDrain) {
const id = game!.uri!.split("/").pop();
- await GenericHTTPDownloader.startDownload(
+ await GenericHttpDownloader.startDownload(
game,
`https://pixeldrain.com/api/file/${id}?download`
);
diff --git a/src/main/services/download/generic-http-downloader.ts b/src/main/services/download/generic-http-downloader.ts
index 8384a5fd..055c8561 100644
--- a/src/main/services/download/generic-http-downloader.ts
+++ b/src/main/services/download/generic-http-downloader.ts
@@ -4,14 +4,14 @@ import { calculateETA } from "./helpers";
import { DownloadProgress } from "@types";
import { HttpDownload } from "./http-download";
-export class GenericHTTPDownloader {
- private static downloads = new Map();
- private static downloadingGame: Game | null = null;
+export class GenericHttpDownloader {
+ public static downloads = new Map();
+ public static downloadingGame: Game | null = null;
public static async getStatus() {
if (this.downloadingGame) {
- const gid = this.downloads.get(this.downloadingGame.id)!;
- const status = HttpDownload.getStatus(gid);
+ const download = this.downloads.get(this.downloadingGame.id)!;
+ const status = download.getStatus();
if (status) {
const progress =
@@ -57,10 +57,10 @@ export class GenericHTTPDownloader {
static async pauseDownload() {
if (this.downloadingGame) {
- const gid = this.downloads.get(this.downloadingGame!.id!);
+ const httpDownload = this.downloads.get(this.downloadingGame!.id!);
- if (gid) {
- await HttpDownload.pauseDownload(gid);
+ if (httpDownload) {
+ await httpDownload.pauseDownload();
}
this.downloadingGame = null;
@@ -79,29 +79,31 @@ export class GenericHTTPDownloader {
return;
}
- const gid = await HttpDownload.startDownload(
+ const httpDownload = new HttpDownload(
game.downloadPath!,
downloadUrl,
headers
);
- this.downloads.set(game.id!, gid);
+ httpDownload.startDownload();
+
+ this.downloads.set(game.id!, httpDownload);
}
static async cancelDownload(gameId: number) {
- const gid = this.downloads.get(gameId);
+ const httpDownload = this.downloads.get(gameId);
- if (gid) {
- await HttpDownload.cancelDownload(gid);
+ if (httpDownload) {
+ await httpDownload.cancelDownload();
this.downloads.delete(gameId);
}
}
static async resumeDownload(gameId: number) {
- const gid = this.downloads.get(gameId);
+ const httpDownload = this.downloads.get(gameId);
- if (gid) {
- await HttpDownload.resumeDownload(gid);
+ if (httpDownload) {
+ await httpDownload.resumeDownload();
}
}
}
diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts
index cd8cbee5..5b30321a 100644
--- a/src/main/services/download/http-download.ts
+++ b/src/main/services/download/http-download.ts
@@ -1,67 +1,54 @@
-import { DownloadItem } from "electron";
import { WindowManager } from "../window-manager";
import path from "node:path";
export class HttpDownload {
- private static id = 0;
+ private downloadItem: Electron.DownloadItem;
- private static downloads: Record = {};
+ constructor(
+ private downloadPath: string,
+ private downloadUrl: string,
+ private headers?: Record
+ ) {}
- public static getStatus(gid: string): {
- completedLength: number;
- totalLength: number;
- downloadSpeed: number;
- folderName: string;
- } | null {
- const downloadItem = this.downloads[gid];
- if (downloadItem) {
- return {
- completedLength: downloadItem.getReceivedBytes(),
- totalLength: downloadItem.getTotalBytes(),
- downloadSpeed: downloadItem.getCurrentBytesPerSecond(),
- folderName: downloadItem.getFilename(),
- };
- }
-
- return null;
+ public getStatus() {
+ return {
+ completedLength: this.downloadItem.getReceivedBytes(),
+ totalLength: this.downloadItem.getTotalBytes(),
+ downloadSpeed: this.downloadItem.getCurrentBytesPerSecond(),
+ folderName: this.downloadItem.getFilename(),
+ };
}
- static async cancelDownload(gid: string) {
- const downloadItem = this.downloads[gid];
- downloadItem?.cancel();
- delete this.downloads[gid];
+ async cancelDownload() {
+ this.downloadItem.cancel();
}
- static async pauseDownload(gid: string) {
- const downloadItem = this.downloads[gid];
- downloadItem?.pause();
+ async pauseDownload() {
+ this.downloadItem.pause();
}
- static async resumeDownload(gid: string) {
- const downloadItem = this.downloads[gid];
- downloadItem?.resume();
+ async resumeDownload() {
+ this.downloadItem.resume();
}
- static async startDownload(
- downloadPath: string,
- downloadUrl: string,
- headers?: Record
- ) {
- return new Promise((resolve) => {
- const options = headers ? { headers } : {};
- WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, options);
+ async startDownload() {
+ return new Promise((resolve) => {
+ const options = this.headers ? { headers: this.headers } : {};
+ WindowManager.mainWindow?.webContents.downloadURL(
+ this.downloadUrl,
+ options
+ );
- const gid = ++this.id;
-
- WindowManager.mainWindow?.webContents.session.on(
+ WindowManager.mainWindow?.webContents.session.once(
"will-download",
(_event, item, _webContents) => {
- this.downloads[gid.toString()] = item;
+ console.log(_event);
- // Set the save path, making Electron not to prompt a save dialog.
- item.setSavePath(path.join(downloadPath, item.getFilename()));
+ this.downloadItem = item;
- resolve(gid.toString());
+ item.setSavePath(path.join(this.downloadPath, item.getFilename()));
+
+ resolve(null);
}
);
});
diff --git a/src/main/services/download/real-debrid-downloader.ts b/src/main/services/download/real-debrid-downloader.ts
index c6925f57..2818644a 100644
--- a/src/main/services/download/real-debrid-downloader.ts
+++ b/src/main/services/download/real-debrid-downloader.ts
@@ -1,14 +1,9 @@
import { Game } from "@main/entity";
import { RealDebridClient } from "../real-debrid";
-import { gameRepository } from "@main/repository";
-import { calculateETA } from "./helpers";
-import { DownloadProgress } from "@types";
import { HttpDownload } from "./http-download";
+import { GenericHttpDownloader } from "./generic-http-downloader";
-export class RealDebridDownloader {
- private static downloads = new Map();
- private static downloadingGame: Game | null = null;
-
+export class RealDebridDownloader extends GenericHttpDownloader {
private static realDebridTorrentId: string | null = null;
private static async getRealDebridDownloadUrl() {
@@ -48,66 +43,6 @@ export class RealDebridDownloader {
return null;
}
- public static async getStatus() {
- if (this.downloadingGame) {
- const gid = this.downloads.get(this.downloadingGame.id)!;
- const status = HttpDownload.getStatus(gid);
-
- if (status) {
- const progress =
- Number(status.completedLength) / Number(status.totalLength);
-
- await gameRepository.update(
- { id: this.downloadingGame!.id },
- {
- bytesDownloaded: Number(status.completedLength),
- fileSize: Number(status.totalLength),
- progress,
- status: "active",
- folderName: status.folderName,
- }
- );
-
- const result = {
- numPeers: 0,
- numSeeds: 0,
- downloadSpeed: Number(status.downloadSpeed),
- timeRemaining: calculateETA(
- Number(status.totalLength),
- Number(status.completedLength),
- Number(status.downloadSpeed)
- ),
- isDownloadingMetadata: false,
- isCheckingFiles: false,
- progress,
- gameId: this.downloadingGame!.id,
- } as DownloadProgress;
-
- if (progress === 1) {
- this.downloads.delete(this.downloadingGame.id);
- this.realDebridTorrentId = null;
- this.downloadingGame = null;
- }
-
- return result;
- }
- }
-
- return null;
- }
-
- static async pauseDownload() {
- if (this.downloadingGame) {
- const gid = this.downloads.get(this.downloadingGame.id);
- if (gid) {
- await HttpDownload.pauseDownload(gid);
- }
- }
-
- this.realDebridTorrentId = null;
- this.downloadingGame = null;
- }
-
static async startDownload(game: Game) {
if (this.downloads.has(game.id)) {
await this.resumeDownload(game.id!);
@@ -128,32 +63,10 @@ export class RealDebridDownloader {
if (downloadUrl) {
this.realDebridTorrentId = null;
- const gid = await HttpDownload.startDownload(
- game.downloadPath!,
- downloadUrl
- );
+ const httpDownload = new HttpDownload(game.downloadPath!, downloadUrl);
+ httpDownload.startDownload();
- this.downloads.set(game.id!, gid);
- }
- }
-
- static async cancelDownload(gameId: number) {
- const gid = this.downloads.get(gameId);
-
- if (gid) {
- await HttpDownload.cancelDownload(gid);
- this.downloads.delete(gameId);
- }
-
- this.realDebridTorrentId = null;
- this.downloadingGame = null;
- }
-
- static async resumeDownload(gameId: number) {
- const gid = this.downloads.get(gameId);
-
- if (gid) {
- await HttpDownload.resumeDownload(gid);
+ this.downloads.set(game.id!, httpDownload);
}
}
}
diff --git a/src/main/services/hosters/gofile.ts b/src/main/services/hosters/gofile.ts
index 770bb15f..2c23556f 100644
--- a/src/main/services/hosters/gofile.ts
+++ b/src/main/services/hosters/gofile.ts
@@ -16,6 +16,8 @@ export interface GofileContentsResponse {
children: Record;
}
+export const WT = "4fd6sg89d7s6";
+
export class GofileApi {
private static token: string;
@@ -35,7 +37,7 @@ export class GofileApi {
public static async getDownloadLink(id: string) {
const searchParams = new URLSearchParams({
- wt: "4fd6sg89d7s6",
+ wt: WT,
});
const response = await axios.get<{
From dd7229dc5923d428ea80000cd85a49c0c92d3f6d Mon Sep 17 00:00:00 2001
From: Chubby Granny Chaser
Date: Sat, 17 Aug 2024 21:51:03 +0100
Subject: [PATCH 054/108] feat: reducing http downloads duplicate
---
src/main/services/download/http-download.ts | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts
index 5b30321a..4f6c31a9 100644
--- a/src/main/services/download/http-download.ts
+++ b/src/main/services/download/http-download.ts
@@ -42,8 +42,6 @@ export class HttpDownload {
WindowManager.mainWindow?.webContents.session.once(
"will-download",
(_event, item, _webContents) => {
- console.log(_event);
-
this.downloadItem = item;
item.setSavePath(path.join(this.downloadPath, item.getFilename()));
From 42ea35441c232ad9e818f606c7745e69c48aa8eb Mon Sep 17 00:00:00 2001
From: Chubby Granny Chaser
Date: Sun, 18 Aug 2024 01:39:50 +0100
Subject: [PATCH 055/108] fix: showing multiple download options
---
src/locales/en/translation.json | 9 +-
src/main/data-source.ts | 2 +-
src/main/entity/repack.entity.ts | 10 ++-
.../download-sources/get-download-sources.ts | 17 ++--
.../events/torrenting/start-game-download.ts | 7 +-
src/main/helpers/download-source.ts | 2 +-
src/main/services/hydra-api.ts | 86 +++++++++----------
src/main/services/repacks-manager.ts | 17 ++--
src/main/services/window-manager.ts | 2 +
src/renderer/src/hooks/use-download.ts | 5 +-
.../src/pages/game-details/game-details.tsx | 6 +-
.../modals/download-settings-modal.tsx | 6 +-
.../game-details/modals/repacks-modal.tsx | 11 ++-
.../settings/settings-download-sources.css.ts | 7 --
.../settings/settings-download-sources.tsx | 9 --
src/shared/index.ts | 15 +++-
src/types/index.ts | 5 ++
17 files changed, 118 insertions(+), 98 deletions(-)
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index 57511227..e2726b79 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -174,12 +174,9 @@
"validate_download_source": "Validate",
"remove_download_source": "Remove",
"add_download_source": "Add source",
- "download_count_zero": "No downloads in list",
- "download_count_one": "{{countFormatted}} download in list",
- "download_count_other": "{{countFormatted}} downloads in list",
- "download_options_zero": "No download available",
- "download_options_one": "{{countFormatted}} download available",
- "download_options_other": "{{countFormatted}} downloads available",
+ "download_count_zero": "No download options",
+ "download_count_one": "{{countFormatted}} download option",
+ "download_count_other": "{{countFormatted}} download options",
"download_source_url": "Download source URL",
"add_download_source_description": "Insert the URL containing the .json file",
"download_source_up_to_date": "Up-to-date",
diff --git a/src/main/data-source.ts b/src/main/data-source.ts
index b47ce2c0..a88a8883 100644
--- a/src/main/data-source.ts
+++ b/src/main/data-source.ts
@@ -6,12 +6,12 @@ import {
GameShopCache,
Repack,
UserPreferences,
+ UserAuth,
} from "@main/entity";
import type { BetterSqlite3ConnectionOptions } from "typeorm/driver/better-sqlite3/BetterSqlite3ConnectionOptions";
import { databasePath } from "./constants";
import migrations from "./migrations";
-import { UserAuth } from "./entity/user-auth";
export const createDataSource = (
options: Partial
diff --git a/src/main/entity/repack.entity.ts b/src/main/entity/repack.entity.ts
index 1d5259fd..380d7b8c 100644
--- a/src/main/entity/repack.entity.ts
+++ b/src/main/entity/repack.entity.ts
@@ -16,11 +16,14 @@ export class Repack {
@Column("text", { unique: true })
title: string;
- @Column("text", { unique: true })
+ /**
+ * @deprecated Use uris instead
+ */
+ @Column("text", { unique: true, nullable: true })
magnet: string;
/**
- * @deprecated
+ * @deprecated Direct scraping capability has been removed
*/
@Column("int", { nullable: true })
page: number;
@@ -37,6 +40,9 @@ export class Repack {
@ManyToOne(() => DownloadSource, { nullable: true, onDelete: "CASCADE" })
downloadSource: DownloadSource;
+ @Column("text")
+ uris: string;
+
@CreateDateColumn()
createdAt: Date;
diff --git a/src/main/events/download-sources/get-download-sources.ts b/src/main/events/download-sources/get-download-sources.ts
index 8f24caad..b8565645 100644
--- a/src/main/events/download-sources/get-download-sources.ts
+++ b/src/main/events/download-sources/get-download-sources.ts
@@ -1,16 +1,11 @@
import { downloadSourceRepository } from "@main/repository";
import { registerEvent } from "../register-event";
-const getDownloadSources = async (_event: Electron.IpcMainInvokeEvent) => {
- return downloadSourceRepository
- .createQueryBuilder("downloadSource")
- .leftJoin("downloadSource.repacks", "repacks")
- .orderBy("downloadSource.createdAt", "DESC")
- .loadRelationCountAndMap(
- "downloadSource.repackCount",
- "downloadSource.repacks"
- )
- .getMany();
-};
+const getDownloadSources = async (_event: Electron.IpcMainInvokeEvent) =>
+ downloadSourceRepository.find({
+ order: {
+ createdAt: "DESC",
+ },
+ });
registerEvent("getDownloadSources", getDownloadSources);
diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts
index cea41596..00978abc 100644
--- a/src/main/events/torrenting/start-game-download.ts
+++ b/src/main/events/torrenting/start-game-download.ts
@@ -18,7 +18,8 @@ const startGameDownload = async (
_event: Electron.IpcMainInvokeEvent,
payload: StartGameDownloadPayload
) => {
- const { repackId, objectID, title, shop, downloadPath, downloader } = payload;
+ const { repackId, objectID, title, shop, downloadPath, downloader, uri } =
+ payload;
const [game, repack] = await Promise.all([
gameRepository.findOne({
@@ -54,7 +55,7 @@ const startGameDownload = async (
bytesDownloaded: 0,
downloadPath,
downloader,
- uri: repack.magnet,
+ uri,
isDeleted: false,
}
);
@@ -76,7 +77,7 @@ const startGameDownload = async (
shop,
status: "active",
downloadPath,
- uri: repack.magnet,
+ uri,
})
.then((result) => {
if (iconUrl) {
diff --git a/src/main/helpers/download-source.ts b/src/main/helpers/download-source.ts
index 012a4d24..0b996fcc 100644
--- a/src/main/helpers/download-source.ts
+++ b/src/main/helpers/download-source.ts
@@ -17,7 +17,7 @@ export const insertDownloadsFromSource = async (
const repacks: QueryDeepPartialEntity[] = downloads.map(
(download) => ({
title: download.title,
- magnet: download.uris[0],
+ uris: JSON.stringify(download.uris),
fileSize: download.fileSize,
repacker: downloadSource.name,
uploadDate: download.uploadDate,
diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts
index 120d27ac..6f0e1905 100644
--- a/src/main/services/hydra-api.ts
+++ b/src/main/services/hydra-api.ts
@@ -77,54 +77,54 @@ export class HydraApi {
baseURL: import.meta.env.MAIN_VITE_API_URL,
});
- this.instance.interceptors.request.use(
- (request) => {
- logger.log(" ---- REQUEST -----");
- logger.log(request.method, request.url, request.params, request.data);
- return request;
- },
- (error) => {
- logger.error("request error", error);
- return Promise.reject(error);
- }
- );
+ // this.instance.interceptors.request.use(
+ // (request) => {
+ // logger.log(" ---- REQUEST -----");
+ // logger.log(request.method, request.url, request.params, request.data);
+ // return request;
+ // },
+ // (error) => {
+ // logger.error("request error", error);
+ // return Promise.reject(error);
+ // }
+ // );
- this.instance.interceptors.response.use(
- (response) => {
- logger.log(" ---- RESPONSE -----");
- logger.log(
- response.status,
- response.config.method,
- response.config.url,
- response.data
- );
- return response;
- },
- (error) => {
- logger.error(" ---- RESPONSE ERROR -----");
+ // this.instance.interceptors.response.use(
+ // (response) => {
+ // logger.log(" ---- RESPONSE -----");
+ // logger.log(
+ // response.status,
+ // response.config.method,
+ // response.config.url,
+ // response.data
+ // );
+ // return response;
+ // },
+ // (error) => {
+ // logger.error(" ---- RESPONSE ERROR -----");
- const { config } = error;
+ // const { config } = error;
- logger.error(
- config.method,
- config.baseURL,
- config.url,
- config.headers,
- config.data
- );
+ // logger.error(
+ // config.method,
+ // config.baseURL,
+ // config.url,
+ // config.headers,
+ // config.data
+ // );
- if (error.response) {
- logger.error("Response", error.response.status, error.response.data);
- } else if (error.request) {
- logger.error("Request", error.request);
- } else {
- logger.error("Error", error.message);
- }
+ // if (error.response) {
+ // logger.error("Response", error.response.status, error.response.data);
+ // } else if (error.request) {
+ // logger.error("Request", error.request);
+ // } else {
+ // logger.error("Error", error.message);
+ // }
- logger.error(" ----- END RESPONSE ERROR -------");
- return Promise.reject(error);
- }
- );
+ // logger.error(" ----- END RESPONSE ERROR -------");
+ // return Promise.reject(error);
+ // }
+ // );
const userAuth = await userAuthRepository.findOne({
where: { id: 1 },
diff --git a/src/main/services/repacks-manager.ts b/src/main/services/repacks-manager.ts
index 02821127..bfe4bc8a 100644
--- a/src/main/services/repacks-manager.ts
+++ b/src/main/services/repacks-manager.ts
@@ -8,11 +8,18 @@ export class RepacksManager {
private static repacksIndex = new flexSearch.Index();
public static async updateRepacks() {
- this.repacks = await repackRepository.find({
- order: {
- createdAt: "DESC",
- },
- });
+ this.repacks = await repackRepository
+ .find({
+ order: {
+ createdAt: "DESC",
+ },
+ })
+ .then((repacks) =>
+ repacks.map((repack) => ({
+ ...repack,
+ uris: JSON.parse(repack.uris),
+ }))
+ );
for (let i = 0; i < this.repacks.length; i++) {
this.repacksIndex.remove(i);
diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts
index 201b13ad..fcef12d6 100644
--- a/src/main/services/window-manager.ts
+++ b/src/main/services/window-manager.ts
@@ -64,6 +64,8 @@ export class WindowManager {
this.loadURL();
this.mainWindow.removeMenu();
+ WindowManager.mainWindow?.webContents.openDevTools();
+
this.mainWindow.on("ready-to-show", () => {
if (!app.isPackaged) WindowManager.mainWindow?.webContents.openDevTools();
WindowManager.mainWindow?.show();
diff --git a/src/renderer/src/hooks/use-download.ts b/src/renderer/src/hooks/use-download.ts
index f58a8765..07c885cf 100644
--- a/src/renderer/src/hooks/use-download.ts
+++ b/src/renderer/src/hooks/use-download.ts
@@ -22,9 +22,10 @@ export function useDownload() {
);
const dispatch = useAppDispatch();
- const startDownload = (payload: StartGameDownloadPayload) => {
+ const startDownload = async (payload: StartGameDownloadPayload) => {
dispatch(clearDownload());
- window.electron.startGameDownload(payload).then((game) => {
+
+ return window.electron.startGameDownload(payload).then((game) => {
updateLibrary();
return game;
diff --git a/src/renderer/src/pages/game-details/game-details.tsx b/src/renderer/src/pages/game-details/game-details.tsx
index 5f32965a..5ac9673f 100644
--- a/src/renderer/src/pages/game-details/game-details.tsx
+++ b/src/renderer/src/pages/game-details/game-details.tsx
@@ -23,7 +23,7 @@ import {
} from "@renderer/context";
import { useDownload } from "@renderer/hooks";
import { GameOptionsModal, RepacksModal } from "./modals";
-import { Downloader } from "@shared";
+import { Downloader, getDownloadersForUri } from "@shared";
export function GameDetails() {
const [randomGame, setRandomGame] = useState(null);
@@ -70,6 +70,9 @@ export function GameDetails() {
}
};
+ const selectRepackUri = (repack: GameRepack, downloader: Downloader) =>
+ repack.uris.find((uri) => getDownloadersForUri(uri).includes(downloader))!;
+
return (
@@ -96,6 +99,7 @@ export function GameDetails() {
downloader,
shop: shop as GameShop,
downloadPath,
+ uri: selectRepackUri(repack, downloader),
});
await updateGame();
diff --git a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx
index dff73ea0..3450af24 100644
--- a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx
+++ b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx
@@ -5,7 +5,7 @@ import { DiskSpace } from "check-disk-space";
import * as styles from "./download-settings-modal.css";
import { Button, Link, Modal, TextField } from "@renderer/components";
import { CheckCircleFillIcon, DownloadIcon } from "@primer/octicons-react";
-import { Downloader, formatBytes, getDownloadersForUri } from "@shared";
+import { Downloader, formatBytes, getDownloadersForUris } from "@shared";
import type { GameRepack } from "@types";
import { SPACING_UNIT } from "@renderer/theme.css";
@@ -48,8 +48,8 @@ export function DownloadSettingsModal({
}, [visible, selectedPath]);
const downloaders = useMemo(() => {
- return getDownloadersForUri(repack?.magnet ?? "");
- }, [repack?.magnet]);
+ return getDownloadersForUris(repack?.uris ?? []);
+ }, [repack?.uris]);
useEffect(() => {
if (userPreferences?.downloadsPath) {
diff --git a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx
index f9d351af..0d1b9c1d 100644
--- a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx
+++ b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx
@@ -76,6 +76,13 @@ export function RepacksModal({
);
};
+ const checkIfLastDownloadedOption = (repack: GameRepack) => {
+ if (infoHash) return repack.uris.some((uri) => uri.includes(infoHash));
+ if (!game?.uri) return false;
+
+ return repack.uris.some((uri) => uri.includes(game?.uri ?? ""));
+ };
+
return (
<>
{filteredRepacks.map((repack) => {
- const isLastDownloadedOption =
- infoHash !== null &&
- repack.magnet.toLowerCase().includes(infoHash);
+ const isLastDownloadedOption = checkIfLastDownloadedOption(repack);
return (
-
-
-
-
- {t("download_options", {
- count: downloadSource.repackCount,
- countFormatted: downloadSource.repackCount.toLocaleString(),
- })}
-
diff --git a/src/shared/index.ts b/src/shared/index.ts
index af4ac17d..3ae476ee 100644
--- a/src/shared/index.ts
+++ b/src/shared/index.ts
@@ -73,13 +73,26 @@ const realDebridHosts = ["https://1fichier.com", "https://mediafire.com"];
export const getDownloadersForUri = (uri: string) => {
if (uri.startsWith("https://gofile.io")) return [Downloader.Gofile];
+
if (uri.startsWith("https://pixeldrain.com")) return [Downloader.PixelDrain];
if (realDebridHosts.some((host) => uri.startsWith(host)))
return [Downloader.RealDebrid];
- if (uri.startsWith("magnet:"))
+ if (uri.startsWith("magnet:")) {
return [Downloader.Torrent, Downloader.RealDebrid];
+ }
return [];
};
+
+export const getDownloadersForUris = (uris: string[]) => {
+ const downloadersSet = uris.reduce
>((prev, next) => {
+ const downloaders = getDownloadersForUri(next);
+ downloaders.forEach((downloader) => prev.add(downloader));
+
+ return prev;
+ }, new Set());
+
+ return Array.from(downloadersSet);
+};
diff --git a/src/types/index.ts b/src/types/index.ts
index 6200d825..46ab5421 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -67,7 +67,11 @@ export interface SteamAppDetails {
export interface GameRepack {
id: number;
title: string;
+ /**
+ * @deprecated Use uris instead
+ */
magnet: string;
+ uris: string[];
repacker: string;
fileSize: string | null;
uploadDate: Date | string | null;
@@ -194,6 +198,7 @@ export interface StartGameDownloadPayload {
objectID: string;
title: string;
shop: GameShop;
+ uri: string;
downloadPath: string;
downloader: Downloader;
}
From 7a9247278db6d766030cd2e150cbd38c0be1013e Mon Sep 17 00:00:00 2001
From: Chubby Granny Chaser
Date: Sun, 18 Aug 2024 02:31:20 +0100
Subject: [PATCH 056/108] fix: removing menu
---
src/main/entity/repack.entity.ts | 2 +-
src/main/services/window-manager.ts | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/main/entity/repack.entity.ts b/src/main/entity/repack.entity.ts
index 380d7b8c..3b546692 100644
--- a/src/main/entity/repack.entity.ts
+++ b/src/main/entity/repack.entity.ts
@@ -19,7 +19,7 @@ export class Repack {
/**
* @deprecated Use uris instead
*/
- @Column("text", { unique: true, nullable: true })
+ @Column("text", { unique: true })
magnet: string;
/**
diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts
index fcef12d6..201b13ad 100644
--- a/src/main/services/window-manager.ts
+++ b/src/main/services/window-manager.ts
@@ -64,8 +64,6 @@ export class WindowManager {
this.loadURL();
this.mainWindow.removeMenu();
- WindowManager.mainWindow?.webContents.openDevTools();
-
this.mainWindow.on("ready-to-show", () => {
if (!app.isPackaged) WindowManager.mainWindow?.webContents.openDevTools();
WindowManager.mainWindow?.show();
From c76ef630e1db69c80359712f01277ad5bdc01cdf Mon Sep 17 00:00:00 2001
From: Chubby Granny Chaser
Date: Sun, 18 Aug 2024 02:45:05 +0100
Subject: [PATCH 057/108] fix: removing menu
---
src/main/helpers/download-source.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/main/helpers/download-source.ts b/src/main/helpers/download-source.ts
index 0b996fcc..c216212a 100644
--- a/src/main/helpers/download-source.ts
+++ b/src/main/helpers/download-source.ts
@@ -18,6 +18,7 @@ export const insertDownloadsFromSource = async (
(download) => ({
title: download.title,
uris: JSON.stringify(download.uris),
+ magnet: download.uris[0]!,
fileSize: download.fileSize,
repacker: downloadSource.name,
uploadDate: download.uploadDate,
From 73a12488cdccbdcf757997c6079654b056aee0dd Mon Sep 17 00:00:00 2001
From: Chubby Granny Chaser
Date: Sun, 18 Aug 2024 03:06:35 +0100
Subject: [PATCH 058/108] fix: adding default to uris
---
src/main/entity/repack.entity.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/entity/repack.entity.ts b/src/main/entity/repack.entity.ts
index 3b546692..ff3f16cb 100644
--- a/src/main/entity/repack.entity.ts
+++ b/src/main/entity/repack.entity.ts
@@ -40,7 +40,7 @@ export class Repack {
@ManyToOne(() => DownloadSource, { nullable: true, onDelete: "CASCADE" })
downloadSource: DownloadSource;
- @Column("text")
+ @Column("text", { default: "[]" })
uris: string;
@CreateDateColumn()
From 76d3fead66df16144c0737c5fae6512220c44d0b Mon Sep 17 00:00:00 2001
From: Chubby Granny Chaser
Date: Sun, 18 Aug 2024 03:38:12 +0100
Subject: [PATCH 059/108] fix: adding default to uris
---
src/main/services/repacks-manager.ts | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/src/main/services/repacks-manager.ts b/src/main/services/repacks-manager.ts
index bfe4bc8a..93157d6c 100644
--- a/src/main/services/repacks-manager.ts
+++ b/src/main/services/repacks-manager.ts
@@ -15,10 +15,17 @@ export class RepacksManager {
},
})
.then((repacks) =>
- repacks.map((repack) => ({
- ...repack,
- uris: JSON.parse(repack.uris),
- }))
+ repacks.map((repack) => {
+ const uris: string[] = [];
+ const magnet = repack?.magnet;
+
+ if (magnet) uris.push(magnet);
+
+ return {
+ ...repack,
+ uris: [...uris, ...JSON.parse(repack.uris)],
+ };
+ })
);
for (let i = 0; i < this.repacks.length; i++) {
From 6c24a523b77fe40704803343a9adfa02ae44704a Mon Sep 17 00:00:00 2001
From: Chubby Granny Chaser
Date: Sun, 18 Aug 2024 16:19:06 +0100
Subject: [PATCH 060/108] feat: adding support to qiwi
---
.../events/torrenting/start-game-download.ts | 1 +
src/main/helpers/index.ts | 6 ++-
.../services/download/download-manager.ts | 48 ++++++++++++-------
src/main/services/hosters/index.ts | 1 +
src/main/services/how-long-to-beat.ts | 6 +--
src/renderer/src/constants.ts | 1 +
.../modals/download-settings-modal.css.ts | 7 ++-
src/shared/index.ts | 2 +
8 files changed, 46 insertions(+), 26 deletions(-)
diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts
index 00978abc..f4db999f 100644
--- a/src/main/events/torrenting/start-game-download.ts
+++ b/src/main/events/torrenting/start-game-download.ts
@@ -101,6 +101,7 @@ const startGameDownload = async (
await downloadQueueRepository.delete({ game: { id: updatedGame!.id } });
await downloadQueueRepository.insert({ game: { id: updatedGame!.id } });
+ await DownloadManager.cancelDownload(updatedGame!.id);
await DownloadManager.startDownload(updatedGame!);
};
diff --git a/src/main/helpers/index.ts b/src/main/helpers/index.ts
index f2b86e5a..b0ff391f 100644
--- a/src/main/helpers/index.ts
+++ b/src/main/helpers/index.ts
@@ -1,4 +1,5 @@
import axios from "axios";
+import { JSDOM } from "jsdom";
import UserAgent from "user-agents";
export const getSteamAppAsset = (
@@ -48,13 +49,16 @@ export const sleep = (ms: number) =>
export const requestWebPage = async (url: string) => {
const userAgent = new UserAgent();
- return axios
+ const data = await axios
.get(url, {
headers: {
"User-Agent": userAgent.toString(),
},
})
.then((response) => response.data);
+
+ const { window } = new JSDOM(data);
+ return window.document;
};
export const isPortableVersion = () =>
diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts
index f97af659..d4733a32 100644
--- a/src/main/services/download/download-manager.ts
+++ b/src/main/services/download/download-manager.ts
@@ -6,7 +6,7 @@ import { downloadQueueRepository, gameRepository } from "@main/repository";
import { publishDownloadCompleteNotification } from "../notifications";
import { RealDebridDownloader } from "./real-debrid-downloader";
import type { DownloadProgress } from "@types";
-import { GofileApi } from "../hosters";
+import { GofileApi, QiwiApi } from "../hosters";
import { GenericHttpDownloader } from "./generic-http-downloader";
export class DownloadManager {
@@ -96,26 +96,38 @@ export class DownloadManager {
}
static async startDownload(game: Game) {
- if (game.downloader === Downloader.Gofile) {
- const id = game!.uri!.split("/").pop();
+ switch (game.downloader) {
+ case Downloader.Gofile: {
+ const id = game!.uri!.split("/").pop();
- const token = await GofileApi.authorize();
- const downloadLink = await GofileApi.getDownloadLink(id!);
+ const token = await GofileApi.authorize();
+ const downloadLink = await GofileApi.getDownloadLink(id!);
- GenericHttpDownloader.startDownload(game, downloadLink, {
- Cookie: `accountToken=${token}`,
- });
- } else if (game.downloader === Downloader.PixelDrain) {
- const id = game!.uri!.split("/").pop();
+ GenericHttpDownloader.startDownload(game, downloadLink, {
+ Cookie: `accountToken=${token}`,
+ });
+ break;
+ }
+ case Downloader.PixelDrain: {
+ const id = game!.uri!.split("/").pop();
- await GenericHttpDownloader.startDownload(
- game,
- `https://pixeldrain.com/api/file/${id}?download`
- );
- } else if (game.downloader === Downloader.Torrent) {
- PythonInstance.startDownload(game);
- } else if (game.downloader === Downloader.RealDebrid) {
- RealDebridDownloader.startDownload(game);
+ await GenericHttpDownloader.startDownload(
+ game,
+ `https://pixeldrain.com/api/file/${id}?download`
+ );
+ break;
+ }
+ case Downloader.Qiwi: {
+ const downloadUrl = await QiwiApi.getDownloadUrl(game.uri!);
+
+ await GenericHttpDownloader.startDownload(game, downloadUrl);
+ break;
+ }
+ case Downloader.Torrent:
+ PythonInstance.startDownload(game);
+ break;
+ case Downloader.RealDebrid:
+ RealDebridDownloader.startDownload(game);
}
this.currentDownloader = game.downloader;
diff --git a/src/main/services/hosters/index.ts b/src/main/services/hosters/index.ts
index 921c45b1..4c5b1803 100644
--- a/src/main/services/hosters/index.ts
+++ b/src/main/services/hosters/index.ts
@@ -1 +1,2 @@
export * from "./gofile";
+export * from "./qiwi";
diff --git a/src/main/services/how-long-to-beat.ts b/src/main/services/how-long-to-beat.ts
index 39b938c5..67e96942 100644
--- a/src/main/services/how-long-to-beat.ts
+++ b/src/main/services/how-long-to-beat.ts
@@ -1,5 +1,4 @@
import axios from "axios";
-import { JSDOM } from "jsdom";
import { requestWebPage } from "@main/helpers";
import { HowLongToBeatCategory } from "@types";
import { formatName } from "@shared";
@@ -52,10 +51,7 @@ const parseListItems = ($lis: Element[]) => {
export const getHowLongToBeatGame = async (
id: string
): Promise => {
- const response = await requestWebPage(`https://howlongtobeat.com/game/${id}`);
-
- const { window } = new JSDOM(response);
- const { document } = window;
+ const document = await requestWebPage(`https://howlongtobeat.com/game/${id}`);
const $ul = document.querySelector(".shadow_shadow ul");
if (!$ul) return [];
diff --git a/src/renderer/src/constants.ts b/src/renderer/src/constants.ts
index 7025df2a..63368c88 100644
--- a/src/renderer/src/constants.ts
+++ b/src/renderer/src/constants.ts
@@ -7,4 +7,5 @@ export const DOWNLOADER_NAME = {
[Downloader.Torrent]: "Torrent",
[Downloader.Gofile]: "Gofile",
[Downloader.PixelDrain]: "PixelDrain",
+ [Downloader.Qiwi]: "Qiwi",
};
diff --git a/src/renderer/src/pages/game-details/modals/download-settings-modal.css.ts b/src/renderer/src/pages/game-details/modals/download-settings-modal.css.ts
index d5655d94..5450378c 100644
--- a/src/renderer/src/pages/game-details/modals/download-settings-modal.css.ts
+++ b/src/renderer/src/pages/game-details/modals/download-settings-modal.css.ts
@@ -20,13 +20,16 @@ export const hintText = style({
});
export const downloaders = style({
- display: "flex",
+ display: "grid",
gap: `${SPACING_UNIT}px`,
+ gridTemplateColumns: "repeat(2, 1fr)",
});
export const downloaderOption = style({
- flex: "1",
position: "relative",
+ ":only-child": {
+ gridColumn: "1 / -1",
+ },
});
export const downloaderIcon = style({
diff --git a/src/shared/index.ts b/src/shared/index.ts
index 3ae476ee..28e7315b 100644
--- a/src/shared/index.ts
+++ b/src/shared/index.ts
@@ -3,6 +3,7 @@ export enum Downloader {
Torrent,
Gofile,
PixelDrain,
+ Qiwi,
}
export enum DownloadSourceStatus {
@@ -75,6 +76,7 @@ export const getDownloadersForUri = (uri: string) => {
if (uri.startsWith("https://gofile.io")) return [Downloader.Gofile];
if (uri.startsWith("https://pixeldrain.com")) return [Downloader.PixelDrain];
+ if (uri.startsWith("https://qiwi.gg")) return [Downloader.Qiwi];
if (realDebridHosts.some((host) => uri.startsWith(host)))
return [Downloader.RealDebrid];
From c1bd1d30d7a702587a54a803224cb5173c731106 Mon Sep 17 00:00:00 2001
From: Chubby Granny Chaser
Date: Sun, 18 Aug 2024 16:21:05 +0100
Subject: [PATCH 061/108] feat: adding support to qiwi
---
src/main/services/hosters/qiwi.ts | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 src/main/services/hosters/qiwi.ts
diff --git a/src/main/services/hosters/qiwi.ts b/src/main/services/hosters/qiwi.ts
new file mode 100644
index 00000000..e18b011c
--- /dev/null
+++ b/src/main/services/hosters/qiwi.ts
@@ -0,0 +1,15 @@
+import { requestWebPage } from "@main/helpers";
+
+export class QiwiApi {
+ public static async getDownloadUrl(url: string) {
+ const document = await requestWebPage(url);
+ const fileName = document.querySelector("h1")?.textContent;
+
+ const slug = url.split("/").pop();
+ const extension = fileName?.split(".").pop();
+
+ const downloadUrl = `https://spyderrock.com/${slug}.${extension}`;
+
+ return downloadUrl;
+ }
+}
From 5101684154d725b25d688c376e2c054e9b172552 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 19 Aug 2024 12:31:39 -0300
Subject: [PATCH 062/108] feat: add property
---
src/main/events/user/get-user.ts | 46 +++++++++++++++-----------------
src/types/index.ts | 1 +
2 files changed, 22 insertions(+), 25 deletions(-)
diff --git a/src/main/events/user/get-user.ts b/src/main/events/user/get-user.ts
index eb4f0619..bc4a58ad 100644
--- a/src/main/events/user/get-user.ts
+++ b/src/main/events/user/get-user.ts
@@ -1,7 +1,7 @@
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { steamGamesWorker } from "@main/workers";
-import { UserProfile } from "@types";
+import { UserGame, UserProfile } from "@types";
import { convertSteamGameToCatalogueEntry } from "../helpers/search-games";
import { getSteamAppAsset } from "@main/helpers";
import { getUserFriends } from "./get-user-friends";
@@ -20,35 +20,13 @@ const getUser = async (
const recentGames = await Promise.all(
profile.recentGames.map(async (game) => {
- const steamGame = await steamGamesWorker.run(Number(game.objectId), {
- name: "getById",
- });
- const iconUrl = steamGame?.clientIcon
- ? getSteamAppAsset("icon", game.objectId, steamGame.clientIcon)
- : null;
-
- return {
- ...game,
- ...convertSteamGameToCatalogueEntry(steamGame),
- iconUrl,
- };
+ return getSteamUserGame(game);
})
);
const libraryGames = await Promise.all(
profile.libraryGames.map(async (game) => {
- const steamGame = await steamGamesWorker.run(Number(game.objectId), {
- name: "getById",
- });
- const iconUrl = steamGame?.clientIcon
- ? getSteamAppAsset("icon", game.objectId, steamGame.clientIcon)
- : null;
-
- return {
- ...game,
- ...convertSteamGameToCatalogueEntry(steamGame),
- iconUrl,
- };
+ return getSteamUserGame(game);
})
);
@@ -58,10 +36,28 @@ const getUser = async (
recentGames,
friends: friends.friends,
totalFriends: friends.totalFriends,
+ currentGame: profile.currentGame
+ ? getSteamUserGame(profile.currentGame)
+ : null,
};
} catch (err) {
return null;
}
};
+const getSteamUserGame = async (game): Promise => {
+ const steamGame = await steamGamesWorker.run(Number(game.objectId), {
+ name: "getById",
+ });
+ const iconUrl = steamGame?.clientIcon
+ ? getSteamAppAsset("icon", game.objectId, steamGame.clientIcon)
+ : null;
+
+ return {
+ ...game,
+ ...convertSteamGameToCatalogueEntry(steamGame),
+ iconUrl,
+ };
+};
+
registerEvent("getUser", getUser);
diff --git a/src/types/index.ts b/src/types/index.ts
index 46ab5421..d97433ae 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -318,6 +318,7 @@ export interface UserProfile {
friends: UserFriend[];
totalFriends: number;
relation: UserRelation | null;
+ currentGame: UserGame | null;
}
export interface UpdateProfileProps {
From 5369274c6e1a5c67731bb6e484a2ee708c17532c Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 19 Aug 2024 12:42:05 -0300
Subject: [PATCH 063/108] feat: remove synchronize true from typeorm
---
.prettierignore | 1 +
src/main/data-source.ts | 36 ++++++-------
.../1715900413313-fix_repack_uploadDate.ts | 11 ----
...76027208-alter_lastTimePlayed_to_datime.ts | 49 ------------------
.../migrations/1724081695967-Hydra_2_0_3.ts | 50 +++++++++++++++++++
.../1724081984535-DowloadsRefactor.ts | 20 ++++++++
src/main/migrations/index.ts | 9 +---
7 files changed, 87 insertions(+), 89 deletions(-)
delete mode 100644 src/main/migrations/1715900413313-fix_repack_uploadDate.ts
delete mode 100644 src/main/migrations/1716776027208-alter_lastTimePlayed_to_datime.ts
create mode 100644 src/main/migrations/1724081695967-Hydra_2_0_3.ts
create mode 100644 src/main/migrations/1724081984535-DowloadsRefactor.ts
diff --git a/.prettierignore b/.prettierignore
index 05d298a1..9b6e9df6 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -5,3 +5,4 @@ pnpm-lock.yaml
LICENSE.md
tsconfig.json
tsconfig.*.json
+src/main/migrations
diff --git a/src/main/data-source.ts b/src/main/data-source.ts
index a88a8883..446ccbdc 100644
--- a/src/main/data-source.ts
+++ b/src/main/data-source.ts
@@ -8,30 +8,22 @@ import {
UserPreferences,
UserAuth,
} from "@main/entity";
-import type { BetterSqlite3ConnectionOptions } from "typeorm/driver/better-sqlite3/BetterSqlite3ConnectionOptions";
import { databasePath } from "./constants";
-import migrations from "./migrations";
+import * as migrations from "./migrations";
-export const createDataSource = (
- options: Partial
-) =>
- new DataSource({
- type: "better-sqlite3",
- entities: [
- Game,
- Repack,
- UserPreferences,
- GameShopCache,
- DownloadSource,
- DownloadQueue,
- UserAuth,
- ],
- synchronize: true,
- database: databasePath,
- ...options,
- });
-
-export const dataSource = createDataSource({
+export const dataSource = new DataSource({
+ type: "better-sqlite3",
+ entities: [
+ Game,
+ Repack,
+ UserPreferences,
+ GameShopCache,
+ DownloadSource,
+ DownloadQueue,
+ UserAuth,
+ ],
+ synchronize: true,
+ database: databasePath,
migrations,
});
diff --git a/src/main/migrations/1715900413313-fix_repack_uploadDate.ts b/src/main/migrations/1715900413313-fix_repack_uploadDate.ts
deleted file mode 100644
index e9d0a6c2..00000000
--- a/src/main/migrations/1715900413313-fix_repack_uploadDate.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { MigrationInterface, QueryRunner } from "typeorm";
-
-export class FixRepackUploadDate1715900413313 implements MigrationInterface {
- public async up(_: QueryRunner): Promise {
- return;
- }
-
- public async down(_: QueryRunner): Promise {
- return;
- }
-}
diff --git a/src/main/migrations/1716776027208-alter_lastTimePlayed_to_datime.ts b/src/main/migrations/1716776027208-alter_lastTimePlayed_to_datime.ts
deleted file mode 100644
index 6a562915..00000000
--- a/src/main/migrations/1716776027208-alter_lastTimePlayed_to_datime.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { Game } from "@main/entity";
-import { MigrationInterface, QueryRunner } from "typeorm";
-
-export class AlterLastTimePlayedToDatime1716776027208
- implements MigrationInterface
-{
- public async up(queryRunner: QueryRunner): Promise {
- // 2024-05-27 02:08:17
- // Mon, 27 May 2024 02:08:17 GMT
- const updateLastTimePlayedValues = `
- UPDATE game SET lastTimePlayed = (SELECT
- SUBSTR(lastTimePlayed, 13, 4) || '-' || -- Year
- CASE SUBSTR(lastTimePlayed, 9, 3)
- WHEN 'Jan' THEN '01'
- WHEN 'Feb' THEN '02'
- WHEN 'Mar' THEN '03'
- WHEN 'Apr' THEN '04'
- WHEN 'May' THEN '05'
- WHEN 'Jun' THEN '06'
- WHEN 'Jul' THEN '07'
- WHEN 'Aug' THEN '08'
- WHEN 'Sep' THEN '09'
- WHEN 'Oct' THEN '10'
- WHEN 'Nov' THEN '11'
- WHEN 'Dec' THEN '12'
- END || '-' || -- Month
- SUBSTR(lastTimePlayed, 6, 2) || ' ' || -- Day
- SUBSTR(lastTimePlayed, 18, 8) -- hh:mm:ss;
- FROM game)
- WHERE lastTimePlayed IS NOT NULL;
- `;
-
- await queryRunner.query(updateLastTimePlayedValues);
- }
-
- public async down(queryRunner: QueryRunner): Promise {
- const queryBuilder = queryRunner.manager.createQueryBuilder(Game, "game");
-
- const result = await queryBuilder.getMany();
-
- for (const game of result) {
- if (!game.lastTimePlayed) continue;
- await queryRunner.query(
- `UPDATE game set lastTimePlayed = ? WHERE id = ?;`,
- [game.lastTimePlayed.toUTCString(), game.id]
- );
- }
- }
-}
diff --git a/src/main/migrations/1724081695967-Hydra_2_0_3.ts b/src/main/migrations/1724081695967-Hydra_2_0_3.ts
new file mode 100644
index 00000000..5ab18acb
--- /dev/null
+++ b/src/main/migrations/1724081695967-Hydra_2_0_3.ts
@@ -0,0 +1,50 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class Hydra2031724081695967 implements MigrationInterface {
+ name = 'Hydra2031724081695967'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_source" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "url" text, "name" text NOT NULL, "etag" text, "downloadCount" integer NOT NULL DEFAULT (0), "status" text NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), CONSTRAINT "UQ_aec2879321a87e9bb2ed477981a" UNIQUE ("url"))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "user_preferences" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "downloadsPath" text, "language" text NOT NULL DEFAULT ('en'), "realDebridApiToken" text, "downloadNotificationsEnabled" boolean NOT NULL DEFAULT (0), "repackUpdatesNotificationsEnabled" boolean NOT NULL DEFAULT (0), "preferQuitInsteadOfHiding" boolean NOT NULL DEFAULT (0), "runAtStartup" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game_shop_cache" ("objectID" text PRIMARY KEY NOT NULL, "shop" text NOT NULL, "serializedData" text, "howLongToBeatSerializedData" text, "language" text, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "user_auth" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "userId" text NOT NULL DEFAULT (''), "displayName" text NOT NULL DEFAULT (''), "profileImageUrl" text, "accessToken" text NOT NULL DEFAULT (''), "refreshToken" text NOT NULL DEFAULT (''), "tokenExpirationTimestamp" integer NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"), CONSTRAINT "FK_0c1d6445ad047d9bbd256f961f6" FOREIGN KEY ("repackId") REFERENCES "repack" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
+ await queryRunner.query(`INSERT INTO "temporary_game"("id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId") SELECT "id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId" FROM "game"`);
+ await queryRunner.query(`DROP TABLE "game"`);
+ await queryRunner.query(`ALTER TABLE "temporary_game" RENAME TO "game"`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
+ await queryRunner.query(`INSERT INTO "temporary_repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`);
+ await queryRunner.query(`DROP TABLE "repack"`);
+ await queryRunner.query(`ALTER TABLE "temporary_repack" RENAME TO "repack"`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"), CONSTRAINT "FK_aed852c94d9ded617a7a07f5415" FOREIGN KEY ("gameId") REFERENCES "game" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
+ await queryRunner.query(`INSERT INTO "temporary_download_queue"("id", "createdAt", "updatedAt", "gameId") SELECT "id", "createdAt", "updatedAt", "gameId" FROM "download_queue"`);
+ await queryRunner.query(`DROP TABLE "download_queue"`);
+ await queryRunner.query(`ALTER TABLE "temporary_download_queue" RENAME TO "download_queue"`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "download_queue" RENAME TO "temporary_download_queue"`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"))`);
+ await queryRunner.query(`INSERT INTO "download_queue"("id", "createdAt", "updatedAt", "gameId") SELECT "id", "createdAt", "updatedAt", "gameId" FROM "temporary_download_queue"`);
+ await queryRunner.query(`DROP TABLE "temporary_download_queue"`);
+ await queryRunner.query(`ALTER TABLE "repack" RENAME TO "temporary_repack"`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"))`);
+ await queryRunner.query(`INSERT INTO "repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`);
+ await queryRunner.query(`DROP TABLE "temporary_repack"`);
+ await queryRunner.query(`ALTER TABLE "game" RENAME TO "temporary_game"`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"))`);
+ await queryRunner.query(`INSERT INTO "game"("id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId") SELECT "id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId" FROM "temporary_game"`);
+ await queryRunner.query(`DROP TABLE "temporary_game"`);
+ await queryRunner.query(`DROP TABLE "user_auth"`);
+ await queryRunner.query(`DROP TABLE "download_queue"`);
+ await queryRunner.query(`DROP TABLE "game_shop_cache"`);
+ await queryRunner.query(`DROP TABLE "user_preferences"`);
+ await queryRunner.query(`DROP TABLE "repack"`);
+ await queryRunner.query(`DROP TABLE "download_source"`);
+ await queryRunner.query(`DROP TABLE "game"`);
+ }
+
+}
diff --git a/src/main/migrations/1724081984535-DowloadsRefactor.ts b/src/main/migrations/1724081984535-DowloadsRefactor.ts
new file mode 100644
index 00000000..3afc8444
--- /dev/null
+++ b/src/main/migrations/1724081984535-DowloadsRefactor.ts
@@ -0,0 +1,20 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class DowloadsRefactor1724081984535 implements MigrationInterface {
+ name = 'DowloadsRefactor1724081984535'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`CREATE TABLE "temporary_repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, "uris" text NOT NULL DEFAULT ('[]'), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
+ await queryRunner.query(`INSERT INTO "temporary_repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`);
+ await queryRunner.query(`DROP TABLE "repack"`);
+ await queryRunner.query(`ALTER TABLE "temporary_repack" RENAME TO "repack"`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "repack" RENAME TO "temporary_repack"`);
+ await queryRunner.query(`CREATE TABLE "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
+ await queryRunner.query(`INSERT INTO "repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`);
+ await queryRunner.query(`DROP TABLE "temporary_repack"`);
+ }
+
+}
diff --git a/src/main/migrations/index.ts b/src/main/migrations/index.ts
index c0c96e45..5546bce0 100644
--- a/src/main/migrations/index.ts
+++ b/src/main/migrations/index.ts
@@ -1,7 +1,2 @@
-import { FixRepackUploadDate1715900413313 } from "./1715900413313-fix_repack_uploadDate";
-import { AlterLastTimePlayedToDatime1716776027208 } from "./1716776027208-alter_lastTimePlayed_to_datime";
-
-export default [
- FixRepackUploadDate1715900413313,
- AlterLastTimePlayedToDatime1716776027208,
-];
+export * from "./1724081695967-Hydra_2_0_3";
+export * from "./1724081984535-DowloadsRefactor";
From bde396c7dbfc16ab29323bbeb8abc91c995f99ad Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 19 Aug 2024 19:55:27 -0300
Subject: [PATCH 064/108] feat: update endpoints
---
src/main/events/library/remove-game-from-library.ts | 2 +-
src/main/events/user/block-user.ts | 2 +-
src/main/events/user/get-user-friends.ts | 2 +-
src/main/events/user/get-user.ts | 2 +-
src/main/events/user/unblock-user.ts | 2 +-
src/main/services/library-sync/create-game.ts | 2 +-
src/main/services/library-sync/merge-with-remote-games.ts | 2 +-
src/main/services/library-sync/update-game-playtime.ts | 2 +-
src/main/services/library-sync/upload-games-batch.ts | 2 +-
9 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/main/events/library/remove-game-from-library.ts b/src/main/events/library/remove-game-from-library.ts
index 468f5b26..a8fc8b01 100644
--- a/src/main/events/library/remove-game-from-library.ts
+++ b/src/main/events/library/remove-game-from-library.ts
@@ -20,7 +20,7 @@ const removeRemoveGameFromLibrary = async (gameId: number) => {
const game = await gameRepository.findOne({ where: { id: gameId } });
if (game?.remoteId) {
- HydraApi.delete(`/games/${game.remoteId}`).catch(() => {});
+ HydraApi.delete(`/profile/games/${game.remoteId}`).catch(() => {});
}
};
diff --git a/src/main/events/user/block-user.ts b/src/main/events/user/block-user.ts
index 8003f478..c81231e5 100644
--- a/src/main/events/user/block-user.ts
+++ b/src/main/events/user/block-user.ts
@@ -5,7 +5,7 @@ const blockUser = async (
_event: Electron.IpcMainInvokeEvent,
userId: string
) => {
- await HydraApi.post(`/user/${userId}/block`);
+ await HydraApi.post(`/users/${userId}/block`);
};
registerEvent("blockUser", blockUser);
diff --git a/src/main/events/user/get-user-friends.ts b/src/main/events/user/get-user-friends.ts
index 28783459..5ff4c8a4 100644
--- a/src/main/events/user/get-user-friends.ts
+++ b/src/main/events/user/get-user-friends.ts
@@ -14,7 +14,7 @@ export const getUserFriends = async (
return HydraApi.get(`/profile/friends`, { take, skip });
}
- return HydraApi.get(`/user/${userId}/friends`, { take, skip });
+ return HydraApi.get(`/users/${userId}/friends`, { take, skip });
};
const getUserFriendsEvent = async (
diff --git a/src/main/events/user/get-user.ts b/src/main/events/user/get-user.ts
index bc4a58ad..d3be856a 100644
--- a/src/main/events/user/get-user.ts
+++ b/src/main/events/user/get-user.ts
@@ -12,7 +12,7 @@ const getUser = async (
): Promise => {
try {
const [profile, friends] = await Promise.all([
- HydraApi.get(`/user/${userId}`),
+ HydraApi.get(`/users/${userId}`),
getUserFriends(userId, 12, 0).catch(() => {
return { totalFriends: 0, friends: [] };
}),
diff --git a/src/main/events/user/unblock-user.ts b/src/main/events/user/unblock-user.ts
index ac678dbd..c604a0b5 100644
--- a/src/main/events/user/unblock-user.ts
+++ b/src/main/events/user/unblock-user.ts
@@ -5,7 +5,7 @@ const unblockUser = async (
_event: Electron.IpcMainInvokeEvent,
userId: string
) => {
- await HydraApi.post(`/user/${userId}/unblock`);
+ await HydraApi.post(`/users/${userId}/unblock`);
};
registerEvent("unblockUser", unblockUser);
diff --git a/src/main/services/library-sync/create-game.ts b/src/main/services/library-sync/create-game.ts
index b66a1897..6699788c 100644
--- a/src/main/services/library-sync/create-game.ts
+++ b/src/main/services/library-sync/create-game.ts
@@ -3,7 +3,7 @@ import { HydraApi } from "../hydra-api";
import { gameRepository } from "@main/repository";
export const createGame = async (game: Game) => {
- HydraApi.post(`/games`, {
+ HydraApi.post(`/profile/games`, {
objectId: game.objectID,
playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds),
shop: game.shop,
diff --git a/src/main/services/library-sync/merge-with-remote-games.ts b/src/main/services/library-sync/merge-with-remote-games.ts
index 2a6b5bb5..2b3f51b3 100644
--- a/src/main/services/library-sync/merge-with-remote-games.ts
+++ b/src/main/services/library-sync/merge-with-remote-games.ts
@@ -4,7 +4,7 @@ import { steamGamesWorker } from "@main/workers";
import { getSteamAppAsset } from "@main/helpers";
export const mergeWithRemoteGames = async () => {
- return HydraApi.get("/games")
+ return HydraApi.get("/profile/games")
.then(async (response) => {
for (const game of response) {
const localGame = await gameRepository.findOne({
diff --git a/src/main/services/library-sync/update-game-playtime.ts b/src/main/services/library-sync/update-game-playtime.ts
index 39206a12..5cfc4103 100644
--- a/src/main/services/library-sync/update-game-playtime.ts
+++ b/src/main/services/library-sync/update-game-playtime.ts
@@ -6,7 +6,7 @@ export const updateGamePlaytime = async (
deltaInMillis: number,
lastTimePlayed: Date
) => {
- HydraApi.put(`/games/${game.remoteId}`, {
+ HydraApi.put(`/profile/games/${game.remoteId}`, {
playTimeDeltaInSeconds: Math.trunc(deltaInMillis / 1000),
lastTimePlayed,
}).catch(() => {});
diff --git a/src/main/services/library-sync/upload-games-batch.ts b/src/main/services/library-sync/upload-games-batch.ts
index 88f02375..22dc595e 100644
--- a/src/main/services/library-sync/upload-games-batch.ts
+++ b/src/main/services/library-sync/upload-games-batch.ts
@@ -14,7 +14,7 @@ export const uploadGamesBatch = async () => {
for (const chunk of gamesChunks) {
await HydraApi.post(
- "/games/batch",
+ "/profile/games/batch",
chunk.map((game) => {
return {
objectId: game.objectID,
From 629b005ea447cc0b0a944ca52bdeab9ed079ff3e Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 19 Aug 2024 19:56:20 -0300
Subject: [PATCH 065/108] feat: show current game on other users profile
---
src/main/events/user/get-user.ts | 21 +++++++++---
.../components/sidebar/sidebar-profile.tsx | 2 +-
src/renderer/src/pages/user/user-content.tsx | 32 +++++++++++++------
src/types/index.ts | 5 ++-
4 files changed, 43 insertions(+), 17 deletions(-)
diff --git a/src/main/events/user/get-user.ts b/src/main/events/user/get-user.ts
index d3be856a..68d69969 100644
--- a/src/main/events/user/get-user.ts
+++ b/src/main/events/user/get-user.ts
@@ -1,7 +1,7 @@
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { steamGamesWorker } from "@main/workers";
-import { UserGame, UserProfile } from "@types";
+import { GameRunning, UserGame, UserProfile } from "@types";
import { convertSteamGameToCatalogueEntry } from "../helpers/search-games";
import { getSteamAppAsset } from "@main/helpers";
import { getUserFriends } from "./get-user-friends";
@@ -30,21 +30,34 @@ const getUser = async (
})
);
+ const currentGame = await getGameRunning(profile.currentGame);
+
return {
...profile,
libraryGames,
recentGames,
friends: friends.friends,
totalFriends: friends.totalFriends,
- currentGame: profile.currentGame
- ? getSteamUserGame(profile.currentGame)
- : null,
+ currentGame,
};
} catch (err) {
return null;
}
};
+const getGameRunning = async (currentGame): Promise => {
+ if (!currentGame) {
+ return null;
+ }
+
+ const gameRunning = await getSteamUserGame(currentGame);
+
+ return {
+ ...gameRunning,
+ sessionDurationInMillis: currentGame.sessionDurationInSeconds * 1000,
+ };
+};
+
const getSteamUserGame = async (game): Promise => {
const steamGame = await steamGamesWorker.run(Number(game.objectId), {
name: "getById",
diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx
index 81736e37..069831cb 100644
--- a/src/renderer/src/components/sidebar/sidebar-profile.tsx
+++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx
@@ -78,7 +78,7 @@ export function SidebarProfile() {
)}
- {userDetails && gameRunning && (
+ {userDetails && gameRunning?.iconUrl && (
(null);
const { gameRunning } = useAppSelector((state) => state.gameRunning);
@@ -113,6 +118,15 @@ export function UserContent({
const isMe = userDetails?.id == userProfile.id;
+ useEffect(() => {
+ if (isMe && gameRunning) {
+ setCurrentGame(gameRunning);
+ return;
+ }
+
+ setCurrentGame(userProfile.currentGame);
+ }, [gameRunning, isMe]);
+
useEffect(() => {
if (isMe) fetchFriendRequests();
}, [isMe, fetchFriendRequests]);
@@ -284,10 +298,10 @@ export function UserContent({
position: "relative",
}}
>
- {gameRunning && isMe && (
+ {currentGame && (
)}
@@ -315,7 +329,7 @@ export function UserContent({
{userProfile.displayName}
- {isMe && gameRunning && (
+ {currentGame && (
-
- {gameRunning.title}
+
+ {currentGame.title}
{t("playing_for", {
amount: formatDiffInMillis(
- gameRunning.sessionDurationInMillis,
+ currentGame.sessionDurationInMillis,
new Date()
),
})}
diff --git a/src/types/index.ts b/src/types/index.ts
index d97433ae..65c73e99 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -141,9 +141,8 @@ export interface Game {
export type LibraryGame = Omit;
export interface GameRunning {
- id: number;
title: string;
- iconUrl: string;
+ iconUrl: string | null;
objectID: string;
shop: GameShop;
sessionDurationInMillis: number;
@@ -318,7 +317,7 @@ export interface UserProfile {
friends: UserFriend[];
totalFriends: number;
relation: UserRelation | null;
- currentGame: UserGame | null;
+ currentGame: GameRunning | null;
}
export interface UpdateProfileProps {
From 856a4c706a9d77cedd765b731d54f613e3fbdee9 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 19 Aug 2024 20:02:41 -0300
Subject: [PATCH 066/108] feat: remove unused vars from ci
---
.env.example | 1 +
.github/workflows/build.yml | 4 ----
.github/workflows/release.yml | 4 ----
src/types/index.ts | 1 +
4 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/.env.example b/.env.example
index c2ad43d9..47d1a1e3 100644
--- a/.env.example
+++ b/.env.example
@@ -1,3 +1,4 @@
MAIN_VITE_STEAMGRIDDB_API_KEY=YOUR_API_KEY
MAIN_VITE_API_URL=API_URL
MAIN_VITE_SENTRY_DSN=YOUR_SENTRY_DSN
+SENTRY_AUTH_TOKEN=
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 20a00ccf..b55b280e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -37,8 +37,6 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: yarn build:linux
env:
- MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }}
- MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }}
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
@@ -48,8 +46,6 @@ jobs:
if: matrix.os == 'windows-latest'
run: yarn build:win
env:
- MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }}
- MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }}
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a0684c6c..d1bc8993 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -39,8 +39,6 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: yarn build:linux
env:
- MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }}
- MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }}
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
@@ -50,8 +48,6 @@ jobs:
if: matrix.os == 'windows-latest'
run: yarn build:win
env:
- MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }}
- MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }}
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
MAIN_VITE_SENTRY_DSN: ${{ vars.MAIN_VITE_SENTRY_DSN }}
diff --git a/src/types/index.ts b/src/types/index.ts
index 65c73e99..3260d274 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -141,6 +141,7 @@ export interface Game {
export type LibraryGame = Omit;
export interface GameRunning {
+ id?: number;
title: string;
iconUrl: string | null;
objectID: string;
From bab041b5f7780f57d5afebf3e93022054161f1e8 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 19 Aug 2024 20:53:34 -0300
Subject: [PATCH 067/108] feat: call update game endpoint on process watcher at
each 120 ticks
---
src/main/services/main-loop.ts | 2 +-
src/main/services/process-watcher.ts | 112 ++++++++++++++++++---------
2 files changed, 76 insertions(+), 38 deletions(-)
diff --git a/src/main/services/main-loop.ts b/src/main/services/main-loop.ts
index ca72707f..f2ec51ba 100644
--- a/src/main/services/main-loop.ts
+++ b/src/main/services/main-loop.ts
@@ -10,6 +10,6 @@ export const startMainLoop = async () => {
DownloadManager.watchDownloads(),
]);
- await sleep(500);
+ await sleep(1000);
}
};
diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts
index 0f7efa62..080f1efc 100644
--- a/src/main/services/process-watcher.ts
+++ b/src/main/services/process-watcher.ts
@@ -4,12 +4,16 @@ import { WindowManager } from "./window-manager";
import { createGame, updateGamePlaytime } from "./library-sync";
import { GameRunning } from "@types";
import { PythonInstance } from "./download";
+import { Game } from "@main/entity";
export const gamesPlaytime = new Map<
number,
- { lastTick: number; firstTick: number }
+ { lastTick: number; firstTick: number; lastSyncTick: number }
>();
+const TICKS_TO_UPDATE_API = 120;
+let currentTick = 1;
+
export const watchProcesses = async () => {
const games = await gameRepository.find({
where: {
@@ -30,48 +34,17 @@ export const watchProcesses = async () => {
if (gameProcess) {
if (gamesPlaytime.has(game.id)) {
- const gamePlaytime = gamesPlaytime.get(game.id)!;
-
- const zero = gamePlaytime.lastTick;
- const delta = performance.now() - zero;
-
- await gameRepository.update(game.id, {
- playTimeInMilliseconds: game.playTimeInMilliseconds + delta,
- lastTimePlayed: new Date(),
- });
-
- gamesPlaytime.set(game.id, {
- ...gamePlaytime,
- lastTick: performance.now(),
- });
+ onTickGame(game);
} else {
- if (game.remoteId) {
- updateGamePlaytime(game, 0, new Date());
- } else {
- createGame({ ...game, lastTimePlayed: new Date() });
- }
-
- gamesPlaytime.set(game.id, {
- lastTick: performance.now(),
- firstTick: performance.now(),
- });
+ onOpenGame(game);
}
} else if (gamesPlaytime.has(game.id)) {
- const gamePlaytime = gamesPlaytime.get(game.id)!;
- gamesPlaytime.delete(game.id);
-
- if (game.remoteId) {
- updateGamePlaytime(
- game,
- performance.now() - gamePlaytime.firstTick,
- game.lastTimePlayed!
- );
- } else {
- createGame(game);
- }
+ onCloseGame(game);
}
}
+ currentTick++;
+
if (WindowManager.mainWindow) {
const gamesRunning = Array.from(gamesPlaytime.entries()).map((entry) => {
return {
@@ -86,3 +59,68 @@ export const watchProcesses = async () => {
);
}
};
+
+function onOpenGame(game: Game) {
+ const now = performance.now();
+
+ gamesPlaytime.set(game.id, {
+ lastTick: now,
+ firstTick: now,
+ lastSyncTick: now,
+ });
+
+ if (game.remoteId) {
+ updateGamePlaytime(game, 0, new Date());
+ } else {
+ createGame({ ...game, lastTimePlayed: new Date() });
+ }
+}
+
+function onTickGame(game: Game) {
+ const now = performance.now();
+ const gamePlaytime = gamesPlaytime.get(game.id)!;
+
+ const delta = now - gamePlaytime.lastTick;
+
+ gameRepository.update(game.id, {
+ playTimeInMilliseconds: game.playTimeInMilliseconds + delta,
+ lastTimePlayed: new Date(),
+ });
+
+ gamesPlaytime.set(game.id, {
+ ...gamePlaytime,
+ lastTick: now,
+ });
+
+ if (currentTick % TICKS_TO_UPDATE_API === 0) {
+ if (game.remoteId) {
+ updateGamePlaytime(
+ game,
+ now - gamePlaytime.lastSyncTick,
+ game.lastTimePlayed!
+ );
+ } else {
+ createGame(game);
+ }
+
+ gamesPlaytime.set(game.id, {
+ ...gamePlaytime,
+ lastSyncTick: now,
+ });
+ }
+}
+
+const onCloseGame = (game: Game) => {
+ const gamePlaytime = gamesPlaytime.get(game.id)!;
+ gamesPlaytime.delete(game.id);
+
+ if (game.remoteId) {
+ updateGamePlaytime(
+ game,
+ performance.now() - gamePlaytime.firstTick,
+ game.lastTimePlayed!
+ );
+ } else {
+ createGame(game);
+ }
+};
From ac6ff04e69b44e51057978bf088aa62c0fd722fe Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 19 Aug 2024 21:04:51 -0300
Subject: [PATCH 068/108] chore: lint warnings
---
.../user-friend-modal/user-friend-modal-list.tsx | 6 +++---
src/renderer/src/pages/user/user-content.tsx | 2 +-
.../user/user-profile-settings-modal/user-block-list.tsx | 6 +++---
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx
index b760351c..8ef96baf 100644
--- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx
+++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx
@@ -62,9 +62,9 @@ export const UserFriendModalList = ({
};
useEffect(() => {
- listContainer.current?.addEventListener("scroll", handleScroll);
- return () =>
- listContainer.current?.removeEventListener("scroll", handleScroll);
+ const container = listContainer.current;
+ container?.addEventListener("scroll", handleScroll);
+ return () => container?.removeEventListener("scroll", handleScroll);
}, [isLoading]);
const reloadList = () => {
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index 90defa8c..c334389e 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -125,7 +125,7 @@ export function UserContent({
}
setCurrentGame(userProfile.currentGame);
- }, [gameRunning, isMe]);
+ }, [gameRunning, isMe, userProfile.currentGame]);
useEffect(() => {
if (isMe) fetchFriendRequests();
diff --git a/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx b/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
index c062eabb..0790b725 100644
--- a/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
+++ b/src/renderer/src/pages/user/user-profile-settings-modal/user-block-list.tsx
@@ -51,9 +51,9 @@ export const UserEditProfileBlockList = () => {
};
useEffect(() => {
- listContainer.current?.addEventListener("scroll", handleScroll);
- return () =>
- listContainer.current?.removeEventListener("scroll", handleScroll);
+ const container = listContainer.current;
+ container?.addEventListener("scroll", handleScroll);
+ return () => container?.removeEventListener("scroll", handleScroll);
}, [isLoading]);
const reloadList = () => {
From c72fa08d40f5a5ede6704e6fff21977fdbdca209 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 19 Aug 2024 21:36:07 -0300
Subject: [PATCH 069/108] feat: undo api logs comment
---
src/main/services/hydra-api.ts | 86 +++++++++++++++++-----------------
1 file changed, 43 insertions(+), 43 deletions(-)
diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts
index 6f0e1905..120d27ac 100644
--- a/src/main/services/hydra-api.ts
+++ b/src/main/services/hydra-api.ts
@@ -77,54 +77,54 @@ export class HydraApi {
baseURL: import.meta.env.MAIN_VITE_API_URL,
});
- // this.instance.interceptors.request.use(
- // (request) => {
- // logger.log(" ---- REQUEST -----");
- // logger.log(request.method, request.url, request.params, request.data);
- // return request;
- // },
- // (error) => {
- // logger.error("request error", error);
- // return Promise.reject(error);
- // }
- // );
+ this.instance.interceptors.request.use(
+ (request) => {
+ logger.log(" ---- REQUEST -----");
+ logger.log(request.method, request.url, request.params, request.data);
+ return request;
+ },
+ (error) => {
+ logger.error("request error", error);
+ return Promise.reject(error);
+ }
+ );
- // this.instance.interceptors.response.use(
- // (response) => {
- // logger.log(" ---- RESPONSE -----");
- // logger.log(
- // response.status,
- // response.config.method,
- // response.config.url,
- // response.data
- // );
- // return response;
- // },
- // (error) => {
- // logger.error(" ---- RESPONSE ERROR -----");
+ this.instance.interceptors.response.use(
+ (response) => {
+ logger.log(" ---- RESPONSE -----");
+ logger.log(
+ response.status,
+ response.config.method,
+ response.config.url,
+ response.data
+ );
+ return response;
+ },
+ (error) => {
+ logger.error(" ---- RESPONSE ERROR -----");
- // const { config } = error;
+ const { config } = error;
- // logger.error(
- // config.method,
- // config.baseURL,
- // config.url,
- // config.headers,
- // config.data
- // );
+ logger.error(
+ config.method,
+ config.baseURL,
+ config.url,
+ config.headers,
+ config.data
+ );
- // if (error.response) {
- // logger.error("Response", error.response.status, error.response.data);
- // } else if (error.request) {
- // logger.error("Request", error.request);
- // } else {
- // logger.error("Error", error.message);
- // }
+ if (error.response) {
+ logger.error("Response", error.response.status, error.response.data);
+ } else if (error.request) {
+ logger.error("Request", error.request);
+ } else {
+ logger.error("Error", error.message);
+ }
- // logger.error(" ----- END RESPONSE ERROR -------");
- // return Promise.reject(error);
- // }
- // );
+ logger.error(" ----- END RESPONSE ERROR -------");
+ return Promise.reject(error);
+ }
+ );
const userAuth = await userAuthRepository.findOne({
where: { id: 1 },
From 52069f703628d8ea06c443b585603082f79f8901 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Tue, 20 Aug 2024 21:27:34 -0300
Subject: [PATCH 070/108] feat: set typeorm synchrozine to false
---
src/main/data-source.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/data-source.ts b/src/main/data-source.ts
index 446ccbdc..826063e1 100644
--- a/src/main/data-source.ts
+++ b/src/main/data-source.ts
@@ -23,7 +23,7 @@ export const dataSource = new DataSource({
DownloadQueue,
UserAuth,
],
- synchronize: true,
+ synchronize: false,
database: databasePath,
migrations,
});
From 1542b346af0299c47069e1ded41011ce04d3be9d Mon Sep 17 00:00:00 2001
From: Antecess <59663394+xxDRV@users.noreply.github.com>
Date: Fri, 23 Aug 2024 01:22:28 +0500
Subject: [PATCH 071/108] Updated RU to fit new features
---
src/locales/ru/translation.json | 37 +++++++++++++++++++++++++++++----
1 file changed, 33 insertions(+), 4 deletions(-)
diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json
index f6f18d11..89d16b28 100644
--- a/src/locales/ru/translation.json
+++ b/src/locales/ru/translation.json
@@ -177,9 +177,6 @@
"download_count_zero": "В списке нет загрузок",
"download_count_one": "{{countFormatted}} загрузка в списке",
"download_count_other": "{{countFormatted}} загрузок в списке",
- "download_options_zero": "Нет доступных загрузок",
- "download_options_one": "{{countFormatted}} вариант загрузки доступен",
- "download_options_other": "{{countFormatted}} вариантов загрузки доступно",
"download_source_url": "Ссылка на источник",
"add_download_source_description": "Вставьте ссылку на .json-файл",
"download_source_up_to_date": "Обновлён",
@@ -241,6 +238,38 @@
"successfully_signed_out": "Успешный выход из аккаунта",
"sign_out": "Выйти",
"playing_for": "Сыграно {{amount}}",
- "sign_out_modal_text": "Ваша библиотека связана с текущей учетной записью. При выходе из системы ваша библиотека станет недоступна, и прогресс не будет сохранен. Выйти?"
+ "sign_out_modal_text": "Ваша библиотека связана с текущей учетной записью. При выходе из системы ваша библиотека станет недоступна, и прогресс не будет сохранен. Выйти?",
+ "add_friends": "Добавить друзей",
+ "add": "Добавить",
+ "friend_code": "Код друга",
+ "see_profile": "Просмотреть профиль",
+ "sending": "Отправка",
+ "friend_request_sent": "Запрос в друзья отправлен",
+ "friends": "Друзья",
+ "friends_list": "Список друзей",
+ "user_not_found": "Пользователь не найден",
+ "block_user": "Заблокировать пользователя",
+ "add_friend": "Добавить друга",
+ "request_sent": "Запрос отправлен",
+ "request_received": "Запрос получен",
+ "accept_request": "Принять запрос",
+ "ignore_request": "Игнорировать запрос",
+ "cancel_request": "Отменить запрос",
+ "undo_friendship": "Удалить друга",
+ "request_accepted": "Запрос принят",
+ "user_blocked_successfully": "Пользователь успешно заблокирован",
+ "user_block_modal_text": "{{displayName}} будет заблокирован",
+ "settings": "Настройки",
+ "public": "Публичный",
+ "private": "Приватный",
+ "friends_only": "Только друзья",
+ "privacy": "Приватность",
+ "blocked_users": "Заблокированные пользователи",
+ "unblock": "Разблокировать",
+ "no_friends_added": "Вы ещё не добавили ни одного друга",
+ "pending": "Ожидание",
+ "no_pending_invites": "У вас нет запросов ожидающих ответа",
+ "no_blocked_users": "Вы не заблокировали ни одного пользователя",
+ "friend_code_copied": "Код друга скопирован"
}
}
From 81aa25fa5293d2d17e67b8c65012ccf50823a605 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Thu, 22 Aug 2024 23:50:35 -0300
Subject: [PATCH 072/108] feat: write migrations
---
.prettierignore | 1 -
package.json | 6 +-
src/main/data-source.ts | 4 +-
src/main/entity/repack.entity.ts | 6 -
src/main/index.ts | 17 ++-
src/main/knex.ts | 14 +++
.../migrations/1724081695967-Hydra_2_0_3.ts | 50 --------
.../1724081984535-DowloadsRefactor.ts | 20 ----
src/main/migrations/1_Hydra_2_0_3.ts | 110 ++++++++++++++++++
src/main/migrations/2_DowloadsRefactor.ts | 47 ++++++++
src/main/migrations/index.ts | 2 -
yarn.lock | 86 ++++++++++++--
12 files changed, 270 insertions(+), 93 deletions(-)
create mode 100644 src/main/knex.ts
delete mode 100644 src/main/migrations/1724081695967-Hydra_2_0_3.ts
delete mode 100644 src/main/migrations/1724081984535-DowloadsRefactor.ts
create mode 100644 src/main/migrations/1_Hydra_2_0_3.ts
create mode 100644 src/main/migrations/2_DowloadsRefactor.ts
delete mode 100644 src/main/migrations/index.ts
diff --git a/.prettierignore b/.prettierignore
index 9b6e9df6..05d298a1 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -5,4 +5,3 @@ pnpm-lock.yaml
LICENSE.md
tsconfig.json
tsconfig.*.json
-src/main/migrations
diff --git a/package.json b/package.json
index aa77084e..113b2e20 100644
--- a/package.json
+++ b/package.json
@@ -28,8 +28,7 @@
"build:win": "electron-vite build && electron-builder --win",
"build:mac": "electron-vite build && electron-builder --mac",
"build:linux": "electron-vite build && electron-builder --linux",
- "prepare": "husky",
- "typeorm:migration-create": "yarn typeorm migration:create"
+ "prepare": "husky"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
@@ -43,7 +42,7 @@
"@vanilla-extract/recipes": "^0.5.2",
"auto-launch": "^5.0.6",
"axios": "^1.6.8",
- "better-sqlite3": "^9.5.0",
+ "better-sqlite3": "^11.2.1",
"check-disk-space": "^3.4.0",
"classnames": "^2.5.1",
"color": "^4.2.3",
@@ -61,6 +60,7 @@
"iso-639-1": "3.1.2",
"jsdom": "^24.0.0",
"jsonwebtoken": "^9.0.2",
+ "knex": "^3.1.0",
"lodash-es": "^4.17.21",
"lottie-react": "^2.4.0",
"parse-torrent": "^11.0.16",
diff --git a/src/main/data-source.ts b/src/main/data-source.ts
index 446ccbdc..29c72f8c 100644
--- a/src/main/data-source.ts
+++ b/src/main/data-source.ts
@@ -10,7 +10,6 @@ import {
} from "@main/entity";
import { databasePath } from "./constants";
-import * as migrations from "./migrations";
export const dataSource = new DataSource({
type: "better-sqlite3",
@@ -23,7 +22,6 @@ export const dataSource = new DataSource({
DownloadQueue,
UserAuth,
],
- synchronize: true,
+ synchronize: false,
database: databasePath,
- migrations,
});
diff --git a/src/main/entity/repack.entity.ts b/src/main/entity/repack.entity.ts
index ff3f16cb..36de2a7c 100644
--- a/src/main/entity/repack.entity.ts
+++ b/src/main/entity/repack.entity.ts
@@ -22,12 +22,6 @@ export class Repack {
@Column("text", { unique: true })
magnet: string;
- /**
- * @deprecated Direct scraping capability has been removed
- */
- @Column("int", { nullable: true })
- page: number;
-
@Column("text")
repacker: string;
diff --git a/src/main/index.ts b/src/main/index.ts
index e288302b..3e14a1ba 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -9,6 +9,7 @@ import { logger, PythonInstance, WindowManager } from "@main/services";
import { dataSource } from "@main/data-source";
import * as resources from "@locales";
import { userPreferencesRepository } from "@main/repository";
+import { knexClient, migrationConfig } from "./knex";
const { autoUpdater } = updater;
@@ -63,8 +64,22 @@ app.whenReady().then(async () => {
return net.fetch(url.pathToFileURL(decodeURI(filePath)).toString());
});
+ await knexClient.migrate.list(migrationConfig).then((result) => {
+ console.log("Migrations to run:", result[1]);
+ });
+
+ await knexClient.migrate
+ .latest(migrationConfig)
+ .then(() => {
+ console.log("Migrations executed successfully");
+ })
+ .catch((err) => {
+ console.log("Migrations failed to run:", err);
+ });
+
+ await knexClient.destroy();
+
await dataSource.initialize();
- await dataSource.runMigrations();
await import("./main");
diff --git a/src/main/knex.ts b/src/main/knex.ts
new file mode 100644
index 00000000..2e5cc27e
--- /dev/null
+++ b/src/main/knex.ts
@@ -0,0 +1,14 @@
+import knex, { Knex } from "knex";
+import { databasePath } from "./constants";
+import path from "node:path";
+
+export const knexClient = knex({
+ client: "better-sqlite3",
+ connection: {
+ filename: databasePath,
+ },
+});
+
+export const migrationConfig: Knex.MigratorConfig = {
+ directory: path.resolve(__dirname, "migrations"),
+};
diff --git a/src/main/migrations/1724081695967-Hydra_2_0_3.ts b/src/main/migrations/1724081695967-Hydra_2_0_3.ts
deleted file mode 100644
index 5ab18acb..00000000
--- a/src/main/migrations/1724081695967-Hydra_2_0_3.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { MigrationInterface, QueryRunner } from "typeorm";
-
-export class Hydra2031724081695967 implements MigrationInterface {
- name = 'Hydra2031724081695967'
-
- public async up(queryRunner: QueryRunner): Promise {
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_source" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "url" text, "name" text NOT NULL, "etag" text, "downloadCount" integer NOT NULL DEFAULT (0), "status" text NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), CONSTRAINT "UQ_aec2879321a87e9bb2ed477981a" UNIQUE ("url"))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "user_preferences" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "downloadsPath" text, "language" text NOT NULL DEFAULT ('en'), "realDebridApiToken" text, "downloadNotificationsEnabled" boolean NOT NULL DEFAULT (0), "repackUpdatesNotificationsEnabled" boolean NOT NULL DEFAULT (0), "preferQuitInsteadOfHiding" boolean NOT NULL DEFAULT (0), "runAtStartup" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game_shop_cache" ("objectID" text PRIMARY KEY NOT NULL, "shop" text NOT NULL, "serializedData" text, "howLongToBeatSerializedData" text, "language" text, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "user_auth" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "userId" text NOT NULL DEFAULT (''), "displayName" text NOT NULL DEFAULT (''), "profileImageUrl" text, "accessToken" text NOT NULL DEFAULT (''), "refreshToken" text NOT NULL DEFAULT (''), "tokenExpirationTimestamp" integer NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"), CONSTRAINT "FK_0c1d6445ad047d9bbd256f961f6" FOREIGN KEY ("repackId") REFERENCES "repack" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
- await queryRunner.query(`INSERT INTO "temporary_game"("id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId") SELECT "id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId" FROM "game"`);
- await queryRunner.query(`DROP TABLE "game"`);
- await queryRunner.query(`ALTER TABLE "temporary_game" RENAME TO "game"`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
- await queryRunner.query(`INSERT INTO "temporary_repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`);
- await queryRunner.query(`DROP TABLE "repack"`);
- await queryRunner.query(`ALTER TABLE "temporary_repack" RENAME TO "repack"`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"), CONSTRAINT "FK_aed852c94d9ded617a7a07f5415" FOREIGN KEY ("gameId") REFERENCES "game" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
- await queryRunner.query(`INSERT INTO "temporary_download_queue"("id", "createdAt", "updatedAt", "gameId") SELECT "id", "createdAt", "updatedAt", "gameId" FROM "download_queue"`);
- await queryRunner.query(`DROP TABLE "download_queue"`);
- await queryRunner.query(`ALTER TABLE "temporary_download_queue" RENAME TO "download_queue"`);
- }
-
- public async down(queryRunner: QueryRunner): Promise {
- await queryRunner.query(`ALTER TABLE "download_queue" RENAME TO "temporary_download_queue"`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"))`);
- await queryRunner.query(`INSERT INTO "download_queue"("id", "createdAt", "updatedAt", "gameId") SELECT "id", "createdAt", "updatedAt", "gameId" FROM "temporary_download_queue"`);
- await queryRunner.query(`DROP TABLE "temporary_download_queue"`);
- await queryRunner.query(`ALTER TABLE "repack" RENAME TO "temporary_repack"`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"))`);
- await queryRunner.query(`INSERT INTO "repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`);
- await queryRunner.query(`DROP TABLE "temporary_repack"`);
- await queryRunner.query(`ALTER TABLE "game" RENAME TO "temporary_game"`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"))`);
- await queryRunner.query(`INSERT INTO "game"("id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId") SELECT "id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId" FROM "temporary_game"`);
- await queryRunner.query(`DROP TABLE "temporary_game"`);
- await queryRunner.query(`DROP TABLE "user_auth"`);
- await queryRunner.query(`DROP TABLE "download_queue"`);
- await queryRunner.query(`DROP TABLE "game_shop_cache"`);
- await queryRunner.query(`DROP TABLE "user_preferences"`);
- await queryRunner.query(`DROP TABLE "repack"`);
- await queryRunner.query(`DROP TABLE "download_source"`);
- await queryRunner.query(`DROP TABLE "game"`);
- }
-
-}
diff --git a/src/main/migrations/1724081984535-DowloadsRefactor.ts b/src/main/migrations/1724081984535-DowloadsRefactor.ts
deleted file mode 100644
index 3afc8444..00000000
--- a/src/main/migrations/1724081984535-DowloadsRefactor.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { MigrationInterface, QueryRunner } from "typeorm";
-
-export class DowloadsRefactor1724081984535 implements MigrationInterface {
- name = 'DowloadsRefactor1724081984535'
-
- public async up(queryRunner: QueryRunner): Promise {
- await queryRunner.query(`CREATE TABLE "temporary_repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, "uris" text NOT NULL DEFAULT ('[]'), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
- await queryRunner.query(`INSERT INTO "temporary_repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`);
- await queryRunner.query(`DROP TABLE "repack"`);
- await queryRunner.query(`ALTER TABLE "temporary_repack" RENAME TO "repack"`);
- }
-
- public async down(queryRunner: QueryRunner): Promise {
- await queryRunner.query(`ALTER TABLE "repack" RENAME TO "temporary_repack"`);
- await queryRunner.query(`CREATE TABLE "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
- await queryRunner.query(`INSERT INTO "repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`);
- await queryRunner.query(`DROP TABLE "temporary_repack"`);
- }
-
-}
diff --git a/src/main/migrations/1_Hydra_2_0_3.ts b/src/main/migrations/1_Hydra_2_0_3.ts
new file mode 100644
index 00000000..3d7dac78
--- /dev/null
+++ b/src/main/migrations/1_Hydra_2_0_3.ts
@@ -0,0 +1,110 @@
+import { Knex } from "knex";
+
+export const up = async (knex: Knex) => {
+ await knex.schema.dropTableIfExists("migrations");
+ await knex.schema.createTableIfNotExists("download_source", (table) => {
+ table.increments("id").primary();
+ table.text("url").unique();
+ table.text("name").notNullable();
+ table.text("etag");
+ table.integer("downloadCount").notNullable().defaultTo(0);
+ table.text("status").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+
+ await knex.schema.createTableIfNotExists("repack", (table) => {
+ table.increments("id").primary();
+ table.text("title").notNullable().unique();
+ table.text("magnet").notNullable().unique();
+ table.integer("page");
+ table.text("repacker").notNullable();
+ table.text("fileSize").notNullable();
+ table.datetime("uploadDate").notNullable();
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table
+ .integer("downloadSourceId")
+ .references("download_source.id")
+ .onDelete("CASCADE");
+ });
+
+ await knex.schema.createTableIfNotExists("game", (table) => {
+ table.increments("id").primary();
+ table.text("objectID").notNullable().unique();
+ table.text("remoteId").unique();
+ table.text("title").notNullable();
+ table.text("iconUrl");
+ table.text("folderName");
+ table.text("downloadPath");
+ table.text("executablePath");
+ table.integer("playTimeInMilliseconds").notNullable().defaultTo(0);
+ table.text("shop").notNullable();
+ table.text("status");
+ table.integer("downloader").notNullable().defaultTo(1);
+ table.float("progress").notNullable().defaultTo(0);
+ table.integer("bytesDownloaded").notNullable().defaultTo(0);
+ table.datetime("lastTimePlayed");
+ table.float("fileSize").notNullable().defaultTo(0);
+ table.text("uri");
+ table.boolean("isDeleted").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table.integer("repackId").references("repack.id").unique();
+ });
+
+ await knex.schema.createTableIfNotExists("user_preferences", (table) => {
+ table.increments("id").primary();
+ table.text("downloadsPath");
+ table.text("language").notNullable().defaultTo("en");
+ table.text("realDebridApiToken");
+ table.boolean("downloadNotificationsEnabled").notNullable().defaultTo(0);
+ table
+ .boolean("repackUpdatesNotificationsEnabled")
+ .notNullable()
+ .defaultTo(0);
+ table.boolean("preferQuitInsteadOfHiding").notNullable().defaultTo(0);
+ table.boolean("runAtStartup").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+
+ await knex.schema.createTableIfNotExists("game_shop_cache", (table) => {
+ table.text("objectID").primary().notNullable();
+ table.text("shop").notNullable();
+ table.text("serializedData");
+ table.text("howLongToBeatSerializedData");
+ table.text("language");
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+
+ await knex.schema.createTableIfNotExists("download_queue", (table) => {
+ table.increments("id").primary();
+ table.integer("gameId").references("game.id").unique();
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+
+ await knex.schema.createTableIfNotExists("user_auth", (table) => {
+ table.increments("id").primary();
+ table.text("userId").notNullable().defaultTo("");
+ table.text("displayName").notNullable().defaultTo("");
+ table.text("profileImageUrl");
+ table.text("accessToken").notNullable().defaultTo("");
+ table.text("refreshToken").notNullable().defaultTo("");
+ table.integer("tokenExpirationTimestamp").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+};
+
+export const down = async (knex: Knex) => {
+ await knex.schema.dropTableIfExists("game");
+ await knex.schema.dropTableIfExists("repack");
+ await knex.schema.dropTableIfExists("download_queue");
+ await knex.schema.dropTableIfExists("user_auth");
+ await knex.schema.dropTableIfExists("game_shop_cache");
+ await knex.schema.dropTableIfExists("user_preferences");
+ await knex.schema.dropTableIfExists("download_source");
+};
diff --git a/src/main/migrations/2_DowloadsRefactor.ts b/src/main/migrations/2_DowloadsRefactor.ts
new file mode 100644
index 00000000..7a2bf5e6
--- /dev/null
+++ b/src/main/migrations/2_DowloadsRefactor.ts
@@ -0,0 +1,47 @@
+import { Knex } from "knex";
+
+export const up = async (knex: Knex) => {
+ await knex.schema.createTableIfNotExists("temporary_repack", (table) => {
+ table.increments("id").primary();
+ table.text("title").notNullable().unique();
+ table.text("magnet").notNullable().unique();
+ table.text("repacker").notNullable();
+ table.text("fileSize").notNullable();
+ table.datetime("uploadDate").notNullable();
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table
+ .integer("downloadSourceId")
+ .references("download_source.id")
+ .onDelete("CASCADE");
+ table.text("uris").notNullable().defaultTo("[]");
+ });
+ await knex.raw(
+ `INSERT INTO "temporary_repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`
+ );
+ await knex.schema.dropTable("repack");
+ await knex.schema.renameTable("temporary_repack", "repack");
+};
+
+export const down = async (knex: Knex) => {
+ await knex.schema.renameTable("repack", "temporary_repack");
+ await knex.schema.createTableIfNotExists("repack", (table) => {
+ table.increments("id").primary();
+ table.text("title").notNullable().unique();
+ table.text("magnet").notNullable().unique();
+ table.integer("page");
+ table.text("repacker").notNullable();
+ table.text("fileSize").notNullable();
+ table.datetime("uploadDate").notNullable();
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table
+ .integer("downloadSourceId")
+ .references("download_source.id")
+ .onDelete("CASCADE");
+ });
+ await knex.raw(
+ `INSERT INTO "repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`
+ );
+ await knex.schema.dropTable("temporary_repack");
+};
diff --git a/src/main/migrations/index.ts b/src/main/migrations/index.ts
deleted file mode 100644
index 5546bce0..00000000
--- a/src/main/migrations/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from "./1724081695967-Hydra_2_0_3";
-export * from "./1724081984535-DowloadsRefactor";
diff --git a/yarn.lock b/yarn.lock
index 74b9a8a1..9ffbf746 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2870,10 +2870,10 @@ bep53-range@^2.0.0:
resolved "https://registry.npmjs.org/bep53-range/-/bep53-range-2.0.0.tgz"
integrity sha512-sMm2sV5PRs0YOVk0LTKtjuIprVzxgTQUsrGX/7Yph2Rm4FO2Fqqtq7hNjsOB5xezM4v4+5rljCgK++UeQJZguA==
-better-sqlite3@^9.5.0:
- version "9.6.0"
- resolved "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.6.0.tgz"
- integrity sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ==
+better-sqlite3@^11.2.1:
+ version "11.2.1"
+ resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-11.2.1.tgz#3c6b8a8e2e12444d380e811796b59c8aba012e03"
+ integrity sha512-Xbt1d68wQnUuFIEVsbt6V+RG30zwgbtCGQ4QOcXVrOH0FE4eHk64FWZ9NUfRHS4/x1PXqwz/+KOrnXD7f0WieA==
dependencies:
bindings "^1.5.0"
prebuild-install "^7.1.1"
@@ -3245,6 +3245,11 @@ color@^4.2.3:
color-convert "^2.0.1"
color-string "^1.9.0"
+colorette@2.0.19:
+ version "2.0.19"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
+ integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
+
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
@@ -3252,6 +3257,11 @@ combined-stream@^1.0.8:
dependencies:
delayed-stream "~1.0.0"
+commander@^10.0.0:
+ version "10.0.1"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
+ integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
+
commander@^5.0.0:
version "5.1.0"
resolved "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz"
@@ -3462,7 +3472,7 @@ dayjs@^1.11.9:
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz"
integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==
-debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
+debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -4101,6 +4111,11 @@ eslint@^8.56.0:
strip-ansi "^6.0.1"
text-table "^0.2.0"
+esm@^3.2.25:
+ version "3.2.25"
+ resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
+ integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
+
espree@^9.6.0, espree@^9.6.1:
version "9.6.1"
resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz"
@@ -4463,6 +4478,11 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@
has-symbols "^1.0.3"
hasown "^2.0.0"
+get-package-type@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
+ integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
+
get-stdin@^9.0.0:
version "9.0.0"
resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz"
@@ -4489,6 +4509,11 @@ get-symbol-description@^1.0.2:
es-errors "^1.3.0"
get-intrinsic "^1.2.4"
+getopts@2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4"
+ integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==
+
git-raw-commits@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz"
@@ -4909,6 +4934,11 @@ internal-slot@^1.0.7:
hasown "^2.0.0"
side-channel "^1.0.4"
+interpret@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
+ integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
+
is-array-buffer@^3.0.4:
version "3.0.4"
resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz"
@@ -5350,6 +5380,26 @@ keyv@^4.0.0, keyv@^4.5.3:
dependencies:
json-buffer "3.0.1"
+knex@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/knex/-/knex-3.1.0.tgz#b6ddd5b5ad26a6315234a5b09ec38dc4a370bd8c"
+ integrity sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==
+ dependencies:
+ colorette "2.0.19"
+ commander "^10.0.0"
+ debug "4.3.4"
+ escalade "^3.1.1"
+ esm "^3.2.25"
+ get-package-type "^0.1.0"
+ getopts "2.3.0"
+ interpret "^2.2.0"
+ lodash "^4.17.21"
+ pg-connection-string "2.6.2"
+ rechoir "^0.8.0"
+ resolve-from "^5.0.0"
+ tarn "^3.0.2"
+ tildify "2.0.0"
+
language-subtag-registry@^0.3.20:
version "0.3.22"
resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz"
@@ -5489,7 +5539,7 @@ lodash.upperfirst@^4.3.1:
resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz"
integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==
-lodash@^4.17.15:
+lodash@^4.17.15, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -6114,6 +6164,11 @@ pend@~1.2.0:
resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz"
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
+pg-connection-string@2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.2.tgz#713d82053de4e2bd166fab70cd4f26ad36aab475"
+ integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==
+
pg-int8@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
@@ -6457,6 +6512,13 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
+rechoir@^0.8.0:
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22"
+ integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==
+ dependencies:
+ resolve "^1.20.0"
+
redux-thunk@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz"
@@ -6554,7 +6616,7 @@ resolve-from@^5.0.0:
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
-resolve@^1.22.1:
+resolve@^1.20.0, resolve@^1.22.1:
version "1.22.8"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
@@ -7083,6 +7145,11 @@ tar@^6.1.12:
mkdirp "^1.0.3"
yallist "^4.0.0"
+tarn@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693"
+ integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==
+
temp-file@^3.4.0:
version "3.4.0"
resolved "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz"
@@ -7120,6 +7187,11 @@ thenify-all@^1.0.0:
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
+tildify@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a"
+ integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==
+
tiny-typed-emitter@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz"
From 5fcca9888d8f329adb7ecf2d15c893df247a96c9 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Fri, 23 Aug 2024 00:07:50 -0300
Subject: [PATCH 073/108] Revert "feat: write migrations"
This reverts commit 81aa25fa5293d2d17e67b8c65012ccf50823a605.
---
.prettierignore | 1 +
package.json | 6 +-
src/main/data-source.ts | 4 +-
src/main/entity/repack.entity.ts | 6 +
src/main/index.ts | 17 +--
src/main/knex.ts | 14 ---
.../migrations/1724081695967-Hydra_2_0_3.ts | 50 ++++++++
.../1724081984535-DowloadsRefactor.ts | 20 ++++
src/main/migrations/1_Hydra_2_0_3.ts | 110 ------------------
src/main/migrations/2_DowloadsRefactor.ts | 47 --------
src/main/migrations/index.ts | 2 +
yarn.lock | 86 ++------------
12 files changed, 93 insertions(+), 270 deletions(-)
delete mode 100644 src/main/knex.ts
create mode 100644 src/main/migrations/1724081695967-Hydra_2_0_3.ts
create mode 100644 src/main/migrations/1724081984535-DowloadsRefactor.ts
delete mode 100644 src/main/migrations/1_Hydra_2_0_3.ts
delete mode 100644 src/main/migrations/2_DowloadsRefactor.ts
create mode 100644 src/main/migrations/index.ts
diff --git a/.prettierignore b/.prettierignore
index 05d298a1..9b6e9df6 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -5,3 +5,4 @@ pnpm-lock.yaml
LICENSE.md
tsconfig.json
tsconfig.*.json
+src/main/migrations
diff --git a/package.json b/package.json
index 113b2e20..aa77084e 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,8 @@
"build:win": "electron-vite build && electron-builder --win",
"build:mac": "electron-vite build && electron-builder --mac",
"build:linux": "electron-vite build && electron-builder --linux",
- "prepare": "husky"
+ "prepare": "husky",
+ "typeorm:migration-create": "yarn typeorm migration:create"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
@@ -42,7 +43,7 @@
"@vanilla-extract/recipes": "^0.5.2",
"auto-launch": "^5.0.6",
"axios": "^1.6.8",
- "better-sqlite3": "^11.2.1",
+ "better-sqlite3": "^9.5.0",
"check-disk-space": "^3.4.0",
"classnames": "^2.5.1",
"color": "^4.2.3",
@@ -60,7 +61,6 @@
"iso-639-1": "3.1.2",
"jsdom": "^24.0.0",
"jsonwebtoken": "^9.0.2",
- "knex": "^3.1.0",
"lodash-es": "^4.17.21",
"lottie-react": "^2.4.0",
"parse-torrent": "^11.0.16",
diff --git a/src/main/data-source.ts b/src/main/data-source.ts
index 29c72f8c..446ccbdc 100644
--- a/src/main/data-source.ts
+++ b/src/main/data-source.ts
@@ -10,6 +10,7 @@ import {
} from "@main/entity";
import { databasePath } from "./constants";
+import * as migrations from "./migrations";
export const dataSource = new DataSource({
type: "better-sqlite3",
@@ -22,6 +23,7 @@ export const dataSource = new DataSource({
DownloadQueue,
UserAuth,
],
- synchronize: false,
+ synchronize: true,
database: databasePath,
+ migrations,
});
diff --git a/src/main/entity/repack.entity.ts b/src/main/entity/repack.entity.ts
index 36de2a7c..ff3f16cb 100644
--- a/src/main/entity/repack.entity.ts
+++ b/src/main/entity/repack.entity.ts
@@ -22,6 +22,12 @@ export class Repack {
@Column("text", { unique: true })
magnet: string;
+ /**
+ * @deprecated Direct scraping capability has been removed
+ */
+ @Column("int", { nullable: true })
+ page: number;
+
@Column("text")
repacker: string;
diff --git a/src/main/index.ts b/src/main/index.ts
index 3e14a1ba..e288302b 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -9,7 +9,6 @@ import { logger, PythonInstance, WindowManager } from "@main/services";
import { dataSource } from "@main/data-source";
import * as resources from "@locales";
import { userPreferencesRepository } from "@main/repository";
-import { knexClient, migrationConfig } from "./knex";
const { autoUpdater } = updater;
@@ -64,22 +63,8 @@ app.whenReady().then(async () => {
return net.fetch(url.pathToFileURL(decodeURI(filePath)).toString());
});
- await knexClient.migrate.list(migrationConfig).then((result) => {
- console.log("Migrations to run:", result[1]);
- });
-
- await knexClient.migrate
- .latest(migrationConfig)
- .then(() => {
- console.log("Migrations executed successfully");
- })
- .catch((err) => {
- console.log("Migrations failed to run:", err);
- });
-
- await knexClient.destroy();
-
await dataSource.initialize();
+ await dataSource.runMigrations();
await import("./main");
diff --git a/src/main/knex.ts b/src/main/knex.ts
deleted file mode 100644
index 2e5cc27e..00000000
--- a/src/main/knex.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import knex, { Knex } from "knex";
-import { databasePath } from "./constants";
-import path from "node:path";
-
-export const knexClient = knex({
- client: "better-sqlite3",
- connection: {
- filename: databasePath,
- },
-});
-
-export const migrationConfig: Knex.MigratorConfig = {
- directory: path.resolve(__dirname, "migrations"),
-};
diff --git a/src/main/migrations/1724081695967-Hydra_2_0_3.ts b/src/main/migrations/1724081695967-Hydra_2_0_3.ts
new file mode 100644
index 00000000..5ab18acb
--- /dev/null
+++ b/src/main/migrations/1724081695967-Hydra_2_0_3.ts
@@ -0,0 +1,50 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class Hydra2031724081695967 implements MigrationInterface {
+ name = 'Hydra2031724081695967'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_source" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "url" text, "name" text NOT NULL, "etag" text, "downloadCount" integer NOT NULL DEFAULT (0), "status" text NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), CONSTRAINT "UQ_aec2879321a87e9bb2ed477981a" UNIQUE ("url"))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "user_preferences" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "downloadsPath" text, "language" text NOT NULL DEFAULT ('en'), "realDebridApiToken" text, "downloadNotificationsEnabled" boolean NOT NULL DEFAULT (0), "repackUpdatesNotificationsEnabled" boolean NOT NULL DEFAULT (0), "preferQuitInsteadOfHiding" boolean NOT NULL DEFAULT (0), "runAtStartup" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game_shop_cache" ("objectID" text PRIMARY KEY NOT NULL, "shop" text NOT NULL, "serializedData" text, "howLongToBeatSerializedData" text, "language" text, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "user_auth" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "userId" text NOT NULL DEFAULT (''), "displayName" text NOT NULL DEFAULT (''), "profileImageUrl" text, "accessToken" text NOT NULL DEFAULT (''), "refreshToken" text NOT NULL DEFAULT (''), "tokenExpirationTimestamp" integer NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"), CONSTRAINT "FK_0c1d6445ad047d9bbd256f961f6" FOREIGN KEY ("repackId") REFERENCES "repack" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
+ await queryRunner.query(`INSERT INTO "temporary_game"("id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId") SELECT "id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId" FROM "game"`);
+ await queryRunner.query(`DROP TABLE "game"`);
+ await queryRunner.query(`ALTER TABLE "temporary_game" RENAME TO "game"`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
+ await queryRunner.query(`INSERT INTO "temporary_repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`);
+ await queryRunner.query(`DROP TABLE "repack"`);
+ await queryRunner.query(`ALTER TABLE "temporary_repack" RENAME TO "repack"`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"), CONSTRAINT "FK_aed852c94d9ded617a7a07f5415" FOREIGN KEY ("gameId") REFERENCES "game" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
+ await queryRunner.query(`INSERT INTO "temporary_download_queue"("id", "createdAt", "updatedAt", "gameId") SELECT "id", "createdAt", "updatedAt", "gameId" FROM "download_queue"`);
+ await queryRunner.query(`DROP TABLE "download_queue"`);
+ await queryRunner.query(`ALTER TABLE "temporary_download_queue" RENAME TO "download_queue"`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "download_queue" RENAME TO "temporary_download_queue"`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"))`);
+ await queryRunner.query(`INSERT INTO "download_queue"("id", "createdAt", "updatedAt", "gameId") SELECT "id", "createdAt", "updatedAt", "gameId" FROM "temporary_download_queue"`);
+ await queryRunner.query(`DROP TABLE "temporary_download_queue"`);
+ await queryRunner.query(`ALTER TABLE "repack" RENAME TO "temporary_repack"`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"))`);
+ await queryRunner.query(`INSERT INTO "repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`);
+ await queryRunner.query(`DROP TABLE "temporary_repack"`);
+ await queryRunner.query(`ALTER TABLE "game" RENAME TO "temporary_game"`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"))`);
+ await queryRunner.query(`INSERT INTO "game"("id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId") SELECT "id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId" FROM "temporary_game"`);
+ await queryRunner.query(`DROP TABLE "temporary_game"`);
+ await queryRunner.query(`DROP TABLE "user_auth"`);
+ await queryRunner.query(`DROP TABLE "download_queue"`);
+ await queryRunner.query(`DROP TABLE "game_shop_cache"`);
+ await queryRunner.query(`DROP TABLE "user_preferences"`);
+ await queryRunner.query(`DROP TABLE "repack"`);
+ await queryRunner.query(`DROP TABLE "download_source"`);
+ await queryRunner.query(`DROP TABLE "game"`);
+ }
+
+}
diff --git a/src/main/migrations/1724081984535-DowloadsRefactor.ts b/src/main/migrations/1724081984535-DowloadsRefactor.ts
new file mode 100644
index 00000000..3afc8444
--- /dev/null
+++ b/src/main/migrations/1724081984535-DowloadsRefactor.ts
@@ -0,0 +1,20 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class DowloadsRefactor1724081984535 implements MigrationInterface {
+ name = 'DowloadsRefactor1724081984535'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`CREATE TABLE "temporary_repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, "uris" text NOT NULL DEFAULT ('[]'), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
+ await queryRunner.query(`INSERT INTO "temporary_repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`);
+ await queryRunner.query(`DROP TABLE "repack"`);
+ await queryRunner.query(`ALTER TABLE "temporary_repack" RENAME TO "repack"`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "repack" RENAME TO "temporary_repack"`);
+ await queryRunner.query(`CREATE TABLE "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
+ await queryRunner.query(`INSERT INTO "repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`);
+ await queryRunner.query(`DROP TABLE "temporary_repack"`);
+ }
+
+}
diff --git a/src/main/migrations/1_Hydra_2_0_3.ts b/src/main/migrations/1_Hydra_2_0_3.ts
deleted file mode 100644
index 3d7dac78..00000000
--- a/src/main/migrations/1_Hydra_2_0_3.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import { Knex } from "knex";
-
-export const up = async (knex: Knex) => {
- await knex.schema.dropTableIfExists("migrations");
- await knex.schema.createTableIfNotExists("download_source", (table) => {
- table.increments("id").primary();
- table.text("url").unique();
- table.text("name").notNullable();
- table.text("etag");
- table.integer("downloadCount").notNullable().defaultTo(0);
- table.text("status").notNullable().defaultTo(0);
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- });
-
- await knex.schema.createTableIfNotExists("repack", (table) => {
- table.increments("id").primary();
- table.text("title").notNullable().unique();
- table.text("magnet").notNullable().unique();
- table.integer("page");
- table.text("repacker").notNullable();
- table.text("fileSize").notNullable();
- table.datetime("uploadDate").notNullable();
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- table
- .integer("downloadSourceId")
- .references("download_source.id")
- .onDelete("CASCADE");
- });
-
- await knex.schema.createTableIfNotExists("game", (table) => {
- table.increments("id").primary();
- table.text("objectID").notNullable().unique();
- table.text("remoteId").unique();
- table.text("title").notNullable();
- table.text("iconUrl");
- table.text("folderName");
- table.text("downloadPath");
- table.text("executablePath");
- table.integer("playTimeInMilliseconds").notNullable().defaultTo(0);
- table.text("shop").notNullable();
- table.text("status");
- table.integer("downloader").notNullable().defaultTo(1);
- table.float("progress").notNullable().defaultTo(0);
- table.integer("bytesDownloaded").notNullable().defaultTo(0);
- table.datetime("lastTimePlayed");
- table.float("fileSize").notNullable().defaultTo(0);
- table.text("uri");
- table.boolean("isDeleted").notNullable().defaultTo(0);
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- table.integer("repackId").references("repack.id").unique();
- });
-
- await knex.schema.createTableIfNotExists("user_preferences", (table) => {
- table.increments("id").primary();
- table.text("downloadsPath");
- table.text("language").notNullable().defaultTo("en");
- table.text("realDebridApiToken");
- table.boolean("downloadNotificationsEnabled").notNullable().defaultTo(0);
- table
- .boolean("repackUpdatesNotificationsEnabled")
- .notNullable()
- .defaultTo(0);
- table.boolean("preferQuitInsteadOfHiding").notNullable().defaultTo(0);
- table.boolean("runAtStartup").notNullable().defaultTo(0);
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- });
-
- await knex.schema.createTableIfNotExists("game_shop_cache", (table) => {
- table.text("objectID").primary().notNullable();
- table.text("shop").notNullable();
- table.text("serializedData");
- table.text("howLongToBeatSerializedData");
- table.text("language");
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- });
-
- await knex.schema.createTableIfNotExists("download_queue", (table) => {
- table.increments("id").primary();
- table.integer("gameId").references("game.id").unique();
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- });
-
- await knex.schema.createTableIfNotExists("user_auth", (table) => {
- table.increments("id").primary();
- table.text("userId").notNullable().defaultTo("");
- table.text("displayName").notNullable().defaultTo("");
- table.text("profileImageUrl");
- table.text("accessToken").notNullable().defaultTo("");
- table.text("refreshToken").notNullable().defaultTo("");
- table.integer("tokenExpirationTimestamp").notNullable().defaultTo(0);
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- });
-};
-
-export const down = async (knex: Knex) => {
- await knex.schema.dropTableIfExists("game");
- await knex.schema.dropTableIfExists("repack");
- await knex.schema.dropTableIfExists("download_queue");
- await knex.schema.dropTableIfExists("user_auth");
- await knex.schema.dropTableIfExists("game_shop_cache");
- await knex.schema.dropTableIfExists("user_preferences");
- await knex.schema.dropTableIfExists("download_source");
-};
diff --git a/src/main/migrations/2_DowloadsRefactor.ts b/src/main/migrations/2_DowloadsRefactor.ts
deleted file mode 100644
index 7a2bf5e6..00000000
--- a/src/main/migrations/2_DowloadsRefactor.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Knex } from "knex";
-
-export const up = async (knex: Knex) => {
- await knex.schema.createTableIfNotExists("temporary_repack", (table) => {
- table.increments("id").primary();
- table.text("title").notNullable().unique();
- table.text("magnet").notNullable().unique();
- table.text("repacker").notNullable();
- table.text("fileSize").notNullable();
- table.datetime("uploadDate").notNullable();
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- table
- .integer("downloadSourceId")
- .references("download_source.id")
- .onDelete("CASCADE");
- table.text("uris").notNullable().defaultTo("[]");
- });
- await knex.raw(
- `INSERT INTO "temporary_repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`
- );
- await knex.schema.dropTable("repack");
- await knex.schema.renameTable("temporary_repack", "repack");
-};
-
-export const down = async (knex: Knex) => {
- await knex.schema.renameTable("repack", "temporary_repack");
- await knex.schema.createTableIfNotExists("repack", (table) => {
- table.increments("id").primary();
- table.text("title").notNullable().unique();
- table.text("magnet").notNullable().unique();
- table.integer("page");
- table.text("repacker").notNullable();
- table.text("fileSize").notNullable();
- table.datetime("uploadDate").notNullable();
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- table
- .integer("downloadSourceId")
- .references("download_source.id")
- .onDelete("CASCADE");
- });
- await knex.raw(
- `INSERT INTO "repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`
- );
- await knex.schema.dropTable("temporary_repack");
-};
diff --git a/src/main/migrations/index.ts b/src/main/migrations/index.ts
new file mode 100644
index 00000000..5546bce0
--- /dev/null
+++ b/src/main/migrations/index.ts
@@ -0,0 +1,2 @@
+export * from "./1724081695967-Hydra_2_0_3";
+export * from "./1724081984535-DowloadsRefactor";
diff --git a/yarn.lock b/yarn.lock
index 9ffbf746..74b9a8a1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2870,10 +2870,10 @@ bep53-range@^2.0.0:
resolved "https://registry.npmjs.org/bep53-range/-/bep53-range-2.0.0.tgz"
integrity sha512-sMm2sV5PRs0YOVk0LTKtjuIprVzxgTQUsrGX/7Yph2Rm4FO2Fqqtq7hNjsOB5xezM4v4+5rljCgK++UeQJZguA==
-better-sqlite3@^11.2.1:
- version "11.2.1"
- resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-11.2.1.tgz#3c6b8a8e2e12444d380e811796b59c8aba012e03"
- integrity sha512-Xbt1d68wQnUuFIEVsbt6V+RG30zwgbtCGQ4QOcXVrOH0FE4eHk64FWZ9NUfRHS4/x1PXqwz/+KOrnXD7f0WieA==
+better-sqlite3@^9.5.0:
+ version "9.6.0"
+ resolved "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.6.0.tgz"
+ integrity sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ==
dependencies:
bindings "^1.5.0"
prebuild-install "^7.1.1"
@@ -3245,11 +3245,6 @@ color@^4.2.3:
color-convert "^2.0.1"
color-string "^1.9.0"
-colorette@2.0.19:
- version "2.0.19"
- resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
- integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
-
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
@@ -3257,11 +3252,6 @@ combined-stream@^1.0.8:
dependencies:
delayed-stream "~1.0.0"
-commander@^10.0.0:
- version "10.0.1"
- resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
- integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
-
commander@^5.0.0:
version "5.1.0"
resolved "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz"
@@ -3472,7 +3462,7 @@ dayjs@^1.11.9:
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz"
integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==
-debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
+debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -4111,11 +4101,6 @@ eslint@^8.56.0:
strip-ansi "^6.0.1"
text-table "^0.2.0"
-esm@^3.2.25:
- version "3.2.25"
- resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
- integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
-
espree@^9.6.0, espree@^9.6.1:
version "9.6.1"
resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz"
@@ -4478,11 +4463,6 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@
has-symbols "^1.0.3"
hasown "^2.0.0"
-get-package-type@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
- integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
-
get-stdin@^9.0.0:
version "9.0.0"
resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz"
@@ -4509,11 +4489,6 @@ get-symbol-description@^1.0.2:
es-errors "^1.3.0"
get-intrinsic "^1.2.4"
-getopts@2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4"
- integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==
-
git-raw-commits@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz"
@@ -4934,11 +4909,6 @@ internal-slot@^1.0.7:
hasown "^2.0.0"
side-channel "^1.0.4"
-interpret@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
- integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
-
is-array-buffer@^3.0.4:
version "3.0.4"
resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz"
@@ -5380,26 +5350,6 @@ keyv@^4.0.0, keyv@^4.5.3:
dependencies:
json-buffer "3.0.1"
-knex@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/knex/-/knex-3.1.0.tgz#b6ddd5b5ad26a6315234a5b09ec38dc4a370bd8c"
- integrity sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==
- dependencies:
- colorette "2.0.19"
- commander "^10.0.0"
- debug "4.3.4"
- escalade "^3.1.1"
- esm "^3.2.25"
- get-package-type "^0.1.0"
- getopts "2.3.0"
- interpret "^2.2.0"
- lodash "^4.17.21"
- pg-connection-string "2.6.2"
- rechoir "^0.8.0"
- resolve-from "^5.0.0"
- tarn "^3.0.2"
- tildify "2.0.0"
-
language-subtag-registry@^0.3.20:
version "0.3.22"
resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz"
@@ -5539,7 +5489,7 @@ lodash.upperfirst@^4.3.1:
resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz"
integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==
-lodash@^4.17.15, lodash@^4.17.21:
+lodash@^4.17.15:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -6164,11 +6114,6 @@ pend@~1.2.0:
resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz"
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
-pg-connection-string@2.6.2:
- version "2.6.2"
- resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.2.tgz#713d82053de4e2bd166fab70cd4f26ad36aab475"
- integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==
-
pg-int8@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
@@ -6512,13 +6457,6 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
-rechoir@^0.8.0:
- version "0.8.0"
- resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22"
- integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==
- dependencies:
- resolve "^1.20.0"
-
redux-thunk@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz"
@@ -6616,7 +6554,7 @@ resolve-from@^5.0.0:
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
-resolve@^1.20.0, resolve@^1.22.1:
+resolve@^1.22.1:
version "1.22.8"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
@@ -7145,11 +7083,6 @@ tar@^6.1.12:
mkdirp "^1.0.3"
yallist "^4.0.0"
-tarn@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693"
- integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==
-
temp-file@^3.4.0:
version "3.4.0"
resolved "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz"
@@ -7187,11 +7120,6 @@ thenify-all@^1.0.0:
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
-tildify@2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a"
- integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==
-
tiny-typed-emitter@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz"
From f9979a38da9fe687c243bd9657c3d706bcb9d3ef Mon Sep 17 00:00:00 2001
From: Lianela <140931995+Lianela@users.noreply.github.com>
Date: Sun, 25 Aug 2024 16:58:48 -0600
Subject: [PATCH 074/108] removed strings / added new translations (es)
---
src/locales/es/translation.json | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json
index 0800b0c9..9cc7af9d 100644
--- a/src/locales/es/translation.json
+++ b/src/locales/es/translation.json
@@ -177,9 +177,6 @@
"download_count_zero": "No hay descargas en la lista",
"download_count_one": "{{countFormatted}} descarga en la lista",
"download_count_other": "{{countFormatted}} descargas en la lista",
- "download_options_zero": "No hay descargas disponibles",
- "download_options_one": "{{countFormatted}} descarga disponible",
- "download_options_other": "{{countFormatted}} descargas disponibles",
"download_source_url": "Descargar URL de origen",
"add_download_source_description": "Introduce la URL con el archivo .json",
"download_source_up_to_date": "Al día",
@@ -261,6 +258,18 @@
"undo_friendship": "Eliminar amistad",
"request_accepted": "Solicitud aceptada",
"user_blocked_successfully": "Usuario bloqueado exitosamente",
- "user_block_modal_text": "Esto va a bloquear a {{displayName}}"
+ "user_block_modal_text": "Esto va a bloquear a {{displayName}}",
+ "settings": "Ajustes",
+ "public": "Público",
+ "private": "Privado",
+ "friends_only": "Solo Amigos",
+ "privacy": "Privacidad",
+ "blocked_users": "Usuarios bloqueados",
+ "unblock": "Desbloquear",
+ "no_friends_added": "Todavía no tienes amigos añadidos",
+ "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"
}
}
From fb60c91c8399ef0b556da22892f14d12f3fc3b6e Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 26 Aug 2024 12:23:32 -0300
Subject: [PATCH 075/108] feat: add undo friendship confirm modal
---
src/locales/en/translation.json | 3 +-
src/locales/pt/translation.json | 3 +-
.../user-confirm-undo-friendship-modal.tsx | 42 +++++++++++++++++++
src/renderer/src/pages/user/user-content.tsx | 16 ++++---
4 files changed, 56 insertions(+), 8 deletions(-)
create mode 100644 src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index e2726b79..08c9fda2 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -270,6 +270,7 @@
"pending": "Pending",
"no_pending_invites": "You have no pending invites",
"no_blocked_users": "You have no blocked users",
- "friend_code_copied": "Friend code copied"
+ "friend_code_copied": "Friend code copied",
+ "undo_friendship_modal_text": "This will undo your friendship with {{displayName}}"
}
}
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index 36d38c96..a97a81cf 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -273,6 +273,7 @@
"pending": "Pendentes",
"no_pending_invites": "Você não possui convites de amizade pendentes",
"no_blocked_users": "Você não tem nenhum usuário bloqueado",
- "friend_code_copied": "Código de amigo copiado"
+ "friend_code_copied": "Código de amigo copiado",
+ "undo_friendship_modal_text": "Isso irá remover sua amizade com {{displayName}}"
}
}
diff --git a/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx b/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx
new file mode 100644
index 00000000..cf2a0415
--- /dev/null
+++ b/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx
@@ -0,0 +1,42 @@
+import { Button, Modal } from "@renderer/components";
+import * as styles from "./user.css";
+import { useTranslation } from "react-i18next";
+
+export interface UserConfirmUndoFriendshipModalProps {
+ visible: boolean;
+ displayName: string;
+ onConfirm: () => void;
+ onClose: () => void;
+}
+
+export const UserConfirmUndoFriendshipModal = ({
+ visible,
+ displayName,
+ onConfirm,
+ onClose,
+}: UserConfirmUndoFriendshipModalProps) => {
+ const { t } = useTranslation("user_profile");
+
+ return (
+ <>
+
+
+
{t("undo_friendship_modal_text", { displayName })}
+
+
+ {t("undo_friendship")}
+
+
+
+ {t("cancel")}
+
+
+
+
+ >
+ );
+};
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index c334389e..fb092d06 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -34,6 +34,7 @@ import { UserProfileSettingsModal } from "./user-profile-settings-modal";
import { UserSignOutModal } from "./user-sign-out-modal";
import { UserFriendModalTab } from "../shared-modals/user-friend-modal";
import { UserBlockModal } from "./user-block-modal";
+import { UserConfirmUndoFriendshipModal } from "./user-confirm-undo-friendship-modal";
const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120;
@@ -68,6 +69,7 @@ export function UserContent({
useState(false);
const [showSignOutModal, setShowSignOutModal] = useState(false);
const [showUserBlockModal, setShowUserBlockModal] = useState(false);
+ const [showUndoFriendshipModal, setShowUndoFriendshipModal] = useState(false);
const [currentGame, setCurrentGame] = useState(null);
const { gameRunning } = useAppSelector((state) => state.gameRunning);
@@ -213,17 +215,12 @@ export function UserContent({
}
if (userProfile.relation.status === "ACCEPTED") {
- const userId =
- userProfile.relation.AId === userDetails?.id
- ? userProfile.relation.BId
- : userProfile.relation.AId;
-
return (
<>
handleFriendAction(userId, "UNDO")}
+ onClick={() => setShowUndoFriendshipModal(true)}
>
{t("undo_friendship")}
@@ -291,6 +288,13 @@ export function UserContent({
displayName={userProfile.displayName}
/>
+ setShowUndoFriendshipModal(false)}
+ onConfirm={() => handleFriendAction(userProfile.id, "UNDO")}
+ displayName={userProfile.displayName}
+ />
+
Date: Mon, 26 Aug 2024 12:45:27 -0300
Subject: [PATCH 076/108] fix: profile display name
---
src/renderer/src/pages/user/user-content.tsx | 4 +++-
.../user-profile-settings-modal/user-edit-profile.tsx | 1 +
src/renderer/src/pages/user/user.css.ts | 9 +++++++++
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx
index fb092d06..f4a46ccd 100644
--- a/src/renderer/src/pages/user/user-content.tsx
+++ b/src/renderer/src/pages/user/user-content.tsx
@@ -332,7 +332,9 @@ export function UserContent({
-
{userProfile.displayName}
+
+ {userProfile.displayName}
+
{currentGame && (
setForm({ ...form, displayName: e.target.value })}
/>
diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts
index 4e1c2139..6bcb30b0 100644
--- a/src/renderer/src/pages/user/user.css.ts
+++ b/src/renderer/src/pages/user/user.css.ts
@@ -23,6 +23,7 @@ export const profileContentBox = style({
export const profileAvatarContainer = style({
width: "96px",
+ minWidth: "96px",
height: "96px",
borderRadius: "50%",
display: "flex",
@@ -100,6 +101,14 @@ export const profileInformation = style({
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({
From 3293320926e74a867a39188231604840c0f0c3aa Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 26 Aug 2024 13:21:21 -0300
Subject: [PATCH 077/108] fix: sidebar profile layout
---
.../components/sidebar/sidebar-profile.css.ts | 20 +++++---------
.../components/sidebar/sidebar-profile.tsx | 27 ++++++++++---------
2 files changed, 20 insertions(+), 27 deletions(-)
diff --git a/src/renderer/src/components/sidebar/sidebar-profile.css.ts b/src/renderer/src/components/sidebar/sidebar-profile.css.ts
index ba29c850..d97b0c44 100644
--- a/src/renderer/src/components/sidebar/sidebar-profile.css.ts
+++ b/src/renderer/src/components/sidebar/sidebar-profile.css.ts
@@ -7,22 +7,24 @@ export const profileContainerBackground = createVar();
export const profileContainer = style({
background: profileContainerBackground,
position: "relative",
+ display: "flex",
+ gap: `${SPACING_UNIT}px`,
cursor: "pointer",
":hover": {
backgroundColor: "rgba(255, 255, 255, 0.15)",
},
+ borderBottom: `solid 1px ${vars.color.border}`,
+ boxShadow: "0px 0px 15px 0px rgb(0 0 0 / 70%)",
+ padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 2}px`,
});
export const profileButton = style({
display: "flex",
cursor: "pointer",
transition: "all ease 0.1s",
- padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 2}px`,
color: vars.color.muted,
- borderBottom: `solid 1px ${vars.color.border}`,
- boxShadow: "0px 0px 15px 0px rgb(0 0 0 / 70%)",
width: "100%",
- zIndex: "10",
+ overflow: "hidden",
});
export const profileButtonContent = style({
@@ -75,16 +77,6 @@ export const profileButtonTitle = style({
whiteSpace: "nowrap",
});
-export const friendRequestContainer = style({
- position: "absolute",
- padding: "8px",
- right: `${SPACING_UNIT}px`,
- display: "flex",
- top: 0,
- bottom: 0,
- alignItems: "center",
-});
-
export const friendRequestButton = style({
color: vars.color.success,
cursor: "pointer",
diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx
index 069831cb..e41d7da2 100644
--- a/src/renderer/src/components/sidebar/sidebar-profile.tsx
+++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx
@@ -41,6 +41,9 @@ export function SidebarProfile() {
return undefined;
}, [profileBackground]);
+ const showPendingRequests =
+ userDetails && receivedRequests.length > -1 && !gameRunning;
+
return (
- {userDetails && receivedRequests.length > 0 && !gameRunning && (
-
-
- showFriendsModal(UserFriendModalTab.AddFriend, userDetails.id)
- }
- >
-
- {receivedRequests.length}
-
-
+ {showPendingRequests && (
+
+ showFriendsModal(UserFriendModalTab.AddFriend, userDetails.id)
+ }
+ >
+
+ {receivedRequests.length}
+
)}
);
From 0bedb7c9b7e237e3bf872b1754f21782c624e4ce Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 26 Aug 2024 14:12:42 -0300
Subject: [PATCH 078/108] fix: header overflow text
---
src/renderer/src/components/header/header.css.ts | 5 +++++
src/renderer/src/components/header/header.tsx | 2 +-
src/renderer/src/components/sidebar/sidebar-profile.tsx | 2 +-
3 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/renderer/src/components/header/header.css.ts b/src/renderer/src/components/header/header.css.ts
index 0e82aaef..12855986 100644
--- a/src/renderer/src/components/header/header.css.ts
+++ b/src/renderer/src/components/header/header.css.ts
@@ -104,6 +104,7 @@ export const section = style({
alignItems: "center",
gap: `${SPACING_UNIT * 2}px`,
height: "100%",
+ overflow: "hidden",
});
export const backButton = recipe({
@@ -136,11 +137,15 @@ export const backButton = recipe({
export const title = recipe({
base: {
transition: "all ease 0.2s",
+ overflow: "hidden",
+ textOverflow: "ellipsis",
+ width: "100%",
},
variants: {
hasBackButton: {
true: {
transform: "translateX(28px)",
+ width: "calc(100% - 28px)",
},
},
},
diff --git a/src/renderer/src/components/header/header.tsx b/src/renderer/src/components/header/header.tsx
index d3315098..c37e4b44 100644
--- a/src/renderer/src/components/header/header.tsx
+++ b/src/renderer/src/components/header/header.tsx
@@ -72,7 +72,7 @@ export function Header({ onSearch, onClear, search }: HeaderProps) {
isWindows: window.electron.platform === "win32",
})}
>
-
+
-1 && !gameRunning;
+ userDetails && receivedRequests.length > 0 && !gameRunning;
return (
Date: Mon, 26 Aug 2024 15:16:32 -0300
Subject: [PATCH 079/108] Update bug_report.yml
---
.github/ISSUE_TEMPLATE/bug_report.yml | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 88467f6a..472ed853 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -27,7 +27,7 @@ body:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
- required: true
+ required: false
- type: textarea
id: screenshots
attributes:
@@ -56,3 +56,12 @@ body:
description: Please provide any additional information and context about your problem.
validations:
required: false
+ - type: checkboxes
+ id: terms
+ attributes:
+ label: Before opening this Issue
+ options:
+ - label: I have searched the issues of this repository and believe that this is not a duplicate.
+ required: true
+ - label: I am aware that Hydra team does not offer any support or help regarding the downloaded games.
+ required: true
From cc10186ec3fa1e0e69a2dc374d4f469e8b2c7928 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 26 Aug 2024 15:24:32 -0300
Subject: [PATCH 080/108] feat: add trycatch for handleDeepLinkPath
---
src/main/index.ts | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/main/index.ts b/src/main/index.ts
index e288302b..11eb479d 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -86,10 +86,15 @@ app.on("browser-window-created", (_, window) => {
const handleDeepLinkPath = (uri?: string) => {
if (!uri) return;
- const url = new URL(uri);
- if (url.host === "install-source") {
- WindowManager.redirect(`settings${url.search}`);
+ try {
+ const url = new URL(uri);
+
+ if (url.host === "install-source") {
+ WindowManager.redirect(`settings${url.search}`);
+ }
+ } catch (error) {
+ logger.error("Error handling deep link", uri, error);
}
};
From 89399a6da4d7056a1164dadbd61371c0776d403c Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Thu, 22 Aug 2024 23:50:35 -0300
Subject: [PATCH 081/108] feat: write migrations
---
.prettierignore | 1 -
package.json | 6 +-
src/main/data-source.ts | 2 -
src/main/entity/repack.entity.ts | 6 -
src/main/index.ts | 20 ++-
src/main/knex-client.ts | 30 ++++
src/main/migrations/001_Hydra_2_0_3.ts | 169 ++++++++++++++++++
src/main/migrations/002_RepackUris.ts | 56 ++++++
.../migrations/1724081695967-Hydra_2_0_3.ts | 50 ------
.../1724081984535-DowloadsRefactor.ts | 20 ---
src/main/migrations/index.ts | 4 +-
yarn.lock | 86 ++++++++-
12 files changed, 358 insertions(+), 92 deletions(-)
create mode 100644 src/main/knex-client.ts
create mode 100644 src/main/migrations/001_Hydra_2_0_3.ts
create mode 100644 src/main/migrations/002_RepackUris.ts
delete mode 100644 src/main/migrations/1724081695967-Hydra_2_0_3.ts
delete mode 100644 src/main/migrations/1724081984535-DowloadsRefactor.ts
diff --git a/.prettierignore b/.prettierignore
index 9b6e9df6..05d298a1 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -5,4 +5,3 @@ pnpm-lock.yaml
LICENSE.md
tsconfig.json
tsconfig.*.json
-src/main/migrations
diff --git a/package.json b/package.json
index aa77084e..113b2e20 100644
--- a/package.json
+++ b/package.json
@@ -28,8 +28,7 @@
"build:win": "electron-vite build && electron-builder --win",
"build:mac": "electron-vite build && electron-builder --mac",
"build:linux": "electron-vite build && electron-builder --linux",
- "prepare": "husky",
- "typeorm:migration-create": "yarn typeorm migration:create"
+ "prepare": "husky"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
@@ -43,7 +42,7 @@
"@vanilla-extract/recipes": "^0.5.2",
"auto-launch": "^5.0.6",
"axios": "^1.6.8",
- "better-sqlite3": "^9.5.0",
+ "better-sqlite3": "^11.2.1",
"check-disk-space": "^3.4.0",
"classnames": "^2.5.1",
"color": "^4.2.3",
@@ -61,6 +60,7 @@
"iso-639-1": "3.1.2",
"jsdom": "^24.0.0",
"jsonwebtoken": "^9.0.2",
+ "knex": "^3.1.0",
"lodash-es": "^4.17.21",
"lottie-react": "^2.4.0",
"parse-torrent": "^11.0.16",
diff --git a/src/main/data-source.ts b/src/main/data-source.ts
index 826063e1..29c72f8c 100644
--- a/src/main/data-source.ts
+++ b/src/main/data-source.ts
@@ -10,7 +10,6 @@ import {
} from "@main/entity";
import { databasePath } from "./constants";
-import * as migrations from "./migrations";
export const dataSource = new DataSource({
type: "better-sqlite3",
@@ -25,5 +24,4 @@ export const dataSource = new DataSource({
],
synchronize: false,
database: databasePath,
- migrations,
});
diff --git a/src/main/entity/repack.entity.ts b/src/main/entity/repack.entity.ts
index ff3f16cb..36de2a7c 100644
--- a/src/main/entity/repack.entity.ts
+++ b/src/main/entity/repack.entity.ts
@@ -22,12 +22,6 @@ export class Repack {
@Column("text", { unique: true })
magnet: string;
- /**
- * @deprecated Direct scraping capability has been removed
- */
- @Column("int", { nullable: true })
- page: number;
-
@Column("text")
repacker: string;
diff --git a/src/main/index.ts b/src/main/index.ts
index e288302b..5293cda8 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -9,6 +9,7 @@ import { logger, PythonInstance, WindowManager } from "@main/services";
import { dataSource } from "@main/data-source";
import * as resources from "@locales";
import { userPreferencesRepository } from "@main/repository";
+import { knexClient, migrationConfig } from "./knex-client";
const { autoUpdater } = updater;
@@ -63,8 +64,25 @@ app.whenReady().then(async () => {
return net.fetch(url.pathToFileURL(decodeURI(filePath)).toString());
});
+ await knexClient.migrate.list(migrationConfig).then((result) => {
+ logger.log(
+ "Migrations to run:",
+ result[1].map((migration) => migration.name)
+ );
+ });
+
+ await knexClient.migrate
+ .latest(migrationConfig)
+ .then(() => {
+ logger.log("Migrations executed successfully");
+ })
+ .catch((err) => {
+ logger.log("Migrations failed to run:", err);
+ });
+
+ await knexClient.destroy();
+
await dataSource.initialize();
- await dataSource.runMigrations();
await import("./main");
diff --git a/src/main/knex-client.ts b/src/main/knex-client.ts
new file mode 100644
index 00000000..ad6cf928
--- /dev/null
+++ b/src/main/knex-client.ts
@@ -0,0 +1,30 @@
+import knex, { Knex } from "knex";
+import { databasePath } from "./constants";
+import * as migrations from "./migrations";
+
+type Migration = Knex.Migration & { name: string };
+
+class MigrationSource implements Knex.MigrationSource
{
+ getMigrations(): Promise {
+ return Promise.resolve(
+ Object.values(migrations).sort((a, b) => a.name.localeCompare(b.name))
+ );
+ }
+ getMigrationName(migration: Migration): string {
+ return migration.name;
+ }
+ getMigration(migration: Migration): Promise {
+ return Promise.resolve(migration);
+ }
+}
+
+export const knexClient = knex({
+ client: "better-sqlite3",
+ connection: {
+ filename: databasePath,
+ },
+});
+
+export const migrationConfig: Knex.MigratorConfig = {
+ migrationSource: new MigrationSource(),
+};
diff --git a/src/main/migrations/001_Hydra_2_0_3.ts b/src/main/migrations/001_Hydra_2_0_3.ts
new file mode 100644
index 00000000..36a41739
--- /dev/null
+++ b/src/main/migrations/001_Hydra_2_0_3.ts
@@ -0,0 +1,169 @@
+import { Knex } from "knex";
+
+export const name = "001_Hydra_2_0_3";
+
+export const up = async (knex: Knex) => {
+ const timestamp = new Date().getTime();
+
+ await knex.schema.hasTable("migrations").then(async (exists) => {
+ if (exists) {
+ await knex.schema.dropTable("migrations");
+ }
+ });
+
+ await knex.schema.hasTable("download_source").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("download_source", (table) => {
+ table.increments("id").primary();
+ table
+ .text("url")
+ .unique({ indexName: "download_source_url_unique_" + timestamp });
+ table.text("name").notNullable();
+ table.text("etag");
+ table.integer("downloadCount").notNullable().defaultTo(0);
+ table.text("status").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+ }
+ });
+
+ await knex.schema.hasTable("repack").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("repack", (table) => {
+ table.increments("id").primary();
+ table
+ .text("title")
+ .notNullable()
+ .unique({ indexName: "repack_title_unique_" + timestamp });
+ table
+ .text("magnet")
+ .notNullable()
+ .unique({ indexName: "repack_magnet_unique_" + timestamp });
+ table.integer("page");
+ table.text("repacker").notNullable();
+ table.text("fileSize").notNullable();
+ table.datetime("uploadDate").notNullable();
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table
+ .integer("downloadSourceId")
+ .references("download_source.id")
+ .onDelete("CASCADE");
+ });
+ }
+ });
+
+ await knex.schema.hasTable("game").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("game", (table) => {
+ table.increments("id").primary();
+ table
+ .text("objectID")
+ .notNullable()
+ .unique({ indexName: "game_objectID_unique_" + timestamp });
+ table
+ .text("remoteId")
+ .unique({ indexName: "game_remoteId_unique_" + timestamp });
+ table.text("title").notNullable();
+ table.text("iconUrl");
+ table.text("folderName");
+ table.text("downloadPath");
+ table.text("executablePath");
+ table.integer("playTimeInMilliseconds").notNullable().defaultTo(0);
+ table.text("shop").notNullable();
+ table.text("status");
+ table.integer("downloader").notNullable().defaultTo(1);
+ table.float("progress").notNullable().defaultTo(0);
+ table.integer("bytesDownloaded").notNullable().defaultTo(0);
+ table.datetime("lastTimePlayed");
+ table.float("fileSize").notNullable().defaultTo(0);
+ table.text("uri");
+ table.boolean("isDeleted").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table
+ .integer("repackId")
+ .references("repack.id")
+ .unique("repack_repackId_unique_" + timestamp);
+ });
+ }
+ });
+
+ await knex.schema.hasTable("user_preferences").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("user_preferences", (table) => {
+ table.increments("id").primary();
+ table.text("downloadsPath");
+ table.text("language").notNullable().defaultTo("en");
+ table.text("realDebridApiToken");
+ table
+ .boolean("downloadNotificationsEnabled")
+ .notNullable()
+ .defaultTo(0);
+ table
+ .boolean("repackUpdatesNotificationsEnabled")
+ .notNullable()
+ .defaultTo(0);
+ table.boolean("preferQuitInsteadOfHiding").notNullable().defaultTo(0);
+ table.boolean("runAtStartup").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+ }
+ });
+
+ await knex.schema.hasTable("game_shop_cache").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("game_shop_cache", (table) => {
+ table.text("objectID").primary().notNullable();
+ table.text("shop").notNullable();
+ table.text("serializedData");
+ table.text("howLongToBeatSerializedData");
+ table.text("language");
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+ }
+ });
+
+ await knex.schema.hasTable("download_queue").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("download_queue", (table) => {
+ table.increments("id").primary();
+ table
+ .integer("gameId")
+ .references("game.id")
+ .unique("download_queue_gameId_unique_" + timestamp);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+ }
+ });
+
+ await knex.schema.hasTable("user_auth").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("user_auth", (table) => {
+ table.increments("id").primary();
+ table.text("userId").notNullable().defaultTo("");
+ table.text("displayName").notNullable().defaultTo("");
+ table.text("profileImageUrl");
+ table.text("accessToken").notNullable().defaultTo("");
+ table.text("refreshToken").notNullable().defaultTo("");
+ table.integer("tokenExpirationTimestamp").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+ }
+ });
+};
+
+export const down = async (knex: Knex) => {
+ await knex.schema.dropTableIfExists("game");
+ await knex.schema.dropTableIfExists("repack");
+ await knex.schema.dropTableIfExists("download_queue");
+ await knex.schema.dropTableIfExists("user_auth");
+ await knex.schema.dropTableIfExists("game_shop_cache");
+ await knex.schema.dropTableIfExists("user_preferences");
+ await knex.schema.dropTableIfExists("download_source");
+};
diff --git a/src/main/migrations/002_RepackUris.ts b/src/main/migrations/002_RepackUris.ts
new file mode 100644
index 00000000..50d38d0f
--- /dev/null
+++ b/src/main/migrations/002_RepackUris.ts
@@ -0,0 +1,56 @@
+import { Knex } from "knex";
+
+export const name = "002_RepackUris";
+
+export const up = async (knex: Knex) => {
+ await knex.schema.createTable("temporary_repack", (table) => {
+ const timestamp = new Date().getTime();
+ table.increments("id").primary();
+ table
+ .text("title")
+ .notNullable()
+ .unique({ indexName: "repack_title_unique_" + timestamp });
+ table
+ .text("magnet")
+ .notNullable()
+ .unique({ indexName: "repack_magnet_unique_" + timestamp });
+ table.text("repacker").notNullable();
+ table.text("fileSize").notNullable();
+ table.datetime("uploadDate").notNullable();
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table
+ .integer("downloadSourceId")
+ .references("download_source.id")
+ .onDelete("CASCADE");
+ table.text("uris").notNullable().defaultTo("[]");
+ });
+ await knex.raw(
+ `INSERT INTO "temporary_repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`
+ );
+ await knex.schema.dropTable("repack");
+ await knex.schema.renameTable("temporary_repack", "repack");
+};
+
+export const down = async (knex: Knex) => {
+ await knex.schema.renameTable("repack", "temporary_repack");
+ await knex.schema.createTable("repack", (table) => {
+ table.increments("id").primary();
+ table.text("title").notNullable().unique();
+ table.text("magnet").notNullable().unique();
+ table.integer("page");
+ table.text("repacker").notNullable();
+ table.text("fileSize").notNullable();
+ table.datetime("uploadDate").notNullable();
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table
+ .integer("downloadSourceId")
+ .references("download_source.id")
+ .onDelete("CASCADE");
+ });
+ await knex.raw(
+ `INSERT INTO "repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`
+ );
+ await knex.schema.dropTable("temporary_repack");
+};
diff --git a/src/main/migrations/1724081695967-Hydra_2_0_3.ts b/src/main/migrations/1724081695967-Hydra_2_0_3.ts
deleted file mode 100644
index 5ab18acb..00000000
--- a/src/main/migrations/1724081695967-Hydra_2_0_3.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { MigrationInterface, QueryRunner } from "typeorm";
-
-export class Hydra2031724081695967 implements MigrationInterface {
- name = 'Hydra2031724081695967'
-
- public async up(queryRunner: QueryRunner): Promise {
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_source" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "url" text, "name" text NOT NULL, "etag" text, "downloadCount" integer NOT NULL DEFAULT (0), "status" text NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), CONSTRAINT "UQ_aec2879321a87e9bb2ed477981a" UNIQUE ("url"))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "user_preferences" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "downloadsPath" text, "language" text NOT NULL DEFAULT ('en'), "realDebridApiToken" text, "downloadNotificationsEnabled" boolean NOT NULL DEFAULT (0), "repackUpdatesNotificationsEnabled" boolean NOT NULL DEFAULT (0), "preferQuitInsteadOfHiding" boolean NOT NULL DEFAULT (0), "runAtStartup" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game_shop_cache" ("objectID" text PRIMARY KEY NOT NULL, "shop" text NOT NULL, "serializedData" text, "howLongToBeatSerializedData" text, "language" text, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "user_auth" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "userId" text NOT NULL DEFAULT (''), "displayName" text NOT NULL DEFAULT (''), "profileImageUrl" text, "accessToken" text NOT NULL DEFAULT (''), "refreshToken" text NOT NULL DEFAULT (''), "tokenExpirationTimestamp" integer NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"), CONSTRAINT "FK_0c1d6445ad047d9bbd256f961f6" FOREIGN KEY ("repackId") REFERENCES "repack" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
- await queryRunner.query(`INSERT INTO "temporary_game"("id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId") SELECT "id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId" FROM "game"`);
- await queryRunner.query(`DROP TABLE "game"`);
- await queryRunner.query(`ALTER TABLE "temporary_game" RENAME TO "game"`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
- await queryRunner.query(`INSERT INTO "temporary_repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`);
- await queryRunner.query(`DROP TABLE "repack"`);
- await queryRunner.query(`ALTER TABLE "temporary_repack" RENAME TO "repack"`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "temporary_download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"), CONSTRAINT "FK_aed852c94d9ded617a7a07f5415" FOREIGN KEY ("gameId") REFERENCES "game" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
- await queryRunner.query(`INSERT INTO "temporary_download_queue"("id", "createdAt", "updatedAt", "gameId") SELECT "id", "createdAt", "updatedAt", "gameId" FROM "download_queue"`);
- await queryRunner.query(`DROP TABLE "download_queue"`);
- await queryRunner.query(`ALTER TABLE "temporary_download_queue" RENAME TO "download_queue"`);
- }
-
- public async down(queryRunner: QueryRunner): Promise {
- await queryRunner.query(`ALTER TABLE "download_queue" RENAME TO "temporary_download_queue"`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "download_queue" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "gameId" integer, CONSTRAINT "REL_aed852c94d9ded617a7a07f541" UNIQUE ("gameId"))`);
- await queryRunner.query(`INSERT INTO "download_queue"("id", "createdAt", "updatedAt", "gameId") SELECT "id", "createdAt", "updatedAt", "gameId" FROM "temporary_download_queue"`);
- await queryRunner.query(`DROP TABLE "temporary_download_queue"`);
- await queryRunner.query(`ALTER TABLE "repack" RENAME TO "temporary_repack"`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"))`);
- await queryRunner.query(`INSERT INTO "repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`);
- await queryRunner.query(`DROP TABLE "temporary_repack"`);
- await queryRunner.query(`ALTER TABLE "game" RENAME TO "temporary_game"`);
- await queryRunner.query(`CREATE TABLE IF NOT EXISTS "game" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "objectID" text NOT NULL, "remoteId" text, "title" text NOT NULL, "iconUrl" text, "folderName" text, "downloadPath" text, "executablePath" text, "playTimeInMilliseconds" integer NOT NULL DEFAULT (0), "shop" text NOT NULL, "status" text, "downloader" integer NOT NULL DEFAULT (1), "progress" float NOT NULL DEFAULT (0), "bytesDownloaded" integer NOT NULL DEFAULT (0), "lastTimePlayed" datetime, "fileSize" float NOT NULL DEFAULT (0), "uri" text, "isDeleted" boolean NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "repackId" integer, CONSTRAINT "UQ_04293f46e8db3deaec8dfb69264" UNIQUE ("objectID"), CONSTRAINT "UQ_6dac8c3148e141251a4864e94d4" UNIQUE ("remoteId"), CONSTRAINT "REL_0c1d6445ad047d9bbd256f961f" UNIQUE ("repackId"))`);
- await queryRunner.query(`INSERT INTO "game"("id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId") SELECT "id", "objectID", "remoteId", "title", "iconUrl", "folderName", "downloadPath", "executablePath", "playTimeInMilliseconds", "shop", "status", "downloader", "progress", "bytesDownloaded", "lastTimePlayed", "fileSize", "uri", "isDeleted", "createdAt", "updatedAt", "repackId" FROM "temporary_game"`);
- await queryRunner.query(`DROP TABLE "temporary_game"`);
- await queryRunner.query(`DROP TABLE "user_auth"`);
- await queryRunner.query(`DROP TABLE "download_queue"`);
- await queryRunner.query(`DROP TABLE "game_shop_cache"`);
- await queryRunner.query(`DROP TABLE "user_preferences"`);
- await queryRunner.query(`DROP TABLE "repack"`);
- await queryRunner.query(`DROP TABLE "download_source"`);
- await queryRunner.query(`DROP TABLE "game"`);
- }
-
-}
diff --git a/src/main/migrations/1724081984535-DowloadsRefactor.ts b/src/main/migrations/1724081984535-DowloadsRefactor.ts
deleted file mode 100644
index 3afc8444..00000000
--- a/src/main/migrations/1724081984535-DowloadsRefactor.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { MigrationInterface, QueryRunner } from "typeorm";
-
-export class DowloadsRefactor1724081984535 implements MigrationInterface {
- name = 'DowloadsRefactor1724081984535'
-
- public async up(queryRunner: QueryRunner): Promise {
- await queryRunner.query(`CREATE TABLE "temporary_repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, "uris" text NOT NULL DEFAULT ('[]'), CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
- await queryRunner.query(`INSERT INTO "temporary_repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`);
- await queryRunner.query(`DROP TABLE "repack"`);
- await queryRunner.query(`ALTER TABLE "temporary_repack" RENAME TO "repack"`);
- }
-
- public async down(queryRunner: QueryRunner): Promise {
- await queryRunner.query(`ALTER TABLE "repack" RENAME TO "temporary_repack"`);
- await queryRunner.query(`CREATE TABLE "repack" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" text NOT NULL, "magnet" text NOT NULL, "page" integer, "repacker" text NOT NULL, "fileSize" text NOT NULL, "uploadDate" datetime NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "downloadSourceId" integer, CONSTRAINT "UQ_5e8d57798643e693bced32095d2" UNIQUE ("magnet"), CONSTRAINT "UQ_a46a68496754a4d429ddf9d48ec" UNIQUE ("title"), CONSTRAINT "FK_13f131029be1dd361fd3cd9c2a6" FOREIGN KEY ("downloadSourceId") REFERENCES "download_source" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
- await queryRunner.query(`INSERT INTO "repack"("id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "page", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`);
- await queryRunner.query(`DROP TABLE "temporary_repack"`);
- }
-
-}
diff --git a/src/main/migrations/index.ts b/src/main/migrations/index.ts
index 5546bce0..3859255a 100644
--- a/src/main/migrations/index.ts
+++ b/src/main/migrations/index.ts
@@ -1,2 +1,2 @@
-export * from "./1724081695967-Hydra_2_0_3";
-export * from "./1724081984535-DowloadsRefactor";
+export * as hydra from "./001_Hydra_2_0_3";
+export * as downloadRefactor from "./002_RepackUris";
diff --git a/yarn.lock b/yarn.lock
index 74b9a8a1..9ffbf746 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2870,10 +2870,10 @@ bep53-range@^2.0.0:
resolved "https://registry.npmjs.org/bep53-range/-/bep53-range-2.0.0.tgz"
integrity sha512-sMm2sV5PRs0YOVk0LTKtjuIprVzxgTQUsrGX/7Yph2Rm4FO2Fqqtq7hNjsOB5xezM4v4+5rljCgK++UeQJZguA==
-better-sqlite3@^9.5.0:
- version "9.6.0"
- resolved "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.6.0.tgz"
- integrity sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ==
+better-sqlite3@^11.2.1:
+ version "11.2.1"
+ resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-11.2.1.tgz#3c6b8a8e2e12444d380e811796b59c8aba012e03"
+ integrity sha512-Xbt1d68wQnUuFIEVsbt6V+RG30zwgbtCGQ4QOcXVrOH0FE4eHk64FWZ9NUfRHS4/x1PXqwz/+KOrnXD7f0WieA==
dependencies:
bindings "^1.5.0"
prebuild-install "^7.1.1"
@@ -3245,6 +3245,11 @@ color@^4.2.3:
color-convert "^2.0.1"
color-string "^1.9.0"
+colorette@2.0.19:
+ version "2.0.19"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
+ integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
+
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
@@ -3252,6 +3257,11 @@ combined-stream@^1.0.8:
dependencies:
delayed-stream "~1.0.0"
+commander@^10.0.0:
+ version "10.0.1"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
+ integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
+
commander@^5.0.0:
version "5.1.0"
resolved "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz"
@@ -3462,7 +3472,7 @@ dayjs@^1.11.9:
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz"
integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==
-debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
+debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -4101,6 +4111,11 @@ eslint@^8.56.0:
strip-ansi "^6.0.1"
text-table "^0.2.0"
+esm@^3.2.25:
+ version "3.2.25"
+ resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
+ integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
+
espree@^9.6.0, espree@^9.6.1:
version "9.6.1"
resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz"
@@ -4463,6 +4478,11 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@
has-symbols "^1.0.3"
hasown "^2.0.0"
+get-package-type@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
+ integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
+
get-stdin@^9.0.0:
version "9.0.0"
resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz"
@@ -4489,6 +4509,11 @@ get-symbol-description@^1.0.2:
es-errors "^1.3.0"
get-intrinsic "^1.2.4"
+getopts@2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4"
+ integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==
+
git-raw-commits@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz"
@@ -4909,6 +4934,11 @@ internal-slot@^1.0.7:
hasown "^2.0.0"
side-channel "^1.0.4"
+interpret@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
+ integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
+
is-array-buffer@^3.0.4:
version "3.0.4"
resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz"
@@ -5350,6 +5380,26 @@ keyv@^4.0.0, keyv@^4.5.3:
dependencies:
json-buffer "3.0.1"
+knex@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/knex/-/knex-3.1.0.tgz#b6ddd5b5ad26a6315234a5b09ec38dc4a370bd8c"
+ integrity sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==
+ dependencies:
+ colorette "2.0.19"
+ commander "^10.0.0"
+ debug "4.3.4"
+ escalade "^3.1.1"
+ esm "^3.2.25"
+ get-package-type "^0.1.0"
+ getopts "2.3.0"
+ interpret "^2.2.0"
+ lodash "^4.17.21"
+ pg-connection-string "2.6.2"
+ rechoir "^0.8.0"
+ resolve-from "^5.0.0"
+ tarn "^3.0.2"
+ tildify "2.0.0"
+
language-subtag-registry@^0.3.20:
version "0.3.22"
resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz"
@@ -5489,7 +5539,7 @@ lodash.upperfirst@^4.3.1:
resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz"
integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==
-lodash@^4.17.15:
+lodash@^4.17.15, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -6114,6 +6164,11 @@ pend@~1.2.0:
resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz"
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
+pg-connection-string@2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.2.tgz#713d82053de4e2bd166fab70cd4f26ad36aab475"
+ integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==
+
pg-int8@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
@@ -6457,6 +6512,13 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
+rechoir@^0.8.0:
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22"
+ integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==
+ dependencies:
+ resolve "^1.20.0"
+
redux-thunk@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz"
@@ -6554,7 +6616,7 @@ resolve-from@^5.0.0:
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
-resolve@^1.22.1:
+resolve@^1.20.0, resolve@^1.22.1:
version "1.22.8"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
@@ -7083,6 +7145,11 @@ tar@^6.1.12:
mkdirp "^1.0.3"
yallist "^4.0.0"
+tarn@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693"
+ integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==
+
temp-file@^3.4.0:
version "3.4.0"
resolved "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz"
@@ -7120,6 +7187,11 @@ thenify-all@^1.0.0:
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
+tildify@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a"
+ integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==
+
tiny-typed-emitter@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz"
From 84c6951d30ba5c7a898d723e9b84288eb5c6466e Mon Sep 17 00:00:00 2001
From: Thales Euflauzino <120137721+thaleseuflauzino@users.noreply.github.com>
Date: Tue, 27 Aug 2024 01:59:41 -0300
Subject: [PATCH 082/108] chore: rename pt to ptbr to add portugal language to
hydra
---
src/locales/ptbr/translation.json | 1 +
1 file changed, 1 insertion(+)
create mode 100644 src/locales/ptbr/translation.json
diff --git a/src/locales/ptbr/translation.json b/src/locales/ptbr/translation.json
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/src/locales/ptbr/translation.json
@@ -0,0 +1 @@
+
From 7867b7c838cb0163ff1b8ee0979dc1fce4f44629 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino <120137721+thaleseuflauzino@users.noreply.github.com>
Date: Tue, 27 Aug 2024 02:00:15 -0300
Subject: [PATCH 083/108] chore: add ptbr translate
---
src/locales/ptbr/translation.json | 278 ++++++++++++++++++++++++++++++
1 file changed, 278 insertions(+)
diff --git a/src/locales/ptbr/translation.json b/src/locales/ptbr/translation.json
index 8b137891..3fc31951 100644
--- a/src/locales/ptbr/translation.json
+++ b/src/locales/ptbr/translation.json
@@ -1 +1,279 @@
+{
+ "app": {
+ "successfully_signed_in": "Autenticado com sucesso"
+ },
+ "home": {
+ "featured": "Destaques",
+ "trending": "Populares",
+ "surprise_me": "Surpreenda-me",
+ "no_results": "Nenhum resultado encontrado"
+ },
+ "sidebar": {
+ "catalogue": "Catálogo",
+ "downloads": "Downloads",
+ "settings": "Ajustes",
+ "my_library": "Biblioteca",
+ "downloading_metadata": "{{title}} (Baixando metadados…)",
+ "paused": "{{title}} (Pausado)",
+ "downloading": "{{title}} ({{percentage}} - Baixando…)",
+ "filter": "Buscar",
+ "home": "Início",
+ "queued": "{{title}} (Na fila)",
+ "game_has_no_executable": "Jogo não possui executável selecionado",
+ "sign_in": "Login"
+ },
+ "header": {
+ "search": "Buscar jogos",
+ "catalogue": "Catálogo",
+ "downloads": "Downloads",
+ "search_results": "Resultados da busca",
+ "settings": "Ajustes",
+ "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 downloads em andamento",
+ "downloading_metadata": "Baixando metadados de {{title}}…",
+ "downloading": "Baixando {{title}}… ({{percentage}} concluído) - Conclusão {{eta}} - {{speed}}",
+ "calculating_eta": "Baixando {{title}}… ({{percentage}} concluído) - Calculando tempo restante…",
+ "checking_files": "Verificando arquivos de {{title}}…"
+ },
+ "game_details": {
+ "open_download_options": "Ver opções de download",
+ "download_options_zero": "Sem opções de download",
+ "download_options_one": "{{count}} opção de download",
+ "download_options_other": "{{count}} opções de download",
+ "updated_at": "Atualizado {{updated_at}}",
+ "resume": "Retomar",
+ "pause": "Pausar",
+ "cancel": "Cancelar",
+ "remove": "Remover",
+ "space_left_on_disk": "{{space}} livres em disco",
+ "eta": "Conclusão {{eta}}",
+ "calculating_eta": "Calculando tempo restante…",
+ "downloading_metadata": "Baixando metadados…",
+ "filter": "Filtrar repacks",
+ "requirements": "Requisitos de 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": "Nenhum download 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": "Você ainda não jogou {{title}}",
+ "close": "Fechar",
+ "deleting": "Excluindo instalador…",
+ "playing_now": "Jogando agora",
+ "change": "Explorar",
+ "repacks_modal_description": "Escolha o repack do jogo que deseja baixar",
+ "select_folder_hint": "Para trocar o diretório padrão, acesse a <0>Tela de Ajustes0>",
+ "download_now": "Iniciar download",
+ "no_shop_details": "Não foi possível obter os detalhes da loja.",
+ "download_options": "Opções de download",
+ "download_path": "Diretório de download",
+ "previous_screenshot": "Captura de tela anterior",
+ "next_screenshot": "Próxima captura de tela",
+ "screenshot": "Captura de tela {{number}}",
+ "open_screenshot": "Ver captura de tela {{number}}",
+ "download_settings": "Ajustes do download",
+ "downloader": "Downloader",
+ "select_executable": "Explorar",
+ "no_executable_selected": "Nenhum executável selecionado",
+ "open_folder": "Abrir pasta",
+ "open_download_location": "Ver arquivos baixados",
+ "create_shortcut": "Criar atalho na área de trabalho",
+ "remove_files": "Remover arquivos",
+ "options": "Gerenciar",
+ "remove_from_library_description": "Isso irá remover {{game}} da sua biblioteca",
+ "remove_from_library_title": "Tem certeza?",
+ "executable_section_title": "Executável",
+ "executable_section_description": "O caminho do arquivo que será executado ao clicar em \"Jogar\"",
+ "downloads_secion_title": "Downloads",
+ "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 arquivos que foram baixados pelo Hydra",
+ "download_in_progress": "Download em andamento",
+ "download_paused": "Download pausado",
+ "last_downloaded_option": "Última opção baixada",
+ "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 seu código de ativação",
+ "message": "Se você não sabe onde conseguir o código, talvez você não devesse estar aqui.",
+ "activate": "Ativar",
+ "loading": "Carregando…"
+ },
+ "downloads": {
+ "resume": "Retomar",
+ "pause": "Pausar",
+ "eta": "Conclusão {{eta}}",
+ "paused": "Pausado",
+ "verifying": "Verificando…",
+ "completed": "Concluído",
+ "removed": "Cancelado",
+ "cancel": "Cancelar",
+ "filter": "Filtrar jogos baixados",
+ "remove": "Remover",
+ "downloading_metadata": "Baixando metadados…",
+ "delete": "Remover instalador",
+ "delete_modal_description": "Isso removerá todos os arquivos de instalação do seu computador",
+ "delete_modal_title": "Tem certeza?",
+ "deleting": "Excluindo instalador…",
+ "install": "Instalar",
+ "download_in_progress": "Baixando agora",
+ "queued_downloads": "Na fila",
+ "downloads_completed": "Completo",
+ "queued": "Na fila",
+ "no_downloads_title": "Nada por aqui…",
+ "no_downloads_description": "Você ainda não baixou nada pelo Hydra, mas nunca é tarde para começar.",
+ "checking_files": "Verificando arquivos…"
+ },
+ "settings": {
+ "downloads_path": "Diretório dos downloads",
+ "change": "Explorar...",
+ "notifications": "Notificações",
+ "enable_download_notifications": "Quando um download for concluído",
+ "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 junto com o sistema",
+ "general": "Geral",
+ "behavior": "Comportamento",
+ "download_sources": "Fontes de download",
+ "language": "Idioma",
+ "real_debrid_api_token": "Token de API",
+ "enable_real_debrid": "Habilitar Real-Debrid",
+ "real_debrid_api_token_hint": "Você pode obter seu token de API <0>aqui0>",
+ "real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite baixar arquivos 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, assine a Real-Debrid",
+ "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.",
+ "validate_download_source": "Validar",
+ "remove_download_source": "Remover",
+ "add_download_source": "Adicionar fonte",
+ "download_count_zero": "Sem downloads na lista",
+ "download_count_one": "{{countFormatted}} download na lista",
+ "download_count_other": "{{countFormatted}} downloads na lista",
+ "download_options_zero": "Sem downloads disponíveis",
+ "download_options_one": "{{countFormatted}} download disponível",
+ "download_options_other": "{{countFormatted}} downloads disponíveis",
+ "download_source_url": "URL da fonte",
+ "add_download_source_description": "Insira a 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 a url de um JSON válido",
+ "found_download_option_zero": "Nenhuma opção de download encontrada",
+ "found_download_option_one": "{{countFormatted}} opção de download encontrada",
+ "found_download_option_other": "{{countFormatted}} opções de download encontradas",
+ "import": "Importar"
+ },
+ "notifications": {
+ "download_complete": "Download concluído",
+ "game_ready_to_install": "{{title}} está pronto para ser instalado",
+ "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 downloads 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 no seu 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 senha"
+ },
+ "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 você não jogou nada recentemente. Que tal começar agora?",
+ "display_name": "Nome de exibição",
+ "saving": "Salvando…",
+ "save": "Salvar",
+ "edit_profile": "Editar perfil",
+ "saved_successfully": "Salvo com sucesso",
+ "try_again": "Por favor, tente novamente",
+ "cancel": "Cancelar",
+ "successfully_signed_out": "Deslogado com sucesso",
+ "sign_out": "Sair da conta",
+ "sign_out_modal_title": "Tem certeza?",
+ "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",
+ "friend_code": "Código de amigo",
+ "see_profile": "Ver perfil",
+ "friend_request_sent": "Pedido de amizade enviado",
+ "friends": "Amigos",
+ "add": "Adicionar",
+ "sending": "Enviando",
+ "friends_list": "Lista de amigos",
+ "user_not_found": "Usuário 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": "Usuário bloqueado com sucesso",
+ "user_block_modal_text": "Bloquear {{displayName}}",
+ "settings": "Configurações",
+ "privacy": "Privacidade",
+ "private": "Privado",
+ "friends_only": "Apenas amigos",
+ "public": "Público",
+ "blocked_users": "Usuários bloqueados",
+ "unblock": "Desbloquear",
+ "no_friends_added": "Você ainda não possui amigos adicionados",
+ "pending": "Pendentes",
+ "no_pending_invites": "Você não possui convites de amizade pendentes",
+ "no_blocked_users": "Você não tem nenhum usuário bloqueado",
+ "friend_code_copied": "Código de amigo copiado"
+ }
+}
From 75bc386048d354a9e52bf21af7a9d622dc4a5317 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino <120137721+thaleseuflauzino@users.noreply.github.com>
Date: Tue, 27 Aug 2024 02:29:51 -0300
Subject: [PATCH 084/108] feat: add pt(portugal) translate
---
src/locales/pt/translation.json | 226 ++++++++++++++++----------------
1 file changed, 115 insertions(+), 111 deletions(-)
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index 36d38c96..ceeec7b2 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -1,60 +1,60 @@
{
"app": {
- "successfully_signed_in": "Autenticado com sucesso"
+ "successfully_signed_in": "Sessão iniciada com sucesso"
},
"home": {
"featured": "Destaques",
"trending": "Populares",
- "surprise_me": "Surpreenda-me",
+ "surprise_me": "Surpreende-me",
"no_results": "Nenhum resultado encontrado"
},
"sidebar": {
"catalogue": "Catálogo",
- "downloads": "Downloads",
- "settings": "Ajustes",
+ "downloads": "Transferências",
+ "settings": "Definições",
"my_library": "Biblioteca",
- "downloading_metadata": "{{title}} (Baixando metadados…)",
+ "downloading_metadata": "{{title}} (A transferir metadados…)",
"paused": "{{title}} (Pausado)",
- "downloading": "{{title}} ({{percentage}} - Baixando…)",
- "filter": "Buscar",
+ "downloading": "{{title}} ({{percentage}} - A transferir…)",
+ "filter": "Procurar",
"home": "Início",
"queued": "{{title}} (Na fila)",
- "game_has_no_executable": "Jogo não possui executável selecionado",
- "sign_in": "Login"
+ "game_has_no_executable": "Jogo não tem executável selecionado",
+ "sign_in": "Iniciar sessão"
},
"header": {
- "search": "Buscar jogos",
+ "search": "Procurar jogos",
"catalogue": "Catálogo",
- "downloads": "Downloads",
- "search_results": "Resultados da busca",
- "settings": "Ajustes",
+ "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 downloads em andamento",
- "downloading_metadata": "Baixando metadados de {{title}}…",
- "downloading": "Baixando {{title}}… ({{percentage}} concluído) - Conclusão {{eta}} - {{speed}}",
- "calculating_eta": "Baixando {{title}}… ({{percentage}} concluído) - Calculando tempo restante…",
- "checking_files": "Verificando arquivos de {{title}}…"
+ "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 download",
- "download_options_zero": "Sem opções de download",
- "download_options_one": "{{count}} opção de download",
- "download_options_other": "{{count}} opções de download",
- "updated_at": "Atualizado {{updated_at}}",
+ "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 em disco",
+ "space_left_on_disk": "{{space}} livres no disco",
"eta": "Conclusão {{eta}}",
- "calculating_eta": "Calculando tempo restante…",
- "downloading_metadata": "Baixando metadados…",
+ "calculating_eta": "A calcular tempo restante…",
+ "downloading_metadata": "A transferir metadados…",
"filter": "Filtrar repacks",
- "requirements": "Requisitos de sistema",
+ "requirements": "Requisitos do sistema",
"minimum": "Mínimos",
"recommended": "Recomendados",
"paused": "Pausado",
@@ -67,132 +67,136 @@
"accuracy": "{{accuracy}}% de precisão",
"add_to_library": "Adicionar à biblioteca",
"remove_from_library": "Remover da biblioteca",
- "no_downloads": "Nenhum download disponível",
+ "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": "Você ainda não jogou {{title}}",
+ "not_played_yet": "Ainda não jogou {{title}}",
"close": "Fechar",
- "deleting": "Excluindo instalador…",
- "playing_now": "Jogando agora",
+ "deleting": "A eliminar instalador…",
+ "playing_now": "A jogar agora",
"change": "Explorar",
- "repacks_modal_description": "Escolha o repack do jogo que deseja baixar",
- "select_folder_hint": "Para trocar o diretório padrão, acesse a <0>Tela de Ajustes0>",
- "download_now": "Iniciar download",
+ "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ções0>",
+ "download_now": "Iniciar transferência",
"no_shop_details": "Não foi possível obter os detalhes da loja.",
- "download_options": "Opções de download",
- "download_path": "Diretório de download",
- "previous_screenshot": "Captura de tela anterior",
- "next_screenshot": "Próxima captura de tela",
- "screenshot": "Captura de tela {{number}}",
- "open_screenshot": "Ver captura de tela {{number}}",
- "download_settings": "Ajustes do download",
+ "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 arquivos baixados",
- "create_shortcut": "Criar atalho na área de trabalho",
- "remove_files": "Remover arquivos",
- "options": "Gerenciar",
- "remove_from_library_description": "Isso irá remover {{game}} da sua biblioteca",
- "remove_from_library_title": "Tem certeza?",
+ "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 arquivo que será executado ao clicar em \"Jogar\"",
- "downloads_secion_title": "Downloads",
+ "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 arquivos que foram baixados pelo Hydra",
- "download_in_progress": "Download em andamento",
- "download_paused": "Download pausado",
- "last_downloaded_option": "Última opção baixada",
+ "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 seu código de ativação",
- "message": "Se você não sabe onde conseguir o código, talvez você não devesse estar aqui.",
+ "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": "Carregando…"
+ "loading": "A carregar…"
},
"downloads": {
"resume": "Retomar",
"pause": "Pausar",
"eta": "Conclusão {{eta}}",
"paused": "Pausado",
- "verifying": "Verificando…",
+ "verifying": "A verificar…",
"completed": "Concluído",
"removed": "Cancelado",
"cancel": "Cancelar",
- "filter": "Filtrar jogos baixados",
+ "filter": "Filtrar jogos transferidos",
"remove": "Remover",
- "downloading_metadata": "Baixando metadados…",
+ "downloading_metadata": "A transferir metadados…",
"delete": "Remover instalador",
- "delete_modal_description": "Isso removerá todos os arquivos de instalação do seu computador",
- "delete_modal_title": "Tem certeza?",
- "deleting": "Excluindo 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": "Baixando agora",
+ "download_in_progress": "A transferir agora",
"queued_downloads": "Na fila",
- "downloads_completed": "Completo",
+ "downloads_completed": "Concluído",
"queued": "Na fila",
"no_downloads_title": "Nada por aqui…",
- "no_downloads_description": "Você ainda não baixou nada pelo Hydra, mas nunca é tarde para começar.",
- "checking_files": "Verificando arquivos…"
+ "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 dos downloads",
+ "downloads_path": "Diretório das transferências",
"change": "Explorar...",
"notifications": "Notificações",
- "enable_download_notifications": "Quando um download for concluído",
+ "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 junto com o sistema",
+ "launch_with_system": "Iniciar o Hydra com o sistema",
"general": "Geral",
"behavior": "Comportamento",
- "download_sources": "Fontes de download",
+ "download_sources": "Fontes de transferência",
"language": "Idioma",
"real_debrid_api_token": "Token de API",
- "enable_real_debrid": "Habilitar Real-Debrid",
- "real_debrid_api_token_hint": "Você pode obter seu token de API <0>aqui0>",
- "real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite baixar arquivos instantaneamente e com a melhor velocidade da sua Internet.",
+ "enable_real_debrid": "Ativar Real-Debrid",
+ "real_debrid_api_token_hint": "Pode obter o seu token de API <0>aqui0>",
+ "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, assine a Real-Debrid",
+ "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": "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.",
+ "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 downloads na lista",
- "download_count_one": "{{countFormatted}} download na lista",
- "download_count_other": "{{countFormatted}} downloads na lista",
- "download_options_zero": "Sem downloads disponíveis",
- "download_options_one": "{{countFormatted}} download disponível",
- "download_options_other": "{{countFormatted}} downloads disponíveis",
+ "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 a URL contendo o arquivo .json",
+ "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 a url de um JSON válido",
- "found_download_option_zero": "Nenhuma opção de download encontrada",
- "found_download_option_one": "{{countFormatted}} opção de download encontrada",
- "found_download_option_other": "{{countFormatted}} opções de download encontradas",
+ "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": "Download concluído",
- "game_ready_to_install": "{{title}} está pronto para ser instalado",
+ "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",
@@ -204,12 +208,12 @@
"quit": "Fechar"
},
"game_card": {
- "no_downloads": "Sem downloads disponíveis"
+ "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 no seu distro Linux, garantindo assim a execução normal do jogo"
+ "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",
@@ -219,7 +223,7 @@
"close": "Botão de fechar"
},
"forms": {
- "toggle_password_visibility": "Alternar visibilidade da senha"
+ "toggle_password_visibility": "Alternar visibilidade da palavra-passe"
},
"user_profile": {
"amount_hours": "{{amount}} horas",
@@ -229,28 +233,28 @@
"library": "Biblioteca",
"total_play_time": "Tempo total de jogo: {{amount}}",
"no_recent_activity_title": "Hmmm… nada por aqui",
- "no_recent_activity_description": "Parece que você não jogou nada recentemente. Que tal começar agora?",
+ "no_recent_activity_description": "Parece que não jogaste nada recentemente. Que tal começar agora?",
"display_name": "Nome de exibição",
- "saving": "Salvando…",
- "save": "Salvar",
+ "saving": "a guardar…",
+ "save": "Guardar",
"edit_profile": "Editar perfil",
- "saved_successfully": "Salvo com sucesso",
- "try_again": "Por favor, tente novamente",
+ "saved_successfully": "Guardado com sucesso",
+ "try_again": "Por favor, tenta novamente",
"cancel": "Cancelar",
- "successfully_signed_out": "Deslogado com sucesso",
- "sign_out": "Sair da conta",
- "sign_out_modal_title": "Tem certeza?",
- "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?",
+ "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": "Enviando",
+ "sending": "A enviar",
"friends_list": "Lista de amigos",
- "user_not_found": "Usuário não encontrado",
+ "user_not_found": "Utilizador não encontrado",
"block_user": "Bloquear",
"add_friend": "Adicionar amigo",
"request_sent": "Pedido enviado",
@@ -260,19 +264,19 @@
"cancel_request": "Cancelar pedido",
"undo_friendship": "Desfazer amizade",
"request_accepted": "Pedido de amizade aceito",
- "user_blocked_successfully": "Usuário bloqueado com sucesso",
+ "user_blocked_successfully": "Utilizador bloqueado com sucesso",
"user_block_modal_text": "Bloquear {{displayName}}",
- "settings": "Configurações",
+ "settings": "Definições",
"privacy": "Privacidade",
"private": "Privado",
"friends_only": "Apenas amigos",
"public": "Público",
- "blocked_users": "Usuários bloqueados",
+ "blocked_users": "Utilizadores bloqueados",
"unblock": "Desbloquear",
- "no_friends_added": "Você ainda não possui amigos adicionados",
+ "no_friends_added": "Ainda não adicionaste amigos",
"pending": "Pendentes",
- "no_pending_invites": "Você não possui convites de amizade pendentes",
- "no_blocked_users": "Você não tem nenhum usuário bloqueado",
+ "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"
}
}
From 0b54a33084d50a3d9c9f1dd615181101f47c8ba3 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino <120137721+thaleseuflauzino@users.noreply.github.com>
Date: Tue, 27 Aug 2024 02:31:39 -0300
Subject: [PATCH 085/108] chore: Update index.ts
---
src/locales/index.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/locales/index.ts b/src/locales/index.ts
index 10f128ae..9a0e2f63 100644
--- a/src/locales/index.ts
+++ b/src/locales/index.ts
@@ -1,5 +1,6 @@
export { default as en } from "./en/translation.json";
export { default as pt } from "./pt/translation.json";
+export { default as ptbr } from "./ptbr/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";
From e6f296aba3a898c34e16cd6b9fe062576c66d775 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino <120137721+thaleseuflauzino@users.noreply.github.com>
Date: Tue, 27 Aug 2024 02:36:27 -0300
Subject: [PATCH 086/108] fix: gap between lines and missing ","
---
src/locales/pt/translation.json | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
index ceeec7b2..6fb8b453 100644
--- a/src/locales/pt/translation.json
+++ b/src/locales/pt/translation.json
@@ -168,9 +168,7 @@
"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"
-
-
+ "disable_source": "Desativar",
"validate_download_source": "Validar",
"remove_download_source": "Remover",
"add_download_source": "Adicionar fonte",
From 5b493d8050a39d6c9d799cc2c64c4d381bd85baf Mon Sep 17 00:00:00 2001
From: Thales Euflauzino
Date: Tue, 27 Aug 2024 02:43:12 -0300
Subject: [PATCH 087/108] fix: change name "ptbr" to "br" for prettier
---
src/locales/{ptbr => br}/translation.json | 0
src/locales/index.ts | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
rename src/locales/{ptbr => br}/translation.json (100%)
diff --git a/src/locales/ptbr/translation.json b/src/locales/br/translation.json
similarity index 100%
rename from src/locales/ptbr/translation.json
rename to src/locales/br/translation.json
diff --git a/src/locales/index.ts b/src/locales/index.ts
index 9a0e2f63..a7853d14 100644
--- a/src/locales/index.ts
+++ b/src/locales/index.ts
@@ -1,6 +1,6 @@
export { default as en } from "./en/translation.json";
export { default as pt } from "./pt/translation.json";
-export { default as ptbr } from "./ptbr/translation.json";
+export { default as br } from "./ptbr/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";
From f4e4f7a61cc84a7b782d6a0246c90e5d09354d21 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino
Date: Tue, 27 Aug 2024 02:45:38 -0300
Subject: [PATCH 088/108] fix: error in index.ts, was ptbr
---
src/locales/index.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/locales/index.ts b/src/locales/index.ts
index a7853d14..7cef7a5e 100644
--- a/src/locales/index.ts
+++ b/src/locales/index.ts
@@ -1,6 +1,6 @@
export { default as en } from "./en/translation.json";
export { default as pt } from "./pt/translation.json";
-export { default as br } from "./ptbr/translation.json";
+export { default as br } from "./br/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";
From a809c841512d76b0f7842a5832ad771380443d5c Mon Sep 17 00:00:00 2001
From: Thales Euflauzino
Date: Tue, 27 Aug 2024 02:58:00 -0300
Subject: [PATCH 089/108] fix: run prettier
---
src/locales/br/translation.json | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/locales/br/translation.json b/src/locales/br/translation.json
index 3fc31951..8c7b826a 100644
--- a/src/locales/br/translation.json
+++ b/src/locales/br/translation.json
@@ -1,4 +1,3 @@
-
{
"app": {
"successfully_signed_in": "Autenticado com sucesso"
@@ -167,7 +166,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",
From e4917c1a0784232aa2971eb8f9218cb12b124d60 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino <120137721+thaleseuflauzino@users.noreply.github.com>
Date: Tue, 27 Aug 2024 03:01:10 -0300
Subject: [PATCH 090/108] docs: Create SECURITY.md
---
SECURITY.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
create mode 100644 SECURITY.md
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..852e7cc3
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,68 @@
+# Security Policy
+
+## Purpose of the Policy
+
+The purpose of this Security Policy is to ensure the security of our project and maintain the trust of the community.
+
+## Who is Affected by the Policy
+
+This policy applies to all members of our project community, including developers, testers, repository administrators, and users.
+
+## Supported Versions
+
+Use this section to tell people about which versions of your project are
+currently being supported with security updates.
+
+| Version | Supported |
+| ------- | ------------------ |
+| 2.0.x | :white_check_mark: |
+| < 1.2.0 | :x: |
+
+## Development Recommendations
+
+### Best Practices
+
+- Follow secure coding principles.
+- Use well-established libraries and frameworks.
+- Regularly update dependencies.
+- Conduct thorough testing, including security-related tests.
+
+### Unrecommended Practices
+
+- Do not use known vulnerabilities that have not been patched.
+- Do not publish sensitive information such as API keys or passwords.
+- Do not vote for changes that degrade the security of the project.
+
+### User-Generated Content
+
+- Ensure that user-generated content does not contain hidden threats.
+- Be cautious when handling user data.
+
+### Community Interaction
+
+- Treat each other with respect and politeness.
+- Do not spread spam or spam bots.
+- Follow community guidelines.
+
+### Vulnerability Discovery and Reporting
+
+- If you discover a vulnerability, report it as an issue on GitHub.
+- Your report should contain detailed information about the vulnerability, including steps to resolve it.
+
+### Reporting Method
+
+To report a vulnerability, create a new issue on GitHub and use branch isolation to provide details about the vulnerability.
+
+### Details to Provide
+
+Please provide the following information about the vulnerability:
+
+- Description of the vulnerability
+- Steps to resolve the vulnerability
+- Versions on which the vulnerability was found
+- Code examples illustrating the vulnerability (if it is safe to do so)
+
+### Expected Behavior
+
+- If we accept the reported vulnerability, we will release a patch and update the security information on GitHub.
+- If we reject the reported vulnerability, we will provide an explanation.
From 563e56e54dba06a82c61988bfe9270e87b818c52 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino <120137721+thaleseuflauzino@users.noreply.github.com>
Date: Tue, 27 Aug 2024 14:34:04 -0300
Subject: [PATCH 091/108] chore: update suggestion translation
Co-authored-by: Agnaldo Junior
---
src/locales/br/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/locales/br/translation.json b/src/locales/br/translation.json
index 8c7b826a..98489706 100644
--- a/src/locales/br/translation.json
+++ b/src/locales/br/translation.json
@@ -146,7 +146,7 @@
},
"settings": {
"downloads_path": "Diretório dos downloads",
- "change": "Explorar...",
+ "change": "Alterar",
"notifications": "Notificações",
"enable_download_notifications": "Quando um download for concluído",
"enable_repack_list_notifications": "Quando a lista de repacks for atualizada",
From 7c08668a03b5f22feffbd28f7f61c06daf366350 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino <120137721+thaleseuflauzino@users.noreply.github.com>
Date: Tue, 27 Aug 2024 14:34:14 -0300
Subject: [PATCH 092/108] chore: update suggestion translation
Co-authored-by: Agnaldo Junior
---
src/locales/br/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/locales/br/translation.json b/src/locales/br/translation.json
index 98489706..4f366985 100644
--- a/src/locales/br/translation.json
+++ b/src/locales/br/translation.json
@@ -112,7 +112,7 @@
"create_shortcut_error": "Erro ao criar atalho"
},
"activation": {
- "title": "Ativação",
+ "title": "Ativar Hydra",
"installation_id": "ID da instalação:",
"enter_activation_code": "Insira seu código de ativação",
"message": "Se você não sabe onde conseguir o código, talvez você não devesse estar aqui.",
From 629f20471478c903e2d03478b93b5dbc225068d9 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino <120137721+thaleseuflauzino@users.noreply.github.com>
Date: Tue, 27 Aug 2024 14:34:21 -0300
Subject: [PATCH 093/108] chore: update suggestion translation
Co-authored-by: Agnaldo Junior
---
src/locales/br/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/locales/br/translation.json b/src/locales/br/translation.json
index 4f366985..2b45fc71 100644
--- a/src/locales/br/translation.json
+++ b/src/locales/br/translation.json
@@ -27,7 +27,7 @@
"catalogue": "Catálogo",
"downloads": "Downloads",
"search_results": "Resultados da busca",
- "settings": "Ajustes",
+ "settings": "Configuraçõ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."
From 9e6484ad11573a6241f9458543e21e5362d0da72 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino <120137721+thaleseuflauzino@users.noreply.github.com>
Date: Tue, 27 Aug 2024 14:34:27 -0300
Subject: [PATCH 094/108] chore: update suggestion translation
Co-authored-by: Agnaldo Junior
---
src/locales/br/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/locales/br/translation.json b/src/locales/br/translation.json
index 2b45fc71..c97dd120 100644
--- a/src/locales/br/translation.json
+++ b/src/locales/br/translation.json
@@ -11,7 +11,7 @@
"sidebar": {
"catalogue": "Catálogo",
"downloads": "Downloads",
- "settings": "Ajustes",
+ "settings": "Configurações",
"my_library": "Biblioteca",
"downloading_metadata": "{{title}} (Baixando metadados…)",
"paused": "{{title}} (Pausado)",
From f2c45983517fe12cb0fc5fc8fa3cce0b7fc287f7 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino <120137721+thaleseuflauzino@users.noreply.github.com>
Date: Tue, 27 Aug 2024 14:34:31 -0300
Subject: [PATCH 095/108] chore: update suggestion translation
Co-authored-by: Agnaldo Junior
---
src/locales/br/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/locales/br/translation.json b/src/locales/br/translation.json
index c97dd120..bd355473 100644
--- a/src/locales/br/translation.json
+++ b/src/locales/br/translation.json
@@ -16,7 +16,7 @@
"downloading_metadata": "{{title}} (Baixando metadados…)",
"paused": "{{title}} (Pausado)",
"downloading": "{{title}} ({{percentage}} - Baixando…)",
- "filter": "Buscar",
+ "filter": "Filtrar bibilioteca",
"home": "Início",
"queued": "{{title}} (Na fila)",
"game_has_no_executable": "Jogo não possui executável selecionado",
From d2d00516a8d782ceb22d9a7213b593378a185f32 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino
Date: Tue, 27 Aug 2024 14:38:07 -0300
Subject: [PATCH 096/108] chore: updating translation location
---
src/locales/pt-BR/translation.json | 278 ++++++++++++++++++++++++++++
src/locales/pt-PT/translation.json | 280 +++++++++++++++++++++++++++++
2 files changed, 558 insertions(+)
create mode 100644 src/locales/pt-BR/translation.json
create mode 100644 src/locales/pt-PT/translation.json
diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json
new file mode 100644
index 00000000..8c7b826a
--- /dev/null
+++ b/src/locales/pt-BR/translation.json
@@ -0,0 +1,278 @@
+{
+ "app": {
+ "successfully_signed_in": "Autenticado com sucesso"
+ },
+ "home": {
+ "featured": "Destaques",
+ "trending": "Populares",
+ "surprise_me": "Surpreenda-me",
+ "no_results": "Nenhum resultado encontrado"
+ },
+ "sidebar": {
+ "catalogue": "Catálogo",
+ "downloads": "Downloads",
+ "settings": "Ajustes",
+ "my_library": "Biblioteca",
+ "downloading_metadata": "{{title}} (Baixando metadados…)",
+ "paused": "{{title}} (Pausado)",
+ "downloading": "{{title}} ({{percentage}} - Baixando…)",
+ "filter": "Buscar",
+ "home": "Início",
+ "queued": "{{title}} (Na fila)",
+ "game_has_no_executable": "Jogo não possui executável selecionado",
+ "sign_in": "Login"
+ },
+ "header": {
+ "search": "Buscar jogos",
+ "catalogue": "Catálogo",
+ "downloads": "Downloads",
+ "search_results": "Resultados da busca",
+ "settings": "Ajustes",
+ "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 downloads em andamento",
+ "downloading_metadata": "Baixando metadados de {{title}}…",
+ "downloading": "Baixando {{title}}… ({{percentage}} concluído) - Conclusão {{eta}} - {{speed}}",
+ "calculating_eta": "Baixando {{title}}… ({{percentage}} concluído) - Calculando tempo restante…",
+ "checking_files": "Verificando arquivos de {{title}}…"
+ },
+ "game_details": {
+ "open_download_options": "Ver opções de download",
+ "download_options_zero": "Sem opções de download",
+ "download_options_one": "{{count}} opção de download",
+ "download_options_other": "{{count}} opções de download",
+ "updated_at": "Atualizado {{updated_at}}",
+ "resume": "Retomar",
+ "pause": "Pausar",
+ "cancel": "Cancelar",
+ "remove": "Remover",
+ "space_left_on_disk": "{{space}} livres em disco",
+ "eta": "Conclusão {{eta}}",
+ "calculating_eta": "Calculando tempo restante…",
+ "downloading_metadata": "Baixando metadados…",
+ "filter": "Filtrar repacks",
+ "requirements": "Requisitos de 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": "Nenhum download 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": "Você ainda não jogou {{title}}",
+ "close": "Fechar",
+ "deleting": "Excluindo instalador…",
+ "playing_now": "Jogando agora",
+ "change": "Explorar",
+ "repacks_modal_description": "Escolha o repack do jogo que deseja baixar",
+ "select_folder_hint": "Para trocar o diretório padrão, acesse a <0>Tela de Ajustes0>",
+ "download_now": "Iniciar download",
+ "no_shop_details": "Não foi possível obter os detalhes da loja.",
+ "download_options": "Opções de download",
+ "download_path": "Diretório de download",
+ "previous_screenshot": "Captura de tela anterior",
+ "next_screenshot": "Próxima captura de tela",
+ "screenshot": "Captura de tela {{number}}",
+ "open_screenshot": "Ver captura de tela {{number}}",
+ "download_settings": "Ajustes do download",
+ "downloader": "Downloader",
+ "select_executable": "Explorar",
+ "no_executable_selected": "Nenhum executável selecionado",
+ "open_folder": "Abrir pasta",
+ "open_download_location": "Ver arquivos baixados",
+ "create_shortcut": "Criar atalho na área de trabalho",
+ "remove_files": "Remover arquivos",
+ "options": "Gerenciar",
+ "remove_from_library_description": "Isso irá remover {{game}} da sua biblioteca",
+ "remove_from_library_title": "Tem certeza?",
+ "executable_section_title": "Executável",
+ "executable_section_description": "O caminho do arquivo que será executado ao clicar em \"Jogar\"",
+ "downloads_secion_title": "Downloads",
+ "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 arquivos que foram baixados pelo Hydra",
+ "download_in_progress": "Download em andamento",
+ "download_paused": "Download pausado",
+ "last_downloaded_option": "Última opção baixada",
+ "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 seu código de ativação",
+ "message": "Se você não sabe onde conseguir o código, talvez você não devesse estar aqui.",
+ "activate": "Ativar",
+ "loading": "Carregando…"
+ },
+ "downloads": {
+ "resume": "Retomar",
+ "pause": "Pausar",
+ "eta": "Conclusão {{eta}}",
+ "paused": "Pausado",
+ "verifying": "Verificando…",
+ "completed": "Concluído",
+ "removed": "Cancelado",
+ "cancel": "Cancelar",
+ "filter": "Filtrar jogos baixados",
+ "remove": "Remover",
+ "downloading_metadata": "Baixando metadados…",
+ "delete": "Remover instalador",
+ "delete_modal_description": "Isso removerá todos os arquivos de instalação do seu computador",
+ "delete_modal_title": "Tem certeza?",
+ "deleting": "Excluindo instalador…",
+ "install": "Instalar",
+ "download_in_progress": "Baixando agora",
+ "queued_downloads": "Na fila",
+ "downloads_completed": "Completo",
+ "queued": "Na fila",
+ "no_downloads_title": "Nada por aqui…",
+ "no_downloads_description": "Você ainda não baixou nada pelo Hydra, mas nunca é tarde para começar.",
+ "checking_files": "Verificando arquivos…"
+ },
+ "settings": {
+ "downloads_path": "Diretório dos downloads",
+ "change": "Explorar...",
+ "notifications": "Notificações",
+ "enable_download_notifications": "Quando um download for concluído",
+ "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 junto com o sistema",
+ "general": "Geral",
+ "behavior": "Comportamento",
+ "download_sources": "Fontes de download",
+ "language": "Idioma",
+ "real_debrid_api_token": "Token de API",
+ "enable_real_debrid": "Habilitar Real-Debrid",
+ "real_debrid_api_token_hint": "Você pode obter seu token de API <0>aqui0>",
+ "real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite baixar arquivos 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, assine a Real-Debrid",
+ "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 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",
+ "download_count_zero": "Sem downloads na lista",
+ "download_count_one": "{{countFormatted}} download na lista",
+ "download_count_other": "{{countFormatted}} downloads na lista",
+ "download_options_zero": "Sem downloads disponíveis",
+ "download_options_one": "{{countFormatted}} download disponível",
+ "download_options_other": "{{countFormatted}} downloads disponíveis",
+ "download_source_url": "URL da fonte",
+ "add_download_source_description": "Insira a 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 a url de um JSON válido",
+ "found_download_option_zero": "Nenhuma opção de download encontrada",
+ "found_download_option_one": "{{countFormatted}} opção de download encontrada",
+ "found_download_option_other": "{{countFormatted}} opções de download encontradas",
+ "import": "Importar"
+ },
+ "notifications": {
+ "download_complete": "Download concluído",
+ "game_ready_to_install": "{{title}} está pronto para ser instalado",
+ "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 downloads 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 no seu 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 senha"
+ },
+ "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 você não jogou nada recentemente. Que tal começar agora?",
+ "display_name": "Nome de exibição",
+ "saving": "Salvando…",
+ "save": "Salvar",
+ "edit_profile": "Editar perfil",
+ "saved_successfully": "Salvo com sucesso",
+ "try_again": "Por favor, tente novamente",
+ "cancel": "Cancelar",
+ "successfully_signed_out": "Deslogado com sucesso",
+ "sign_out": "Sair da conta",
+ "sign_out_modal_title": "Tem certeza?",
+ "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",
+ "friend_code": "Código de amigo",
+ "see_profile": "Ver perfil",
+ "friend_request_sent": "Pedido de amizade enviado",
+ "friends": "Amigos",
+ "add": "Adicionar",
+ "sending": "Enviando",
+ "friends_list": "Lista de amigos",
+ "user_not_found": "Usuário 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": "Usuário bloqueado com sucesso",
+ "user_block_modal_text": "Bloquear {{displayName}}",
+ "settings": "Configurações",
+ "privacy": "Privacidade",
+ "private": "Privado",
+ "friends_only": "Apenas amigos",
+ "public": "Público",
+ "blocked_users": "Usuários bloqueados",
+ "unblock": "Desbloquear",
+ "no_friends_added": "Você ainda não possui amigos adicionados",
+ "pending": "Pendentes",
+ "no_pending_invites": "Você não possui convites de amizade pendentes",
+ "no_blocked_users": "Você não tem nenhum usuário bloqueado",
+ "friend_code_copied": "Código de amigo copiado"
+ }
+}
diff --git a/src/locales/pt-PT/translation.json b/src/locales/pt-PT/translation.json
new file mode 100644
index 00000000..6fb8b453
--- /dev/null
+++ b/src/locales/pt-PT/translation.json
@@ -0,0 +1,280 @@
+{
+ "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ções0>",
+ "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>aqui0>",
+ "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"
+ }
+}
From dab12cb316d161409d23701caf52740dd45dc479 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino
Date: Tue, 27 Aug 2024 14:40:01 -0300
Subject: [PATCH 097/108] feat: add a new library for support differents type
of "PT" translation
---
package.json | 1 +
yarn.lock | 5 +++++
2 files changed, 6 insertions(+)
diff --git a/package.json b/package.json
index aa77084e..9514f471 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
"typeorm:migration-create": "yarn typeorm migration:create"
},
"dependencies": {
+ "@cospired/i18n-iso-languages": "^4.2.0",
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
"@fontsource/noto-sans": "^5.0.22",
diff --git a/yarn.lock b/yarn.lock
index 74b9a8a1..acb17953 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -579,6 +579,11 @@
"@types/conventional-commits-parser" "^5.0.0"
chalk "^5.3.0"
+"@cospired/i18n-iso-languages@^4.2.0":
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/@cospired/i18n-iso-languages/-/i18n-iso-languages-4.2.0.tgz#094418a72f250fd612b3fc856b12f674a10864eb"
+ integrity sha512-vy8cq1176MTxVwB1X9niQjcIYOH29F8Huxtx8hLmT5Uz3l1ztGDGri8KN/4zE7LV2mCT7JrcAoNV/I9yb+lNUw==
+
"@develar/schema-utils@~2.6.5":
version "2.6.5"
resolved "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz"
From 15c21164e40bbc3fc9599cd558ec86a5970b9766 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino
Date: Tue, 27 Aug 2024 14:40:16 -0300
Subject: [PATCH 098/108] chore: updating translation location
---
src/locales/index.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/locales/index.ts b/src/locales/index.ts
index 7cef7a5e..f698976f 100644
--- a/src/locales/index.ts
+++ b/src/locales/index.ts
@@ -1,6 +1,6 @@
export { default as en } from "./en/translation.json";
-export { default as pt } from "./pt/translation.json";
-export { default as br } from "./br/translation.json";
+export { default as ptPT } from "./pt-PT/translation.json";
+export { default as ptBR } from "./pt-BR/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";
From f4cf33d369cb16a867d6cc5a2d47998459b756df Mon Sep 17 00:00:00 2001
From: Thales Euflauzino
Date: Tue, 27 Aug 2024 14:41:24 -0300
Subject: [PATCH 099/108] feat: add a new library in setting general
---
.../src/pages/settings/settings-general.tsx | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/src/renderer/src/pages/settings/settings-general.tsx b/src/renderer/src/pages/settings/settings-general.tsx
index e09ebb11..5fee22aa 100644
--- a/src/renderer/src/pages/settings/settings-general.tsx
+++ b/src/renderer/src/pages/settings/settings-general.tsx
@@ -1,5 +1,6 @@
import { useContext, useEffect, useState } from "react";
-import ISO6391 from "iso-639-1";
+import languages from "@cospired/i18n-iso-languages";
+import languagesEn from "@cospired/i18n-iso-languages/langs/en.json";
import {
TextField,
@@ -21,6 +22,14 @@ interface LanguageOption {
nativeName: string;
}
+languages.registerLocale(languagesEn);
+
+const customLanguageNames = {
+ ptBR: "Português (Brasil)",
+ ptPT: "Português (Portugal)",
+ // Adicione outros idiomas personalizados aqui, se necessário
+};
+
export function SettingsGeneral() {
const { t } = useTranslation("settings");
@@ -52,7 +61,9 @@ export function SettingsGeneral() {
orderBy(
Object.keys(languageResources).map((language) => {
return {
- nativeName: ISO6391.getNativeName(language),
+ nativeName:
+ customLanguageNames[language] ||
+ languages.getName(language, "en"),
option: language,
};
}),
@@ -93,7 +104,7 @@ export function SettingsGeneral() {
function updateFormWithUserPreferences() {
if (userPreferences) {
- const parsedLanguage = userPreferences.language.split("-")[0];
+ const parsedLanguage = userPreferences.language;
setForm((prev) => ({
...prev,
From 70f120a62c6f66d6fe08c956627b2a9cf1960634 Mon Sep 17 00:00:00 2001
From: Thales Euflauzino
Date: Wed, 28 Aug 2024 04:42:10 -0300
Subject: [PATCH 100/108] delete: removing unused translation json
---
src/locales/pt/translation.json | 280 ------------------
.../src/pages/settings/settings-general.tsx | 1 -
2 files changed, 281 deletions(-)
delete mode 100644 src/locales/pt/translation.json
diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json
deleted file mode 100644
index 6fb8b453..00000000
--- a/src/locales/pt/translation.json
+++ /dev/null
@@ -1,280 +0,0 @@
-{
- "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ções0>",
- "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>aqui0>",
- "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"
- }
-}
diff --git a/src/renderer/src/pages/settings/settings-general.tsx b/src/renderer/src/pages/settings/settings-general.tsx
index 5fee22aa..3e69e5bf 100644
--- a/src/renderer/src/pages/settings/settings-general.tsx
+++ b/src/renderer/src/pages/settings/settings-general.tsx
@@ -27,7 +27,6 @@ languages.registerLocale(languagesEn);
const customLanguageNames = {
ptBR: "Português (Brasil)",
ptPT: "Português (Portugal)",
- // Adicione outros idiomas personalizados aqui, se necessário
};
export function SettingsGeneral() {
From 40d2b36a20461ef7dc1a9a391ba35dab6074312d Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Fri, 30 Aug 2024 10:06:10 -0300
Subject: [PATCH 101/108] feat: remove unnecessary react fragment
---
.../user-confirm-undo-friendship-modal.tsx | 34 +++++++++----------
1 file changed, 16 insertions(+), 18 deletions(-)
diff --git a/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx b/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx
index cf2a0415..ad83bc00 100644
--- a/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx
+++ b/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx
@@ -18,25 +18,23 @@ export const UserConfirmUndoFriendshipModal = ({
const { t } = useTranslation("user_profile");
return (
- <>
-
-
-
{t("undo_friendship_modal_text", { displayName })}
-
-
- {t("undo_friendship")}
-
+
+
+
{t("undo_friendship_modal_text", { displayName })}
+
+
+ {t("undo_friendship")}
+
-
- {t("cancel")}
-
-
+
+ {t("cancel")}
+
-
- >
+
+
);
};
From d4d94dfc4cf3ff401a846596d40a843fe32ff8e5 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Fri, 30 Aug 2024 10:13:18 -0300
Subject: [PATCH 102/108] feat: use function expression for component
---
.../src/pages/user/user-confirm-undo-friendship-modal.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx b/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx
index ad83bc00..cfdb5d06 100644
--- a/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx
+++ b/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx
@@ -9,12 +9,12 @@ export interface UserConfirmUndoFriendshipModalProps {
onClose: () => void;
}
-export const UserConfirmUndoFriendshipModal = ({
+export function UserConfirmUndoFriendshipModal({
visible,
displayName,
onConfirm,
onClose,
-}: UserConfirmUndoFriendshipModalProps) => {
+}: UserConfirmUndoFriendshipModalProps) {
const { t } = useTranslation("user_profile");
return (
@@ -37,4 +37,4 @@ export const UserConfirmUndoFriendshipModal = ({
);
-};
+}
From 567d9f540d39cdb00b19a2c786f79d6c70e1925b Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Fri, 30 Aug 2024 14:06:53 -0300
Subject: [PATCH 103/108] feat: refactor
---
.eslintignore | 1 +
package.json | 4 +-
src/main/hydra.dev.db | Bin 0 -> 16384 bytes
src/main/knex-client.ts | 17 +-
src/main/knexfile.ts | 10 +
src/main/migrations/001_Hydra_2_0_3.ts | 169 -----------------
src/main/migrations/002_RepackUris.ts | 56 ------
.../migrations/20240830143811_Hydra_2_0_3.ts | 171 ++++++++++++++++++
.../migrations/20240830143906_RepackUris.ts | 58 ++++++
src/main/migrations/index.ts | 2 -
src/main/migrations/migration.stub | 11 ++
11 files changed, 262 insertions(+), 237 deletions(-)
create mode 100644 src/main/hydra.dev.db
create mode 100644 src/main/knexfile.ts
delete mode 100644 src/main/migrations/001_Hydra_2_0_3.ts
delete mode 100644 src/main/migrations/002_RepackUris.ts
create mode 100644 src/main/migrations/20240830143811_Hydra_2_0_3.ts
create mode 100644 src/main/migrations/20240830143906_RepackUris.ts
delete mode 100644 src/main/migrations/index.ts
create mode 100644 src/main/migrations/migration.stub
diff --git a/.eslintignore b/.eslintignore
index a6f34fea..a9960b13 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -2,3 +2,4 @@ node_modules
dist
out
.gitignore
+migration.stub
diff --git a/package.json b/package.json
index 113b2e20..ff05b2ed 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,8 @@
"build:win": "electron-vite build && electron-builder --win",
"build:mac": "electron-vite build && electron-builder --mac",
"build:linux": "electron-vite build && electron-builder --linux",
- "prepare": "husky"
+ "prepare": "husky",
+ "knex:migrate:make": "knex --knexfile src/main/knexfile.ts migrate:make --esm"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
@@ -106,6 +107,7 @@
"prettier": "^3.2.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "ts-node": "^10.9.2",
"typescript": "^5.3.3",
"vite": "^5.0.12",
"vite-plugin-svgr": "^4.2.0"
diff --git a/src/main/hydra.dev.db b/src/main/hydra.dev.db
new file mode 100644
index 0000000000000000000000000000000000000000..d8c65f28e86ecd378cedef374125790f7237c717
GIT binary patch
literal 16384
zcmeI&O;5rw7zgl{5z&L-jcenH1)_;&HxHuWVmSE1#nVb6^V{i3_Vmz}MzEJe$K0?>azZF2*NhQD9NUYwZ(6|)e$MG;;g_>R
z&fZ4Hc7s#CBb8?P)1J^E009U<00Izz00bZa0SG`~uLTyTPO0j7bkR4w8;PM3Ge4Gn
z&5cad#E+72n0Kvs``BuBTCCH&X}4IAwDISZ4
zz0+M>(Ng-z_w&58GwBp|o|#Q{mFIV`e@ikABw(*xhXbx_7oO+Z%)8u#gKgIK!uCV>
zMyC6F+=z7KrnR{(`q3Z&0SG_<0uX=z1Rwwb2tWV=5ctOeY5h+tAH*JL5P$##AOHaf
zKmY;|fB*y_009W>w?LIT-cjafgw-z@>-v95D$D&&5Ya;b0uX=z1Rwwb2tWV=5P$##
MAdoBIP=_3T0ok>*FaQ7m
literal 0
HcmV?d00001
diff --git a/src/main/knex-client.ts b/src/main/knex-client.ts
index ad6cf928..031760f6 100644
--- a/src/main/knex-client.ts
+++ b/src/main/knex-client.ts
@@ -1,19 +1,18 @@
import knex, { Knex } from "knex";
import { databasePath } from "./constants";
-import * as migrations from "./migrations";
+import { Hydra2_0_3 } from "./migrations/20240830143811_Hydra_2_0_3";
+import { RepackUris } from "./migrations/20240830143906_RepackUris";
-type Migration = Knex.Migration & { name: string };
+export type HydraMigration = Knex.Migration & { name: string };
-class MigrationSource implements Knex.MigrationSource {
- getMigrations(): Promise {
- return Promise.resolve(
- Object.values(migrations).sort((a, b) => a.name.localeCompare(b.name))
- );
+class MigrationSource implements Knex.MigrationSource {
+ getMigrations(): Promise {
+ return Promise.resolve([Hydra2_0_3, RepackUris]);
}
- getMigrationName(migration: Migration): string {
+ getMigrationName(migration: HydraMigration): string {
return migration.name;
}
- getMigration(migration: Migration): Promise {
+ getMigration(migration: HydraMigration): Promise {
return Promise.resolve(migration);
}
}
diff --git a/src/main/knexfile.ts b/src/main/knexfile.ts
new file mode 100644
index 00000000..df7972a9
--- /dev/null
+++ b/src/main/knexfile.ts
@@ -0,0 +1,10 @@
+const config = {
+ development: {
+ migrations: {
+ extension: "ts",
+ stub: "migrations/migration.stub",
+ },
+ },
+};
+
+export default config;
diff --git a/src/main/migrations/001_Hydra_2_0_3.ts b/src/main/migrations/001_Hydra_2_0_3.ts
deleted file mode 100644
index 36a41739..00000000
--- a/src/main/migrations/001_Hydra_2_0_3.ts
+++ /dev/null
@@ -1,169 +0,0 @@
-import { Knex } from "knex";
-
-export const name = "001_Hydra_2_0_3";
-
-export const up = async (knex: Knex) => {
- const timestamp = new Date().getTime();
-
- await knex.schema.hasTable("migrations").then(async (exists) => {
- if (exists) {
- await knex.schema.dropTable("migrations");
- }
- });
-
- await knex.schema.hasTable("download_source").then(async (exists) => {
- if (!exists) {
- await knex.schema.createTable("download_source", (table) => {
- table.increments("id").primary();
- table
- .text("url")
- .unique({ indexName: "download_source_url_unique_" + timestamp });
- table.text("name").notNullable();
- table.text("etag");
- table.integer("downloadCount").notNullable().defaultTo(0);
- table.text("status").notNullable().defaultTo(0);
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- });
- }
- });
-
- await knex.schema.hasTable("repack").then(async (exists) => {
- if (!exists) {
- await knex.schema.createTable("repack", (table) => {
- table.increments("id").primary();
- table
- .text("title")
- .notNullable()
- .unique({ indexName: "repack_title_unique_" + timestamp });
- table
- .text("magnet")
- .notNullable()
- .unique({ indexName: "repack_magnet_unique_" + timestamp });
- table.integer("page");
- table.text("repacker").notNullable();
- table.text("fileSize").notNullable();
- table.datetime("uploadDate").notNullable();
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- table
- .integer("downloadSourceId")
- .references("download_source.id")
- .onDelete("CASCADE");
- });
- }
- });
-
- await knex.schema.hasTable("game").then(async (exists) => {
- if (!exists) {
- await knex.schema.createTable("game", (table) => {
- table.increments("id").primary();
- table
- .text("objectID")
- .notNullable()
- .unique({ indexName: "game_objectID_unique_" + timestamp });
- table
- .text("remoteId")
- .unique({ indexName: "game_remoteId_unique_" + timestamp });
- table.text("title").notNullable();
- table.text("iconUrl");
- table.text("folderName");
- table.text("downloadPath");
- table.text("executablePath");
- table.integer("playTimeInMilliseconds").notNullable().defaultTo(0);
- table.text("shop").notNullable();
- table.text("status");
- table.integer("downloader").notNullable().defaultTo(1);
- table.float("progress").notNullable().defaultTo(0);
- table.integer("bytesDownloaded").notNullable().defaultTo(0);
- table.datetime("lastTimePlayed");
- table.float("fileSize").notNullable().defaultTo(0);
- table.text("uri");
- table.boolean("isDeleted").notNullable().defaultTo(0);
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- table
- .integer("repackId")
- .references("repack.id")
- .unique("repack_repackId_unique_" + timestamp);
- });
- }
- });
-
- await knex.schema.hasTable("user_preferences").then(async (exists) => {
- if (!exists) {
- await knex.schema.createTable("user_preferences", (table) => {
- table.increments("id").primary();
- table.text("downloadsPath");
- table.text("language").notNullable().defaultTo("en");
- table.text("realDebridApiToken");
- table
- .boolean("downloadNotificationsEnabled")
- .notNullable()
- .defaultTo(0);
- table
- .boolean("repackUpdatesNotificationsEnabled")
- .notNullable()
- .defaultTo(0);
- table.boolean("preferQuitInsteadOfHiding").notNullable().defaultTo(0);
- table.boolean("runAtStartup").notNullable().defaultTo(0);
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- });
- }
- });
-
- await knex.schema.hasTable("game_shop_cache").then(async (exists) => {
- if (!exists) {
- await knex.schema.createTable("game_shop_cache", (table) => {
- table.text("objectID").primary().notNullable();
- table.text("shop").notNullable();
- table.text("serializedData");
- table.text("howLongToBeatSerializedData");
- table.text("language");
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- });
- }
- });
-
- await knex.schema.hasTable("download_queue").then(async (exists) => {
- if (!exists) {
- await knex.schema.createTable("download_queue", (table) => {
- table.increments("id").primary();
- table
- .integer("gameId")
- .references("game.id")
- .unique("download_queue_gameId_unique_" + timestamp);
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- });
- }
- });
-
- await knex.schema.hasTable("user_auth").then(async (exists) => {
- if (!exists) {
- await knex.schema.createTable("user_auth", (table) => {
- table.increments("id").primary();
- table.text("userId").notNullable().defaultTo("");
- table.text("displayName").notNullable().defaultTo("");
- table.text("profileImageUrl");
- table.text("accessToken").notNullable().defaultTo("");
- table.text("refreshToken").notNullable().defaultTo("");
- table.integer("tokenExpirationTimestamp").notNullable().defaultTo(0);
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- });
- }
- });
-};
-
-export const down = async (knex: Knex) => {
- await knex.schema.dropTableIfExists("game");
- await knex.schema.dropTableIfExists("repack");
- await knex.schema.dropTableIfExists("download_queue");
- await knex.schema.dropTableIfExists("user_auth");
- await knex.schema.dropTableIfExists("game_shop_cache");
- await knex.schema.dropTableIfExists("user_preferences");
- await knex.schema.dropTableIfExists("download_source");
-};
diff --git a/src/main/migrations/002_RepackUris.ts b/src/main/migrations/002_RepackUris.ts
deleted file mode 100644
index 50d38d0f..00000000
--- a/src/main/migrations/002_RepackUris.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Knex } from "knex";
-
-export const name = "002_RepackUris";
-
-export const up = async (knex: Knex) => {
- await knex.schema.createTable("temporary_repack", (table) => {
- const timestamp = new Date().getTime();
- table.increments("id").primary();
- table
- .text("title")
- .notNullable()
- .unique({ indexName: "repack_title_unique_" + timestamp });
- table
- .text("magnet")
- .notNullable()
- .unique({ indexName: "repack_magnet_unique_" + timestamp });
- table.text("repacker").notNullable();
- table.text("fileSize").notNullable();
- table.datetime("uploadDate").notNullable();
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- table
- .integer("downloadSourceId")
- .references("download_source.id")
- .onDelete("CASCADE");
- table.text("uris").notNullable().defaultTo("[]");
- });
- await knex.raw(
- `INSERT INTO "temporary_repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`
- );
- await knex.schema.dropTable("repack");
- await knex.schema.renameTable("temporary_repack", "repack");
-};
-
-export const down = async (knex: Knex) => {
- await knex.schema.renameTable("repack", "temporary_repack");
- await knex.schema.createTable("repack", (table) => {
- table.increments("id").primary();
- table.text("title").notNullable().unique();
- table.text("magnet").notNullable().unique();
- table.integer("page");
- table.text("repacker").notNullable();
- table.text("fileSize").notNullable();
- table.datetime("uploadDate").notNullable();
- table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
- table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
- table
- .integer("downloadSourceId")
- .references("download_source.id")
- .onDelete("CASCADE");
- });
- await knex.raw(
- `INSERT INTO "repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`
- );
- await knex.schema.dropTable("temporary_repack");
-};
diff --git a/src/main/migrations/20240830143811_Hydra_2_0_3.ts b/src/main/migrations/20240830143811_Hydra_2_0_3.ts
new file mode 100644
index 00000000..6013f714
--- /dev/null
+++ b/src/main/migrations/20240830143811_Hydra_2_0_3.ts
@@ -0,0 +1,171 @@
+import type { HydraMigration } from "@main/knex-client";
+import type { Knex } from "knex";
+
+export const Hydra2_0_3: HydraMigration = {
+ name: "Hydra_2_0_3",
+ up: async (knex: Knex) => {
+ const timestamp = new Date().getTime();
+
+ await knex.schema.hasTable("migrations").then(async (exists) => {
+ if (exists) {
+ await knex.schema.dropTable("migrations");
+ }
+ });
+
+ await knex.schema.hasTable("download_source").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("download_source", (table) => {
+ table.increments("id").primary();
+ table
+ .text("url")
+ .unique({ indexName: "download_source_url_unique_" + timestamp });
+ table.text("name").notNullable();
+ table.text("etag");
+ table.integer("downloadCount").notNullable().defaultTo(0);
+ table.text("status").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+ }
+ });
+
+ await knex.schema.hasTable("repack").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("repack", (table) => {
+ table.increments("id").primary();
+ table
+ .text("title")
+ .notNullable()
+ .unique({ indexName: "repack_title_unique_" + timestamp });
+ table
+ .text("magnet")
+ .notNullable()
+ .unique({ indexName: "repack_magnet_unique_" + timestamp });
+ table.integer("page");
+ table.text("repacker").notNullable();
+ table.text("fileSize").notNullable();
+ table.datetime("uploadDate").notNullable();
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table
+ .integer("downloadSourceId")
+ .references("download_source.id")
+ .onDelete("CASCADE");
+ });
+ }
+ });
+
+ await knex.schema.hasTable("game").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("game", (table) => {
+ table.increments("id").primary();
+ table
+ .text("objectID")
+ .notNullable()
+ .unique({ indexName: "game_objectID_unique_" + timestamp });
+ table
+ .text("remoteId")
+ .unique({ indexName: "game_remoteId_unique_" + timestamp });
+ table.text("title").notNullable();
+ table.text("iconUrl");
+ table.text("folderName");
+ table.text("downloadPath");
+ table.text("executablePath");
+ table.integer("playTimeInMilliseconds").notNullable().defaultTo(0);
+ table.text("shop").notNullable();
+ table.text("status");
+ table.integer("downloader").notNullable().defaultTo(1);
+ table.float("progress").notNullable().defaultTo(0);
+ table.integer("bytesDownloaded").notNullable().defaultTo(0);
+ table.datetime("lastTimePlayed");
+ table.float("fileSize").notNullable().defaultTo(0);
+ table.text("uri");
+ table.boolean("isDeleted").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table
+ .integer("repackId")
+ .references("repack.id")
+ .unique("repack_repackId_unique_" + timestamp);
+ });
+ }
+ });
+
+ await knex.schema.hasTable("user_preferences").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("user_preferences", (table) => {
+ table.increments("id").primary();
+ table.text("downloadsPath");
+ table.text("language").notNullable().defaultTo("en");
+ table.text("realDebridApiToken");
+ table
+ .boolean("downloadNotificationsEnabled")
+ .notNullable()
+ .defaultTo(0);
+ table
+ .boolean("repackUpdatesNotificationsEnabled")
+ .notNullable()
+ .defaultTo(0);
+ table.boolean("preferQuitInsteadOfHiding").notNullable().defaultTo(0);
+ table.boolean("runAtStartup").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+ }
+ });
+
+ await knex.schema.hasTable("game_shop_cache").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("game_shop_cache", (table) => {
+ table.text("objectID").primary().notNullable();
+ table.text("shop").notNullable();
+ table.text("serializedData");
+ table.text("howLongToBeatSerializedData");
+ table.text("language");
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+ }
+ });
+
+ await knex.schema.hasTable("download_queue").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("download_queue", (table) => {
+ table.increments("id").primary();
+ table
+ .integer("gameId")
+ .references("game.id")
+ .unique("download_queue_gameId_unique_" + timestamp);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+ }
+ });
+
+ await knex.schema.hasTable("user_auth").then(async (exists) => {
+ if (!exists) {
+ await knex.schema.createTable("user_auth", (table) => {
+ table.increments("id").primary();
+ table.text("userId").notNullable().defaultTo("");
+ table.text("displayName").notNullable().defaultTo("");
+ table.text("profileImageUrl");
+ table.text("accessToken").notNullable().defaultTo("");
+ table.text("refreshToken").notNullable().defaultTo("");
+ table.integer("tokenExpirationTimestamp").notNullable().defaultTo(0);
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ });
+ }
+ });
+ },
+
+ down: async (knex: Knex) => {
+ await knex.schema.dropTableIfExists("game");
+ await knex.schema.dropTableIfExists("repack");
+ await knex.schema.dropTableIfExists("download_queue");
+ await knex.schema.dropTableIfExists("user_auth");
+ await knex.schema.dropTableIfExists("game_shop_cache");
+ await knex.schema.dropTableIfExists("user_preferences");
+ await knex.schema.dropTableIfExists("download_source");
+ },
+};
diff --git a/src/main/migrations/20240830143906_RepackUris.ts b/src/main/migrations/20240830143906_RepackUris.ts
new file mode 100644
index 00000000..0785d50d
--- /dev/null
+++ b/src/main/migrations/20240830143906_RepackUris.ts
@@ -0,0 +1,58 @@
+import type { HydraMigration } from "@main/knex-client";
+import type { Knex } from "knex";
+
+export const RepackUris: HydraMigration = {
+ name: "RepackUris",
+ up: async (knex: Knex) => {
+ await knex.schema.createTable("temporary_repack", (table) => {
+ const timestamp = new Date().getTime();
+ table.increments("id").primary();
+ table
+ .text("title")
+ .notNullable()
+ .unique({ indexName: "repack_title_unique_" + timestamp });
+ table
+ .text("magnet")
+ .notNullable()
+ .unique({ indexName: "repack_magnet_unique_" + timestamp });
+ table.text("repacker").notNullable();
+ table.text("fileSize").notNullable();
+ table.datetime("uploadDate").notNullable();
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table
+ .integer("downloadSourceId")
+ .references("download_source.id")
+ .onDelete("CASCADE");
+ table.text("uris").notNullable().defaultTo("[]");
+ });
+ await knex.raw(
+ `INSERT INTO "temporary_repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`
+ );
+ await knex.schema.dropTable("repack");
+ await knex.schema.renameTable("temporary_repack", "repack");
+ },
+
+ down: async (knex: Knex) => {
+ await knex.schema.renameTable("repack", "temporary_repack");
+ await knex.schema.createTable("repack", (table) => {
+ table.increments("id").primary();
+ table.text("title").notNullable().unique();
+ table.text("magnet").notNullable().unique();
+ table.integer("page");
+ table.text("repacker").notNullable();
+ table.text("fileSize").notNullable();
+ table.datetime("uploadDate").notNullable();
+ table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
+ table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
+ table
+ .integer("downloadSourceId")
+ .references("download_source.id")
+ .onDelete("CASCADE");
+ });
+ await knex.raw(
+ `INSERT INTO "repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`
+ );
+ await knex.schema.dropTable("temporary_repack");
+ },
+};
diff --git a/src/main/migrations/index.ts b/src/main/migrations/index.ts
deleted file mode 100644
index 3859255a..00000000
--- a/src/main/migrations/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * as hydra from "./001_Hydra_2_0_3";
-export * as downloadRefactor from "./002_RepackUris";
diff --git a/src/main/migrations/migration.stub b/src/main/migrations/migration.stub
new file mode 100644
index 00000000..9cb0cbab
--- /dev/null
+++ b/src/main/migrations/migration.stub
@@ -0,0 +1,11 @@
+import type { HydraMigration } from "@main/knex-client";
+import type { Knex } from "knex";
+
+export const MigrationName: HydraMigration = {
+ name: "MigrationName",
+ up: async (knex: Knex) => {
+ await knex.schema.createTable("table_name", (table) => {});
+ },
+
+ down: async (knex: Knex) => {},
+};
From 158b878883beac038cd6b4b96feadf9ff6b7fd23 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Sat, 31 Aug 2024 14:17:03 -0300
Subject: [PATCH 104/108] feat: catch hltb error
---
src/main/services/how-long-to-beat.ts | 36 ++++++++++++++++-----------
1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/src/main/services/how-long-to-beat.ts b/src/main/services/how-long-to-beat.ts
index 67e96942..c7164d09 100644
--- a/src/main/services/how-long-to-beat.ts
+++ b/src/main/services/how-long-to-beat.ts
@@ -2,6 +2,7 @@ import axios from "axios";
import { requestWebPage } from "@main/helpers";
import { HowLongToBeatCategory } from "@types";
import { formatName } from "@shared";
+import { logger } from "./logger";
export interface HowLongToBeatResult {
game_id: number;
@@ -13,22 +14,27 @@ export interface HowLongToBeatSearchResponse {
}
export const searchHowLongToBeat = async (gameName: string) => {
- const response = await axios.post(
- "https://howlongtobeat.com/api/search",
- {
- searchType: "games",
- searchTerms: formatName(gameName).split(" "),
- searchPage: 1,
- size: 100,
- },
- {
- headers: {
- "User-Agent":
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
- Referer: "https://howlongtobeat.com/",
+ const response = await axios
+ .post(
+ "https://howlongtobeat.com/api/search",
+ {
+ searchType: "games",
+ searchTerms: formatName(gameName).split(" "),
+ searchPage: 1,
+ size: 100,
},
- }
- );
+ {
+ headers: {
+ "User-Agent":
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
+ Referer: "https://howlongtobeat.com/",
+ },
+ }
+ )
+ .catch((error) => {
+ logger.error("Error searching HowLongToBeat:", error?.response?.status);
+ return { data: { data: [] } };
+ });
return response.data as HowLongToBeatSearchResponse;
};
From 88737cf80d2efb58f8fd6143213bab7cf9517dec Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Mon, 2 Sep 2024 12:54:55 -0300
Subject: [PATCH 105/108] feat: refactor knex promises
---
src/main/index.ts | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/src/main/index.ts b/src/main/index.ts
index 5293cda8..93481f61 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -53,6 +53,18 @@ if (process.defaultApp) {
app.setAsDefaultProtocolClient(PROTOCOL);
}
+const runMigrations = async () => {
+ await knexClient.migrate.list(migrationConfig).then((result) => {
+ logger.log(
+ "Migrations to run:",
+ result[1].map((migration) => migration.name)
+ );
+ });
+
+ await knexClient.migrate.latest(migrationConfig);
+ await knexClient.destroy();
+};
+
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
@@ -64,15 +76,7 @@ app.whenReady().then(async () => {
return net.fetch(url.pathToFileURL(decodeURI(filePath)).toString());
});
- await knexClient.migrate.list(migrationConfig).then((result) => {
- logger.log(
- "Migrations to run:",
- result[1].map((migration) => migration.name)
- );
- });
-
- await knexClient.migrate
- .latest(migrationConfig)
+ await runMigrations()
.then(() => {
logger.log("Migrations executed successfully");
})
@@ -80,8 +84,6 @@ app.whenReady().then(async () => {
logger.log("Migrations failed to run:", err);
});
- await knexClient.destroy();
-
await dataSource.initialize();
await import("./main");
From 6464cea57634f17fc9a8868188beccae7a3c4d25 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Wed, 4 Sep 2024 16:40:31 -0300
Subject: [PATCH 106/108] Delete src/locales/br/translation.json
---
src/locales/br/translation.json | 278 --------------------------------
1 file changed, 278 deletions(-)
delete mode 100644 src/locales/br/translation.json
diff --git a/src/locales/br/translation.json b/src/locales/br/translation.json
deleted file mode 100644
index bd355473..00000000
--- a/src/locales/br/translation.json
+++ /dev/null
@@ -1,278 +0,0 @@
-{
- "app": {
- "successfully_signed_in": "Autenticado com sucesso"
- },
- "home": {
- "featured": "Destaques",
- "trending": "Populares",
- "surprise_me": "Surpreenda-me",
- "no_results": "Nenhum resultado encontrado"
- },
- "sidebar": {
- "catalogue": "Catálogo",
- "downloads": "Downloads",
- "settings": "Configurações",
- "my_library": "Biblioteca",
- "downloading_metadata": "{{title}} (Baixando metadados…)",
- "paused": "{{title}} (Pausado)",
- "downloading": "{{title}} ({{percentage}} - Baixando…)",
- "filter": "Filtrar bibilioteca",
- "home": "Início",
- "queued": "{{title}} (Na fila)",
- "game_has_no_executable": "Jogo não possui executável selecionado",
- "sign_in": "Login"
- },
- "header": {
- "search": "Buscar jogos",
- "catalogue": "Catálogo",
- "downloads": "Downloads",
- "search_results": "Resultados da busca",
- "settings": "Configuraçõ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 downloads em andamento",
- "downloading_metadata": "Baixando metadados de {{title}}…",
- "downloading": "Baixando {{title}}… ({{percentage}} concluído) - Conclusão {{eta}} - {{speed}}",
- "calculating_eta": "Baixando {{title}}… ({{percentage}} concluído) - Calculando tempo restante…",
- "checking_files": "Verificando arquivos de {{title}}…"
- },
- "game_details": {
- "open_download_options": "Ver opções de download",
- "download_options_zero": "Sem opções de download",
- "download_options_one": "{{count}} opção de download",
- "download_options_other": "{{count}} opções de download",
- "updated_at": "Atualizado {{updated_at}}",
- "resume": "Retomar",
- "pause": "Pausar",
- "cancel": "Cancelar",
- "remove": "Remover",
- "space_left_on_disk": "{{space}} livres em disco",
- "eta": "Conclusão {{eta}}",
- "calculating_eta": "Calculando tempo restante…",
- "downloading_metadata": "Baixando metadados…",
- "filter": "Filtrar repacks",
- "requirements": "Requisitos de 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": "Nenhum download 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": "Você ainda não jogou {{title}}",
- "close": "Fechar",
- "deleting": "Excluindo instalador…",
- "playing_now": "Jogando agora",
- "change": "Explorar",
- "repacks_modal_description": "Escolha o repack do jogo que deseja baixar",
- "select_folder_hint": "Para trocar o diretório padrão, acesse a <0>Tela de Ajustes0>",
- "download_now": "Iniciar download",
- "no_shop_details": "Não foi possível obter os detalhes da loja.",
- "download_options": "Opções de download",
- "download_path": "Diretório de download",
- "previous_screenshot": "Captura de tela anterior",
- "next_screenshot": "Próxima captura de tela",
- "screenshot": "Captura de tela {{number}}",
- "open_screenshot": "Ver captura de tela {{number}}",
- "download_settings": "Ajustes do download",
- "downloader": "Downloader",
- "select_executable": "Explorar",
- "no_executable_selected": "Nenhum executável selecionado",
- "open_folder": "Abrir pasta",
- "open_download_location": "Ver arquivos baixados",
- "create_shortcut": "Criar atalho na área de trabalho",
- "remove_files": "Remover arquivos",
- "options": "Gerenciar",
- "remove_from_library_description": "Isso irá remover {{game}} da sua biblioteca",
- "remove_from_library_title": "Tem certeza?",
- "executable_section_title": "Executável",
- "executable_section_description": "O caminho do arquivo que será executado ao clicar em \"Jogar\"",
- "downloads_secion_title": "Downloads",
- "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 arquivos que foram baixados pelo Hydra",
- "download_in_progress": "Download em andamento",
- "download_paused": "Download pausado",
- "last_downloaded_option": "Última opção baixada",
- "create_shortcut_success": "Atalho criado com sucesso",
- "create_shortcut_error": "Erro ao criar atalho"
- },
- "activation": {
- "title": "Ativar Hydra",
- "installation_id": "ID da instalação:",
- "enter_activation_code": "Insira seu código de ativação",
- "message": "Se você não sabe onde conseguir o código, talvez você não devesse estar aqui.",
- "activate": "Ativar",
- "loading": "Carregando…"
- },
- "downloads": {
- "resume": "Retomar",
- "pause": "Pausar",
- "eta": "Conclusão {{eta}}",
- "paused": "Pausado",
- "verifying": "Verificando…",
- "completed": "Concluído",
- "removed": "Cancelado",
- "cancel": "Cancelar",
- "filter": "Filtrar jogos baixados",
- "remove": "Remover",
- "downloading_metadata": "Baixando metadados…",
- "delete": "Remover instalador",
- "delete_modal_description": "Isso removerá todos os arquivos de instalação do seu computador",
- "delete_modal_title": "Tem certeza?",
- "deleting": "Excluindo instalador…",
- "install": "Instalar",
- "download_in_progress": "Baixando agora",
- "queued_downloads": "Na fila",
- "downloads_completed": "Completo",
- "queued": "Na fila",
- "no_downloads_title": "Nada por aqui…",
- "no_downloads_description": "Você ainda não baixou nada pelo Hydra, mas nunca é tarde para começar.",
- "checking_files": "Verificando arquivos…"
- },
- "settings": {
- "downloads_path": "Diretório dos downloads",
- "change": "Alterar",
- "notifications": "Notificações",
- "enable_download_notifications": "Quando um download for concluído",
- "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 junto com o sistema",
- "general": "Geral",
- "behavior": "Comportamento",
- "download_sources": "Fontes de download",
- "language": "Idioma",
- "real_debrid_api_token": "Token de API",
- "enable_real_debrid": "Habilitar Real-Debrid",
- "real_debrid_api_token_hint": "Você pode obter seu token de API <0>aqui0>",
- "real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite baixar arquivos 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, assine a Real-Debrid",
- "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 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",
- "download_count_zero": "Sem downloads na lista",
- "download_count_one": "{{countFormatted}} download na lista",
- "download_count_other": "{{countFormatted}} downloads na lista",
- "download_options_zero": "Sem downloads disponíveis",
- "download_options_one": "{{countFormatted}} download disponível",
- "download_options_other": "{{countFormatted}} downloads disponíveis",
- "download_source_url": "URL da fonte",
- "add_download_source_description": "Insira a 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 a url de um JSON válido",
- "found_download_option_zero": "Nenhuma opção de download encontrada",
- "found_download_option_one": "{{countFormatted}} opção de download encontrada",
- "found_download_option_other": "{{countFormatted}} opções de download encontradas",
- "import": "Importar"
- },
- "notifications": {
- "download_complete": "Download concluído",
- "game_ready_to_install": "{{title}} está pronto para ser instalado",
- "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 downloads 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 no seu 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 senha"
- },
- "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 você não jogou nada recentemente. Que tal começar agora?",
- "display_name": "Nome de exibição",
- "saving": "Salvando…",
- "save": "Salvar",
- "edit_profile": "Editar perfil",
- "saved_successfully": "Salvo com sucesso",
- "try_again": "Por favor, tente novamente",
- "cancel": "Cancelar",
- "successfully_signed_out": "Deslogado com sucesso",
- "sign_out": "Sair da conta",
- "sign_out_modal_title": "Tem certeza?",
- "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",
- "friend_code": "Código de amigo",
- "see_profile": "Ver perfil",
- "friend_request_sent": "Pedido de amizade enviado",
- "friends": "Amigos",
- "add": "Adicionar",
- "sending": "Enviando",
- "friends_list": "Lista de amigos",
- "user_not_found": "Usuário 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": "Usuário bloqueado com sucesso",
- "user_block_modal_text": "Bloquear {{displayName}}",
- "settings": "Configurações",
- "privacy": "Privacidade",
- "private": "Privado",
- "friends_only": "Apenas amigos",
- "public": "Público",
- "blocked_users": "Usuários bloqueados",
- "unblock": "Desbloquear",
- "no_friends_added": "Você ainda não possui amigos adicionados",
- "pending": "Pendentes",
- "no_pending_invites": "Você não possui convites de amizade pendentes",
- "no_blocked_users": "Você não tem nenhum usuário bloqueado",
- "friend_code_copied": "Código de amigo copiado"
- }
-}
From 40f4bbc1246e855f42c0123796d743c605e6481a Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Wed, 4 Sep 2024 23:40:05 -0300
Subject: [PATCH 107/108] feat: refactor languages
---
package.json | 4 +-
src/locales/ar/translation.json | 1 +
src/locales/be/translation.json | 1 +
src/locales/ca/translation.json | 1 +
src/locales/da/translation.json | 1 +
src/locales/en/translation.json | 1 +
src/locales/es/translation.json | 1 +
src/locales/fa/translation.json | 1 +
src/locales/fr/translation.json | 1 +
src/locales/hu/translation.json | 1 +
src/locales/id/translation.json | 1 +
src/locales/index.ts | 69 ++++++++----
src/locales/it/translation.json | 1 +
src/locales/kk/translation.json | 1 +
src/locales/ko/translation.json | 1 +
src/locales/nl/translation.json | 1 +
src/locales/pl/translation.json | 1 +
src/locales/pt-BR/translation.json | 1 +
src/locales/pt-PT/translation.json | 1 +
src/locales/ro/translation.json | 1 +
src/locales/ru/translation.json | 1 +
src/locales/tr/translation.json | 1 +
src/locales/uk/translation.json | 1 +
src/locales/zh/translation.json | 1 +
src/main/index.ts | 2 +-
src/renderer/src/main.tsx | 2 +-
.../src/pages/settings/settings-general.tsx | 24 +---
yarn.lock | 106 ++++++++++++++++--
28 files changed, 172 insertions(+), 57 deletions(-)
diff --git a/package.json b/package.json
index e3666029..1727e383 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,6 @@
"knex:migrate:make": "knex --knexfile src/main/knexfile.ts migrate:make --esm"
},
"dependencies": {
- "@cospired/i18n-iso-languages": "^4.2.0",
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
"@fontsource/noto-sans": "^5.0.22",
@@ -43,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",
@@ -59,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",
diff --git a/src/locales/ar/translation.json b/src/locales/ar/translation.json
index e95db2f1..26f0654d 100644
--- a/src/locales/ar/translation.json
+++ b/src/locales/ar/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "اَلْعَرَبِيَّةُ",
"home": {
"featured": "مميّز",
"trending": "شائع",
diff --git a/src/locales/be/translation.json b/src/locales/be/translation.json
index 9e945f8d..b89946e8 100644
--- a/src/locales/be/translation.json
+++ b/src/locales/be/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "беларуская мова",
"home": {
"featured": "Рэкамэндаванае",
"trending": "Актуальнае",
diff --git a/src/locales/ca/translation.json b/src/locales/ca/translation.json
index 9124af79..393ea587 100644
--- a/src/locales/ca/translation.json
+++ b/src/locales/ca/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Català",
"app": {
"successfully_signed_in": "Has entrat correctament"
},
diff --git a/src/locales/da/translation.json b/src/locales/da/translation.json
index d5cac8db..20b2df34 100644
--- a/src/locales/da/translation.json
+++ b/src/locales/da/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Dansk",
"home": {
"featured": "Anbefalet",
"trending": "Trender",
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index 08c9fda2..ae9c2712 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "English",
"app": {
"successfully_signed_in": "Successfully signed in"
},
diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json
index 9cc7af9d..f8fa12e4 100644
--- a/src/locales/es/translation.json
+++ b/src/locales/es/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Español",
"app": {
"successfully_signed_in": "Sesión iniciada correctamente"
},
diff --git a/src/locales/fa/translation.json b/src/locales/fa/translation.json
index 8629332f..2b8cd3fb 100644
--- a/src/locales/fa/translation.json
+++ b/src/locales/fa/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "فارسی",
"home": {
"featured": "پیشنهادی",
"trending": "پرطرفدار",
diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json
index c732b22c..f635f1de 100644
--- a/src/locales/fr/translation.json
+++ b/src/locales/fr/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Français",
"home": {
"featured": "En vedette",
"trending": "Tendance",
diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json
index 748ffe28..f68d71bd 100644
--- a/src/locales/hu/translation.json
+++ b/src/locales/hu/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Magyar",
"home": {
"featured": "Featured",
"trending": "Népszerű",
diff --git a/src/locales/id/translation.json b/src/locales/id/translation.json
index 198aa568..3d0f1edf 100644
--- a/src/locales/id/translation.json
+++ b/src/locales/id/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Bahasa Indonesia",
"app": {
"successfully_signed_in": "Berhasil masuk"
},
diff --git a/src/locales/index.ts b/src/locales/index.ts
index f698976f..ea0783c2 100644
--- a/src/locales/index.ts
+++ b/src/locales/index.ts
@@ -1,22 +1,47 @@
-export { default as en } from "./en/translation.json";
-export { default as ptPT } from "./pt-PT/translation.json";
-export { default as ptBR } from "./pt-BR/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 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,
+ es,
+ nl,
+ fr,
+ hu,
+ it,
+ pl,
+ ru,
+ tr,
+ be,
+ uk,
+ zh,
+ id,
+ ko,
+ da,
+ ar,
+ fa,
+ ro,
+ ca,
+ kk,
+};
diff --git a/src/locales/it/translation.json b/src/locales/it/translation.json
index 55f21310..1d5145f9 100644
--- a/src/locales/it/translation.json
+++ b/src/locales/it/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Italiano",
"home": {
"featured": "In primo piano",
"trending": "Di tendenza",
diff --git a/src/locales/kk/translation.json b/src/locales/kk/translation.json
index d565e3b7..15683eb2 100644
--- a/src/locales/kk/translation.json
+++ b/src/locales/kk/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "қазақ тілі",
"app": {
"successfully_signed_in": "Сәтті кіру"
},
diff --git a/src/locales/ko/translation.json b/src/locales/ko/translation.json
index 3d45bb88..933c7dde 100644
--- a/src/locales/ko/translation.json
+++ b/src/locales/ko/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "한국어",
"home": {
"featured": "추천",
"trending": "인기",
diff --git a/src/locales/nl/translation.json b/src/locales/nl/translation.json
index 59cf13e6..6f02c9a3 100644
--- a/src/locales/nl/translation.json
+++ b/src/locales/nl/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Nederlands",
"home": {
"featured": "Uitgelicht",
"trending": "Trending",
diff --git a/src/locales/pl/translation.json b/src/locales/pl/translation.json
index a8e9bdc7..5eb2c242 100644
--- a/src/locales/pl/translation.json
+++ b/src/locales/pl/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Polski",
"home": {
"featured": "Wyróżnione",
"trending": "Trendujące",
diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json
index 02823aa1..1adac376 100644
--- a/src/locales/pt-BR/translation.json
+++ b/src/locales/pt-BR/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Português (Brasil)",
"app": {
"successfully_signed_in": "Autenticado com sucesso"
},
diff --git a/src/locales/pt-PT/translation.json b/src/locales/pt-PT/translation.json
index 6fb8b453..67f99921 100644
--- a/src/locales/pt-PT/translation.json
+++ b/src/locales/pt-PT/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Português (Portugal)",
"app": {
"successfully_signed_in": "Sessão iniciada com sucesso"
},
diff --git a/src/locales/ro/translation.json b/src/locales/ro/translation.json
index 2aed7a7f..9fab3119 100644
--- a/src/locales/ro/translation.json
+++ b/src/locales/ro/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Română",
"home": {
"featured": "Recomandate",
"trending": "Populare",
diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json
index 89d16b28..be3a000e 100644
--- a/src/locales/ru/translation.json
+++ b/src/locales/ru/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Русский",
"app": {
"successfully_signed_in": "Успешный вход"
},
diff --git a/src/locales/tr/translation.json b/src/locales/tr/translation.json
index 150eda84..2da9c977 100644
--- a/src/locales/tr/translation.json
+++ b/src/locales/tr/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Türkçe",
"home": {
"featured": "Öne çıkan",
"trending": "Popüler",
diff --git a/src/locales/uk/translation.json b/src/locales/uk/translation.json
index 48dec3e4..bb840bc2 100644
--- a/src/locales/uk/translation.json
+++ b/src/locales/uk/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "Українська",
"app": {
"successfully_signed_in": "Успішний вхід в систему"
},
diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json
index e0284b8d..0c793172 100644
--- a/src/locales/zh/translation.json
+++ b/src/locales/zh/translation.json
@@ -1,4 +1,5 @@
{
+ "language_name": "中文",
"app": {
"successfully_signed_in": "已成功登录"
},
diff --git a/src/main/index.ts b/src/main/index.ts
index 24c367fd..3c5cc254 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -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";
diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx
index b88348f0..2377bf7c 100644
--- a/src/renderer/src/main.tsx
+++ b/src/renderer/src/main.tsx
@@ -26,7 +26,7 @@ import {
import { store } from "./store";
-import * as resources from "@locales";
+import resources from "@locales";
import { User } from "./pages/user/user";
Sentry.init({});
diff --git a/src/renderer/src/pages/settings/settings-general.tsx b/src/renderer/src/pages/settings/settings-general.tsx
index 3e69e5bf..10c17eca 100644
--- a/src/renderer/src/pages/settings/settings-general.tsx
+++ b/src/renderer/src/pages/settings/settings-general.tsx
@@ -1,7 +1,4 @@
import { useContext, useEffect, useState } from "react";
-import languages from "@cospired/i18n-iso-languages";
-import languagesEn from "@cospired/i18n-iso-languages/langs/en.json";
-
import {
TextField,
Button,
@@ -9,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";
@@ -22,13 +17,6 @@ interface LanguageOption {
nativeName: string;
}
-languages.registerLocale(languagesEn);
-
-const customLanguageNames = {
- ptBR: "Português (Brasil)",
- ptPT: "Português (Portugal)",
-};
-
export function SettingsGeneral() {
const { t } = useTranslation("settings");
@@ -58,11 +46,9 @@ export function SettingsGeneral() {
setLanguageOptions(
orderBy(
- Object.keys(languageResources).map((language) => {
+ Object.entries(languageResources).map(([language, value]) => {
return {
- nativeName:
- customLanguageNames[language] ||
- languages.getName(language, "en"),
+ nativeName: value.language_name,
option: language,
};
}),
@@ -103,8 +89,6 @@ export function SettingsGeneral() {
function updateFormWithUserPreferences() {
if (userPreferences) {
- const parsedLanguage = userPreferences.language;
-
setForm((prev) => ({
...prev,
downloadsPath: userPreferences.downloadsPath ?? defaultDownloadsPath,
@@ -112,7 +96,7 @@ export function SettingsGeneral() {
userPreferences.downloadNotificationsEnabled,
repackUpdatesNotificationsEnabled:
userPreferences.repackUpdatesNotificationsEnabled,
- language: parsedLanguage,
+ language: userPreferences.language,
}));
}
}
diff --git a/yarn.lock b/yarn.lock
index 6ee3491a..91d9977f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -579,10 +579,12 @@
"@types/conventional-commits-parser" "^5.0.0"
chalk "^5.3.0"
-"@cospired/i18n-iso-languages@^4.2.0":
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/@cospired/i18n-iso-languages/-/i18n-iso-languages-4.2.0.tgz#094418a72f250fd612b3fc856b12f674a10864eb"
- integrity sha512-vy8cq1176MTxVwB1X9niQjcIYOH29F8Huxtx8hLmT5Uz3l1ztGDGri8KN/4zE7LV2mCT7JrcAoNV/I9yb+lNUw==
+"@cspotcode/source-map-support@^0.8.0":
+ version "0.8.1"
+ resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
+ integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
+ dependencies:
+ "@jridgewell/trace-mapping" "0.3.9"
"@develar/schema-utils@~2.6.5":
version "2.6.5"
@@ -1008,7 +1010,7 @@
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.24"
-"@jridgewell/resolve-uri@^3.1.0":
+"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0":
version "3.1.2"
resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz"
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
@@ -1023,6 +1025,14 @@
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
+"@jridgewell/trace-mapping@0.3.9":
+ version "0.3.9"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
+ integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
version "0.3.25"
resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz"
@@ -1881,6 +1891,26 @@
resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz"
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
+"@tsconfig/node10@^1.0.7":
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2"
+ integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==
+
+"@tsconfig/node12@^1.0.7":
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
+ integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
+
+"@tsconfig/node14@^1.0.0":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
+ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
+
+"@tsconfig/node16@^1.0.2":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
+ integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
+
"@types/accepts@*":
version "1.3.7"
resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.7.tgz#3b98b1889d2b2386604c2bbbe62e4fb51e95b265"
@@ -2526,6 +2556,18 @@ acorn-jsx@^5.3.2:
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+acorn-walk@^8.1.1:
+ version "8.3.3"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e"
+ integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==
+ dependencies:
+ acorn "^8.11.0"
+
+acorn@^8.11.0, acorn@^8.4.1:
+ version "8.12.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
+ integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
+
acorn@^8.11.3, acorn@^8.9.0:
version "8.11.3"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz"
@@ -2665,6 +2707,11 @@ applescript@^1.0.0:
resolved "https://registry.npmjs.org/applescript/-/applescript-1.0.0.tgz"
integrity sha512-yvtNHdWvtbYEiIazXAdp/NY+BBb65/DAseqlNiJQjOx9DynuzOYDbVLBJvuc0ve0VL9x6B3OHF6eH52y9hCBtQ==
+arg@^4.1.0:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
+ integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
+
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
@@ -3378,6 +3425,11 @@ create-desktop-shortcuts@^1.11.0:
dependencies:
which "2.0.2"
+create-require@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
+ integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
+
cross-fetch-ponyfill@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/cross-fetch-ponyfill/-/cross-fetch-ponyfill-1.0.3.tgz"
@@ -3581,6 +3633,11 @@ detect-node@^2.0.4:
resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz"
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
+diff@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
+ integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
+
dir-compare@^3.0.0:
version "3.3.0"
resolved "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz"
@@ -5185,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"
@@ -5622,6 +5674,11 @@ magnet-uri@^7.0.5:
bep53-range "^2.0.0"
uint8-util "^2.1.9"
+make-error@^1.1.1:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
+ integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
+
matcher@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz"
@@ -7278,6 +7335,25 @@ ts-api-utils@^1.0.1:
resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz"
integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
+ts-node@^10.9.2:
+ version "10.9.2"
+ resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f"
+ integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==
+ dependencies:
+ "@cspotcode/source-map-support" "^0.8.0"
+ "@tsconfig/node10" "^1.0.7"
+ "@tsconfig/node12" "^1.0.7"
+ "@tsconfig/node14" "^1.0.0"
+ "@tsconfig/node16" "^1.0.2"
+ acorn "^8.4.1"
+ acorn-walk "^8.1.1"
+ arg "^4.1.0"
+ create-require "^1.1.0"
+ diff "^4.0.1"
+ make-error "^1.1.1"
+ v8-compile-cache-lib "^3.0.1"
+ yn "3.1.1"
+
tslib@^2.0.3, tslib@^2.5.0, tslib@^2.6.2:
version "2.6.2"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
@@ -7489,6 +7565,11 @@ uuid@^9.0.0:
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz"
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
+v8-compile-cache-lib@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
+ integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
+
verror@^1.10.0:
version "1.10.1"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb"
@@ -7782,6 +7863,11 @@ yauzl@^2.10.0:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"
+yn@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
+ integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
+
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
From dc4aa0f60847662e6853e6c15c7eaab57db9dc59 Mon Sep 17 00:00:00 2001
From: Zamitto <167933696+zamitto@users.noreply.github.com>
Date: Thu, 5 Sep 2024 21:06:49 -0300
Subject: [PATCH 108/108] feat: remove dev db
---
src/main/hydra.dev.db | Bin 16384 -> 0 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 src/main/hydra.dev.db
diff --git a/src/main/hydra.dev.db b/src/main/hydra.dev.db
deleted file mode 100644
index d8c65f28e86ecd378cedef374125790f7237c717..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 16384
zcmeI&O;5rw7zgl{5z&L-jcenH1)_;&HxHuWVmSE1#nVb6^V{i3_Vmz}MzEJe$K0?>azZF2*NhQD9NUYwZ(6|)e$MG;;g_>R
z&fZ4Hc7s#CBb8?P)1J^E009U<00Izz00bZa0SG`~uLTyTPO0j7bkR4w8;PM3Ge4Gn
z&5cad#E+72n0Kvs``BuBTCCH&X}4IAwDISZ4
zz0+M>(Ng-z_w&58GwBp|o|#Q{mFIV`e@ikABw(*xhXbx_7oO+Z%)8u#gKgIK!uCV>
zMyC6F+=z7KrnR{(`q3Z&0SG_<0uX=z1Rwwb2tWV=5ctOeY5h+tAH*JL5P$##AOHaf
zKmY;|fB*y_009W>w?LIT-cjafgw-z@>-v95D$D&&5Ya;b0uX=z1Rwwb2tWV=5P$##
MAdoBIP=_3T0ok>*FaQ7m