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({

- 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 d2d8f5d9..f9d351af 100644 --- a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx @@ -44,8 +44,10 @@ export function RepacksModal({ }, [repacks]); const getInfoHash = useCallback(async () => { - const torrent = await parseTorrent(game?.uri ?? ""); - if (torrent.infoHash) setInfoHash(torrent.infoHash); + if (game?.uri?.startsWith("magnet:")) { + const torrent = await parseTorrent(game?.uri ?? ""); + if (torrent.infoHash) setInfoHash(torrent.infoHash); + } }, [game]); useEffect(() => { diff --git a/src/shared/index.ts b/src/shared/index.ts index 409bdbc9..ba68876f 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -1,6 +1,8 @@ export enum Downloader { RealDebrid, Torrent, + Gofile, + PixelDrain, } export enum DownloadSourceStatus { @@ -63,3 +65,18 @@ export const formatName = pipe( removeDuplicateSpaces, (str) => str.trim() ); + +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:")) + return [Downloader.Torrent, Downloader.RealDebrid]; + + return []; +}; From b5b7fe31ae64a28e483ccbe7b1f0cc3470711b50 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 15 Jul 2024 17:48:10 -0300 Subject: [PATCH 002/108] use webContents.downloadURL to download http --- src/main/services/download/http-download.ts | 107 +++++++++++--------- 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index d147e208..ec16f5cd 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -1,59 +1,42 @@ -import type { ChildProcess } from "node:child_process"; -import { logger } from "../logger"; -import { sleep } from "@main/helpers"; -import { startAria2 } from "../aria2c"; -import Aria2 from "aria2"; +import { DownloadItem } from "electron"; +import { WindowManager } from "../window-manager"; export class HTTPDownload { - private static connected = false; - private static aria2c: ChildProcess | null = null; + private static id = 0; - private static aria2 = new Aria2({}); + private static downloads: Record = {}; - private static async connect() { - this.aria2c = startAria2(); - - let retries = 0; - - while (retries < 4 && !this.connected) { - try { - await this.aria2.open(); - logger.log("Connected to aria2"); - - this.connected = true; - } catch (err) { - await sleep(100); - logger.log("Failed to connect to aria2, retrying..."); - retries++; - } - } - } - - public static getStatus(gid: string) { - if (this.connected) { - return this.aria2.call("tellStatus", gid); + public static getStatus(gid: string): { + completedLength: number; + totalLength: number; + downloadSpeed: number; + } | null { + const downloadItem = this.downloads[gid]; + if (downloadItem) { + return { + completedLength: downloadItem.getReceivedBytes(), + totalLength: downloadItem.getTotalBytes(), + downloadSpeed: 0, + }; } return null; } - public static disconnect() { - if (this.aria2c) { - this.aria2c.kill(); - this.connected = false; - } - } - static async cancelDownload(gid: string) { - await this.aria2.call("forceRemove", gid); + const downloadItem: DownloadItem = this.downloads[gid]; + downloadItem?.cancel(); + this.downloads; } static async pauseDownload(gid: string) { - await this.aria2.call("forcePause", gid); + const downloadItem = this.downloads[gid]; + downloadItem?.pause(); } static async resumeDownload(gid: string) { - await this.aria2.call("unpause", gid); + const downloadItem = this.downloads[gid]; + downloadItem?.resume(); } static async startDownload( @@ -61,14 +44,44 @@ export class HTTPDownload { downloadUrl: string, header: string[] = [] ) { - console.log(header); - if (!this.connected) await this.connect(); + return new Promise((resolve) => { + WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, { + headers: { Cookie: header[0].split(": ")[1] }, + }); - const options = { - dir: downloadPath, - header, - }; + WindowManager.mainWindow?.webContents.session.on( + "will-download", + (_event, item, _webContents) => { + const gid = ++this.id; - return this.aria2.call("addUri", [downloadUrl], options); + this.downloads[gid.toString()] = item; + + // Set the save path, making Electron not to prompt a save dialog. + item.setSavePath(downloadPath); + + item.on("updated", (_event, state) => { + if (state === "interrupted") { + console.log("Download is interrupted but can be resumed"); + } else if (state === "progressing") { + if (item.isPaused()) { + console.log("Download is paused"); + } else { + console.log(`Received bytes: ${item.getReceivedBytes()}`); + } + } + }); + + item.once("done", (_event, state) => { + if (state === "completed") { + console.log("Download successfully"); + } else { + console.log(`Download failed: ${state}`); + } + }); + + resolve(gid.toString()); + } + ); + }); } } From 781e0f4102a26e04a3fcbadbd1fe3f1058f9c849 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 15 Jul 2024 22:29:34 -0300 Subject: [PATCH 003/108] feat: update headers --- .../services/download/download-manager.ts | 6 ++--- .../download/generic-http-downloader.ts | 2 +- src/main/services/download/http-download.ts | 26 +++---------------- 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index d6542396..b9c9d5a6 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -104,9 +104,9 @@ export class DownloadManager { console.log(downloadLink, token, "<<<"); - GenericHTTPDownloader.startDownload(game, downloadLink, [ - `Cookie: accountToken=${token}`, - ]); + GenericHTTPDownloader.startDownload(game, downloadLink, { + Cookie: `accountToken=${token}`, + }); } else if (game.downloader === Downloader.PixelDrain) { const id = game!.uri!.split("/").pop(); diff --git a/src/main/services/download/generic-http-downloader.ts b/src/main/services/download/generic-http-downloader.ts index 688769a4..a482c075 100644 --- a/src/main/services/download/generic-http-downloader.ts +++ b/src/main/services/download/generic-http-downloader.ts @@ -69,7 +69,7 @@ export class GenericHTTPDownloader { static async startDownload( game: Game, downloadUrl: string, - headers: string[] = [] + headers?: Record ) { this.downloadingGame = game; diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index ec16f5cd..ec9ba635 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -26,7 +26,7 @@ export class HTTPDownload { static async cancelDownload(gid: string) { const downloadItem: DownloadItem = this.downloads[gid]; downloadItem?.cancel(); - this.downloads; + delete this.downloads[gid]; } static async pauseDownload(gid: string) { @@ -42,11 +42,11 @@ export class HTTPDownload { static async startDownload( downloadPath: string, downloadUrl: string, - header: string[] = [] + headers?: Record ) { return new Promise((resolve) => { WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, { - headers: { Cookie: header[0].split(": ")[1] }, + headers, }); WindowManager.mainWindow?.webContents.session.on( @@ -59,26 +59,6 @@ export class HTTPDownload { // Set the save path, making Electron not to prompt a save dialog. item.setSavePath(downloadPath); - item.on("updated", (_event, state) => { - if (state === "interrupted") { - console.log("Download is interrupted but can be resumed"); - } else if (state === "progressing") { - if (item.isPaused()) { - console.log("Download is paused"); - } else { - console.log(`Received bytes: ${item.getReceivedBytes()}`); - } - } - }); - - item.once("done", (_event, state) => { - if (state === "completed") { - console.log("Download successfully"); - } else { - console.log(`Download failed: ${state}`); - } - }); - resolve(gid.toString()); } ); From 8c0c3e617baeda5434e9dfd2fb50a130288b48d2 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:05:06 -0300 Subject: [PATCH 004/108] feat: add logs for python process --- src/main/services/download/python-instance.ts | 32 +++++++++++++++---- src/main/services/download/torrent-client.ts | 25 ++++++++++++--- src/main/services/logger.ts | 5 +++ torrent-client/main.py | 17 ++++++++++ 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/src/main/services/download/python-instance.ts b/src/main/services/download/python-instance.ts index c534e41d..345a3588 100644 --- a/src/main/services/download/python-instance.ts +++ b/src/main/services/download/python-instance.ts @@ -19,6 +19,7 @@ import { LibtorrentPayload, ProcessPayload, } from "./types"; +import { pythonInstanceLogger as logger } from "../logger"; export class PythonInstance { private static pythonProcess: cp.ChildProcess | null = null; @@ -32,11 +33,13 @@ export class PythonInstance { }); public static spawn(args?: StartDownloadPayload) { + logger.log("spawning python process with args:", args); this.pythonProcess = startRPCClient(args); } public static kill() { if (this.pythonProcess) { + logger.log("killing python process"); this.pythonProcess.kill(); this.pythonProcess = null; this.downloadingGameId = -1; @@ -45,7 +48,10 @@ export class PythonInstance { public static killTorrent() { if (this.pythonProcess) { - this.rpc.post("/action", { action: "kill-torrent" }); + logger.log("killing torrent in python process"); + this.rpc + .post("/action", { action: "kill-torrent" }) + .catch((err) => logger.error(err)); this.downloadingGameId = -1; } } @@ -138,12 +144,14 @@ export class PythonInstance { save_path: game.downloadPath!, }); } else { - await this.rpc.post("/action", { - action: "start", - game_id: game.id, - magnet: game.uri, - save_path: game.downloadPath, - } as StartDownloadPayload); + await this.rpc + .post("/action", { + action: "start", + game_id: game.id, + magnet: game.uri, + save_path: game.downloadPath, + } as StartDownloadPayload) + .catch(this.handleRpcError); } this.downloadingGameId = game.id; @@ -159,4 +167,14 @@ export class PythonInstance { this.downloadingGameId = -1; } + + private static async handleRpcError(_error: unknown) { + await this.rpc.get("/healthcheck").catch(() => { + logger.error( + "RPC healthcheck failed. Killing process and starting again" + ); + this.kill(); + this.spawn(); + }); + } } diff --git a/src/main/services/download/torrent-client.ts b/src/main/services/download/torrent-client.ts index 93d20b7f..2a16acad 100644 --- a/src/main/services/download/torrent-client.ts +++ b/src/main/services/download/torrent-client.ts @@ -4,6 +4,8 @@ import crypto from "node:crypto"; import fs from "node:fs"; import { app, dialog } from "electron"; import type { StartDownloadPayload } from "./types"; +import { Readable } from "node:stream"; +import { pythonInstanceLogger as logger } from "../logger"; const binaryNameByPlatform: Partial> = { darwin: "hydra-download-manager", @@ -15,6 +17,13 @@ export const BITTORRENT_PORT = "5881"; export const RPC_PORT = "8084"; export const RPC_PASSWORD = crypto.randomBytes(32).toString("hex"); +const logStderr = (readable: Readable | null) => { + if (!readable) return; + + readable.setEncoding("utf-8"); + readable.on("data", logger.log); +}; + export const startTorrentClient = (args?: StartDownloadPayload) => { const commonArgs = [ BITTORRENT_PORT, @@ -40,10 +49,14 @@ export const startTorrentClient = (args?: StartDownloadPayload) => { app.quit(); } - return cp.spawn(binaryPath, commonArgs, { - stdio: "inherit", + const childProcess = cp.spawn(binaryPath, commonArgs, { windowsHide: true, + stdio: ["inherit", "inherit"], }); + + logStderr(childProcess.stderr); + + return childProcess; } else { const scriptPath = path.join( __dirname, @@ -53,8 +66,12 @@ export const startTorrentClient = (args?: StartDownloadPayload) => { "main.py" ); - return cp.spawn("python3", [scriptPath, ...commonArgs], { - stdio: "inherit", + const childProcess = cp.spawn("python3", [scriptPath, ...commonArgs], { + stdio: ["inherit", "inherit"], }); + + logStderr(childProcess.stderr); + + return childProcess; } }; diff --git a/src/main/services/logger.ts b/src/main/services/logger.ts index 8da27a9e..1eb7060b 100644 --- a/src/main/services/logger.ts +++ b/src/main/services/logger.ts @@ -6,6 +6,10 @@ log.transports.file.resolvePathFn = ( _: log.PathVariables, message?: log.LogMessage | undefined ) => { + if (message?.scope === "python-instance") { + return path.join(logsPath, "pythoninstance.txt"); + } + if (message?.level === "error") { return path.join(logsPath, "error.txt"); } @@ -23,4 +27,5 @@ log.errorHandler.startCatching({ log.initialize(); +export const pythonInstanceLogger = log.scope("python-instance"); export const logger = log.scope("main"); diff --git a/torrent-client/main.py b/torrent-client/main.py index 004cd108..9a0bf5b0 100644 --- a/torrent-client/main.py +++ b/torrent-client/main.py @@ -20,6 +20,23 @@ if start_download_payload: class Handler(BaseHTTPRequestHandler): rpc_password_header = 'x-hydra-rpc-password' + skip_log_routes = [ + "process-list", + "status" + ] + + def log_error(self, format, *args): + sys.stderr.write("%s - - [%s] %s\n" % + (self.address_string(), + self.log_date_time_string(), + format%args)) + + def log_message(self, format, *args): + for route in self.skip_log_routes: + if route in args[0]: return + + super().log_message(format, *args) + def do_GET(self): if self.path == "/status": if self.headers.get(self.rpc_password_header) != rpc_password: From 3d8c63bc40150cba41678094437be5d951f7af2e Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:05:31 -0300 Subject: [PATCH 005/108] feat: truncate game title on tray --- src/main/services/download/python-instance.ts | 4 +--- src/main/services/window-manager.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/services/download/python-instance.ts b/src/main/services/download/python-instance.ts index 345a3588..37ec17db 100644 --- a/src/main/services/download/python-instance.ts +++ b/src/main/services/download/python-instance.ts @@ -49,9 +49,7 @@ export class PythonInstance { public static killTorrent() { if (this.pythonProcess) { logger.log("killing torrent in python process"); - this.rpc - .post("/action", { action: "kill-torrent" }) - .catch((err) => logger.error(err)); + this.rpc.post("/action", { action: "kill-torrent" }); this.downloadingGameId = -1; } } diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index 201b13ad..025e7219 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -158,7 +158,7 @@ export class WindowManager { const recentlyPlayedGames: Array = games.map(({ title, executablePath }) => ({ - label: title, + label: title.length > 15 ? `${title.slice(0, 15)}…` : title, type: "normal", click: async () => { if (!executablePath) return; From 8a1931f75ce068ff1bec78b968b27699d6149bad Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 20 Jul 2024 16:52:27 -0300 Subject: [PATCH 006/108] feat: use new electron version to get download speed --- package.json | 2 +- src/main/services/download/generic-http-downloader.ts | 8 ++++---- src/main/services/download/http-download.ts | 2 +- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index c99a9bac..d7996e6a 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "@types/user-agents": "^1.0.4", "@vanilla-extract/vite-plugin": "^4.0.7", "@vitejs/plugin-react": "^4.2.1", - "electron": "^30.0.9", + "electron": "^30.3.0", "electron-builder": "^24.9.1", "electron-vite": "^2.0.0", "eslint": "^8.56.0", diff --git a/src/main/services/download/generic-http-downloader.ts b/src/main/services/download/generic-http-downloader.ts index a482c075..c078be80 100644 --- a/src/main/services/download/generic-http-downloader.ts +++ b/src/main/services/download/generic-http-downloader.ts @@ -30,11 +30,11 @@ export class GenericHTTPDownloader { const result = { numPeers: 0, numSeeds: 0, - downloadSpeed: Number(status.downloadSpeed), + downloadSpeed: status.downloadSpeed, timeRemaining: calculateETA( - Number(status.totalLength), - Number(status.completedLength), - Number(status.downloadSpeed) + status.totalLength, + status.completedLength, + status.downloadSpeed ), isDownloadingMetadata: false, isCheckingFiles: false, diff --git a/src/main/services/download/http-download.ts b/src/main/services/download/http-download.ts index ec9ba635..d9c36916 100644 --- a/src/main/services/download/http-download.ts +++ b/src/main/services/download/http-download.ts @@ -16,7 +16,7 @@ export class HTTPDownload { return { completedLength: downloadItem.getReceivedBytes(), totalLength: downloadItem.getTotalBytes(), - downloadSpeed: 0, + downloadSpeed: downloadItem.getCurrentBytesPerSecond(), }; } diff --git a/yarn.lock b/yarn.lock index 00172038..9c829f62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3739,10 +3739,10 @@ electron-vite@^2.0.0: magic-string "^0.30.5" picocolors "^1.0.0" -electron@^30.0.9: - version "30.0.9" - resolved "https://registry.npmjs.org/electron/-/electron-30.0.9.tgz" - integrity sha512-ArxgdGHVu3o5uaP+Tqj8cJDvU03R6vrGrOqiMs7JXLnvQHMqXJIIxmFKQAIdJW8VoT3ac3hD21tA7cPO10RLow== +electron@^30.1.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/electron/-/electron-30.3.0.tgz#9d7af06c11242d5c6ca5b9920c9dc49feab90299" + integrity sha512-/rWPcpCL4sYCUm1bY8if1dO8nyFTwXlPUP0dpL3ir5iLK/9NshN6lIJ8xceEY8CEYVLMIYRkxXb44Q9cdrjtOQ== dependencies: "@electron/get" "^2.0.0" "@types/node" "^20.9.0" From 909e288bec20b5362e999821c5d82a10c2b86a1a Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sun, 21 Jul 2024 15:45:13 -0300 Subject: [PATCH 007/108] feat: background on profile page for other users profile --- src/renderer/src/helpers.ts | 12 +++++++++ src/renderer/src/hooks/use-user-details.ts | 13 +++------- src/renderer/src/pages/user/user-content.tsx | 26 +++++++++++++++----- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/renderer/src/helpers.ts b/src/renderer/src/helpers.ts index d37612d4..182aef25 100644 --- a/src/renderer/src/helpers.ts +++ b/src/renderer/src/helpers.ts @@ -1,6 +1,7 @@ import type { GameShop } from "@types"; import Color from "color"; +import { average } from "color.js"; export const steamUrlBuilder = { library: (objectID: string) => @@ -45,3 +46,14 @@ export const buildGameDetailsPath = ( export const darkenColor = (color: string, amount: number, alpha: number = 1) => new Color(color).darken(amount).alpha(alpha).toString(); + +export const profileBackgroundFromProfileImage = async ( + profileImageUrl: string +) => { + const output = await average(profileImageUrl, { + amount: 1, + format: "hex", + }); + + return `linear-gradient(135deg, ${darkenColor(output as string, 0.6)}, ${darkenColor(output as string, 0.8, 0.7)})`; +}; diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index a0da950a..80ef8b4c 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -1,6 +1,4 @@ import { useCallback } from "react"; -import { average } from "color.js"; - import { useAppDispatch, useAppSelector } from "./redux"; import { setProfileBackground, @@ -9,7 +7,7 @@ import { setFriendsModalVisible, setFriendsModalHidden, } from "@renderer/features"; -import { darkenColor } from "@renderer/helpers"; +import { profileBackgroundFromProfileImage } from "@renderer/helpers"; import { FriendRequestAction, UserDetails } from "@types"; import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal"; @@ -42,12 +40,9 @@ export function useUserDetails() { dispatch(setUserDetails(userDetails)); if (userDetails.profileImageUrl) { - const output = await average(userDetails.profileImageUrl, { - amount: 1, - format: "hex", - }); - - const profileBackground = `linear-gradient(135deg, ${darkenColor(output as string, 0.6)}, ${darkenColor(output as string, 0.8, 0.7)})`; + const profileBackground = await profileBackgroundFromProfileImage( + userDetails.profileImageUrl + ); dispatch(setProfileBackground(profileBackground)); window.localStorage.setItem( diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index e70335d8..cf332405 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -12,7 +12,11 @@ import { useUserDetails, } from "@renderer/hooks"; import { useNavigate } from "react-router-dom"; -import { buildGameDetailsPath, steamUrlBuilder } from "@renderer/helpers"; +import { + buildGameDetailsPath, + profileBackgroundFromProfileImage, + steamUrlBuilder, +} from "@renderer/helpers"; import { PersonIcon, PlusIcon, TelescopeIcon } from "@primer/octicons-react"; import { Button, Link } from "@renderer/components"; import { UserEditProfileModal } from "./user-edit-modal"; @@ -41,6 +45,8 @@ export function UserContent({ } = useUserDetails(); const { showSuccessToast } = useToast(); + const [profileContentBoxBackground, setProfileContentBoxBackground] = + useState(); const [showEditProfileModal, setShowEditProfileModal] = useState(false); const [showSignOutModal, setShowSignOutModal] = useState(false); @@ -96,11 +102,19 @@ export function UserContent({ if (isMe) updateFriendRequests(); }, [isMe]); - const profileContentBoxBackground = useMemo(() => { - if (profileBackground) return profileBackground; - /* TODO: Render background colors for other users */ - return undefined; - }, [profileBackground]); + useEffect(() => { + if (isMe && profileBackground) { + setProfileContentBoxBackground(profileBackground); + } + + if (userProfile.profileImageUrl) { + profileBackgroundFromProfileImage(userProfile.profileImageUrl).then( + (profileBackground) => { + setProfileContentBoxBackground(profileBackground); + } + ); + } + }, [profileBackground, isMe]); return ( <> From d350aa950dc4bb52000be5980bd0e737127ee72c Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sun, 21 Jul 2024 16:35:02 -0300 Subject: [PATCH 008/108] feat: get friends event --- src/main/events/user/get-user-friends.ts | 26 ++++++++++++++++++++ src/main/events/user/get-user.ts | 8 ++++-- src/main/services/hydra-api.ts | 6 ++--- src/renderer/src/pages/user/user-content.tsx | 6 ++--- src/types/index.ts | 7 +++++- 5 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 src/main/events/user/get-user-friends.ts diff --git a/src/main/events/user/get-user-friends.ts b/src/main/events/user/get-user-friends.ts new file mode 100644 index 00000000..a82673eb --- /dev/null +++ b/src/main/events/user/get-user-friends.ts @@ -0,0 +1,26 @@ +import { registerEvent } from "../register-event"; +import { HydraApi } from "@main/services"; +import { UserFriends } from "@types"; + +export const getUserFriends = async ( + userId: string, + take: number, + skip: number +): Promise => { + return HydraApi.get(`/user/${userId}/friends`, { take, skip }).catch( + (_err) => { + return { totalFriends: 0, friends: [] } as UserFriends; + } + ); +}; + +const getUserFriendsEvent = async ( + _event: Electron.IpcMainInvokeEvent, + userId: string, + take: number, + skip: number +) => { + return getUserFriends(userId, take, skip); +}; + +registerEvent("getUserFriends", getUserFriendsEvent); diff --git a/src/main/events/user/get-user.ts b/src/main/events/user/get-user.ts index 7b4c0aa8..98b98a77 100644 --- a/src/main/events/user/get-user.ts +++ b/src/main/events/user/get-user.ts @@ -4,13 +4,17 @@ import { steamGamesWorker } from "@main/workers"; import { UserProfile } from "@types"; import { convertSteamGameToCatalogueEntry } from "../helpers/search-games"; import { getSteamAppAsset } from "@main/helpers"; +import { getUserFriends } from "./get-user-friends"; const getUser = async ( _event: Electron.IpcMainInvokeEvent, userId: string ): Promise => { try { - const profile = await HydraApi.get(`/user/${userId}`); + const [profile, friends] = await Promise.all([ + HydraApi.get(`/user/${userId}`), + getUserFriends(userId, 12, 0), + ]); const recentGames = await Promise.all( profile.recentGames.map(async (game) => { @@ -46,7 +50,7 @@ const getUser = async ( }) ); - return { ...profile, libraryGames, recentGames }; + return { ...profile, libraryGames, recentGames, friends }; } catch (err) { return null; } diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index 97517b5b..6547ec15 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -72,7 +72,7 @@ export class HydraApi { this.instance.interceptors.request.use( (request) => { logger.log(" ---- REQUEST -----"); - logger.log(request.method, request.url, request.data); + logger.log(request.method, request.url, request.params, request.data); return request; }, (error) => { @@ -196,12 +196,12 @@ export class HydraApi { throw err; }; - static async get(url: string) { + static async get(url: string, params?: any) { if (!this.isLoggedIn()) throw new UserNotLoggedInError(); await this.revalidateAccessTokenIfExpired(); return this.instance - .get(url, this.getAxiosConfig()) + .get(url, { params, ...this.getAxiosConfig() }) .then((response) => response.data) .catch(this.handleUnauthorizedError); } diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index cf332405..c22aae4e 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -342,7 +342,7 @@ export function UserContent({ {(isMe || - (userProfile.friends && userProfile.friends.length > 0)) && ( + (userProfile.friends && userProfile.friends.totalFriends > 0)) && (
@@ -369,7 +369,7 @@ export function UserContent({ gap: `${SPACING_UNIT}px`, }} > - {userProfile.friends.map((friend) => { + {userProfile.friends.friends.map((friend) => { return (
@@ -72,6 +77,7 @@ export const UserFriendRequest = ({ @@ -80,12 +86,14 @@ export const UserFriendRequest = ({ diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index c22aae4e..6d9401fb 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -17,7 +17,13 @@ import { profileBackgroundFromProfileImage, steamUrlBuilder, } from "@renderer/helpers"; -import { PersonIcon, PlusIcon, TelescopeIcon } from "@primer/octicons-react"; +import { + CheckCircleIcon, + PersonIcon, + PlusIcon, + TelescopeIcon, + XCircleIcon, +} from "@primer/octicons-react"; import { Button, Link } from "@renderer/components"; import { UserEditProfileModal } from "./user-edit-modal"; import { UserSignOutModal } from "./user-signout-modal"; @@ -39,6 +45,7 @@ export function UserContent({ const { userDetails, profileBackground, + friendRequests, signOut, updateFriendRequests, showFriendsModal, @@ -116,6 +123,71 @@ export function UserContent({ } }, [profileBackground, isMe]); + const getProfileActions = () => { + if (isMe) { + return ( + <> + + + + + ); + } + + const friendRequest = friendRequests.find( + (request) => request.id == userProfile.id + ); + + if (!friendRequest) { + return ( + <> + + + + + ); + } + + if (friendRequest.type === "RECEIVED") { + return ( + <> + + + + ); + } + + return ( + + ); + }; + return ( <> - {isMe && ( +
-
- <> - - - - -
+ {getProfileActions()}
- )} +
diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts index 3df92ceb..f9b1b09a 100644 --- a/src/renderer/src/pages/user/user.css.ts +++ b/src/renderer/src/pages/user/user.css.ts @@ -279,3 +279,16 @@ export const profileBackground = style({ top: "0", borderRadius: "4px", }); + +export const cancelRequestButton = style({ + cursor: "pointer", + color: vars.color.body, + ":hover": { + color: vars.color.danger, + }, +}); + +export const acceptRequestButton = style({ + cursor: "pointer", + color: vars.color.success, +}); diff --git a/src/types/index.ts b/src/types/index.ts index 826c768f..1d91af62 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -293,6 +293,7 @@ export interface UserProfile { id: string; displayName: string; profileImageUrl: string | null; + profileVisibility: "PUBLIC" | "PRIVATE" | "FRIEND"; totalPlayTimeInSeconds: number; libraryGames: UserGame[]; recentGames: UserGame[]; From e642bf71b1741ca21a88e9b925ae70b90f286e0d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sun, 21 Jul 2024 20:49:37 -0300 Subject: [PATCH 011/108] feat: rename functions --- src/renderer/src/app.tsx | 6 ++-- src/renderer/src/hooks/use-user-details.ts | 14 ++++----- .../user-friend-modal-add-friend.tsx | 12 +++---- src/renderer/src/pages/user/user-content.tsx | 31 +++++++++++++++---- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 24c7bed6..689a338e 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -42,7 +42,7 @@ export function App() { const { isFriendsModalVisible, friendRequetsModalTab, - updateFriendRequests, + fetchFriendRequests, hideFriendsModal, } = useUserDetails(); @@ -104,7 +104,7 @@ export function App() { fetchUserDetails().then((response) => { if (response) { updateUserDetails(response); - updateFriendRequests(); + fetchFriendRequests(); } }); }, [fetchUserDetails, updateUserDetails, dispatch]); @@ -113,7 +113,7 @@ export function App() { fetchUserDetails().then((response) => { if (response) { updateUserDetails(response); - updateFriendRequests(); + fetchFriendRequests(); showSuccessToast(t("successfully_signed_in")); } }); diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index 80ef8b4c..8d53d996 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -84,7 +84,7 @@ export function useUserDetails() { [updateUserDetails] ); - const updateFriendRequests = useCallback(async () => { + const fetchFriendRequests = useCallback(async () => { const friendRequests = await window.electron.getFriendRequests(); dispatch(setFriendRequests(friendRequests)); }, [dispatch]); @@ -92,7 +92,7 @@ export function useUserDetails() { const showFriendsModal = useCallback( (tab: UserFriendModalTab) => { dispatch(setFriendsModalVisible(tab)); - updateFriendRequests(); + fetchFriendRequests(); }, [dispatch] ); @@ -105,18 +105,18 @@ export function useUserDetails() { async (userId: string) => { return window.electron .sendFriendRequest(userId) - .then(() => updateFriendRequests()); + .then(() => fetchFriendRequests()); }, - [updateFriendRequests] + [fetchFriendRequests] ); const updateFriendRequestState = useCallback( async (userId: string, action: FriendRequestAction) => { return window.electron .updateFriendRequest(userId, action) - .then(() => updateFriendRequests()); + .then(() => fetchFriendRequests()); }, - [updateFriendRequests] + [fetchFriendRequests] ); return { @@ -133,7 +133,7 @@ export function useUserDetails() { updateUserDetails, patchUser, sendFriendRequest, - updateFriendRequests, + fetchFriendRequests, updateFriendRequestState, }; } 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 bf4879b2..62290095 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 @@ -56,19 +56,19 @@ export const UserFriendModalAddFriend = ({ navigate(`/user/${friendCode}`); }; - const handleClickCancelFriendRequest = (userId: string) => { + const handleCancelFriendRequest = (userId: string) => { updateFriendRequestState(userId, "CANCEL").catch(() => { showErrorToast("Falha ao cancelar convite"); }); }; - const handleClickAcceptFriendRequest = (userId: string) => { + const handleAcceptFriendRequest = (userId: string) => { updateFriendRequestState(userId, "ACCEPTED").catch(() => { showErrorToast("Falha ao aceitar convite"); }); }; - const handleClickRefuseFriendRequest = (userId: string) => { + const handleRefuseFriendRequest = (userId: string) => { updateFriendRequestState(userId, "REFUSED").catch(() => { showErrorToast("Falha ao recusar convite"); }); @@ -127,9 +127,9 @@ export const UserFriendModalAddFriend = ({ isRequestSent={request.type === "SENT"} profileImageUrl={request.profileImageUrl} userId={request.id} - onClickAcceptRequest={handleClickAcceptFriendRequest} - onClickCancelRequest={handleClickCancelFriendRequest} - onClickRefuseRequest={handleClickRefuseFriendRequest} + onClickAcceptRequest={handleAcceptFriendRequest} + onClickCancelRequest={handleCancelFriendRequest} + onClickRefuseRequest={handleRefuseFriendRequest} onClickRequest={handleClickRequest} /> ); diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 6d9401fb..3ace2bd1 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -47,10 +47,11 @@ export function UserContent({ profileBackground, friendRequests, signOut, - updateFriendRequests, + fetchFriendRequests, showFriendsModal, + updateFriendRequestState, } = useUserDetails(); - const { showSuccessToast } = useToast(); + const { showSuccessToast, showErrorToast } = useToast(); const [profileContentBoxBackground, setProfileContentBoxBackground] = useState(); @@ -106,7 +107,7 @@ export function UserContent({ const isMe = userDetails?.id == userProfile.id; useEffect(() => { - if (isMe) updateFriendRequests(); + if (isMe) fetchFriendRequests(); }, [isMe]); useEffect(() => { @@ -123,6 +124,24 @@ export function UserContent({ } }, [profileBackground, isMe]); + const handleCancelFriendRequest = (userId: string) => { + updateFriendRequestState(userId, "CANCEL").catch(() => { + showErrorToast("Falha ao cancelar convite"); + }); + }; + + const handleAcceptFriendRequest = (userId: string) => { + updateFriendRequestState(userId, "ACCEPTED").catch(() => { + showErrorToast("Falha ao aceitar convite"); + }); + }; + + const handleRefuseFriendRequest = (userId: string) => { + updateFriendRequestState(userId, "REFUSED").catch(() => { + showErrorToast("Falha ao recusar convite"); + }); + }; + const getProfileActions = () => { if (isMe) { return ( @@ -162,14 +181,14 @@ export function UserContent({ @@ -181,7 +200,7 @@ export function UserContent({ From 11c29355e34339db63ba05810b35e9099c2ea5fa Mon Sep 17 00:00:00 2001 From: Lianela <140931995+Lianela@users.noreply.github.com> Date: Mon, 22 Jul 2024 19:38:34 -0600 Subject: [PATCH 012/108] Friend strings translated (ES) --- src/locales/es/translation.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index 5e016d34..fcb2b099 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -241,6 +241,15 @@ "successfully_signed_out": "Sesión cerrada exitosamente", "sign_out": "Cerrar sesión", "playing_for": "Jugando por {{amount}}", - "sign_out_modal_text": "Tu biblioteca se ha vinculado con tu cuenta. Cuando cierres sesión, tú biblioteca ya no será visible y cualquier progreso no se guardará. ¿Continuar con el cierre de sesión?" + "sign_out_modal_text": "Tu biblioteca se ha vinculado con tu cuenta. Cuando cierres sesión, tú biblioteca ya no será visible y cualquier progreso no se guardará. ¿Continuar con el cierre de sesión?", + "add_friends": "Añadir amigos", + "add": "Añadir", + "friend_code": "Código de amigo", + "see_profile": "Ver perfil", + "sending": "Enviando", + "friend_request_sent": "Solicitud de amistad enviada", + "friends": "Amigos", + "friends_list": "Lista de amigos", + "user_not_found": "Usuario no encontrado" } } From 380143c780c585f2f9e83f0eb349b636976d99d8 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:30:04 -0300 Subject: [PATCH 013/108] feat: correctly get own friends --- src/main/events/user/get-user-friends.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/events/user/get-user-friends.ts b/src/main/events/user/get-user-friends.ts index a82673eb..4505909a 100644 --- a/src/main/events/user/get-user-friends.ts +++ b/src/main/events/user/get-user-friends.ts @@ -1,3 +1,4 @@ +import { userAuthRepository } from "@main/repository"; import { registerEvent } from "../register-event"; import { HydraApi } from "@main/services"; import { UserFriends } from "@types"; @@ -7,9 +8,19 @@ export const getUserFriends = async ( take: number, skip: number ): Promise => { + 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: [] } as UserFriends; + return { totalFriends: 0, friends: [] }; } ); }; From 010f07373d797534a3e6287aff0fcfc2603fa52a Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 23 Jul 2024 18:37:19 -0300 Subject: [PATCH 014/108] feat: add getUserFriends event --- src/main/events/index.ts | 1 + src/main/events/user/get-user-friends.ts | 20 +++++++++++--------- src/preload/index.ts | 1 + src/renderer/src/declaration.d.ts | 2 ++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/events/index.ts b/src/main/events/index.ts index dd5e3263..398235f0 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-friends"; import "./profile/get-friend-requests"; import "./profile/get-me"; import "./profile/update-friend-request"; diff --git a/src/main/events/user/get-user-friends.ts b/src/main/events/user/get-user-friends.ts index 4505909a..a9394d95 100644 --- a/src/main/events/user/get-user-friends.ts +++ b/src/main/events/user/get-user-friends.ts @@ -8,21 +8,23 @@ export const getUserFriends = async ( take: number, skip: number ): Promise => { - const loggedUser = await userAuthRepository.findOne({ where: { id: 1 } }); + try { + const loggedUser = await userAuthRepository.findOne({ where: { id: 1 } }); - if (loggedUser?.userId == userId) { - return HydraApi.get(`/profile/friends`, { take, skip }).catch( + 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: [] }; } - - return HydraApi.get(`/user/${userId}/friends`, { take, skip }).catch( - (_err) => { - return { totalFriends: 0, friends: [] }; - } - ); }; const getUserFriendsEvent = async ( diff --git a/src/preload/index.ts b/src/preload/index.ts index 91722606..e6dafe0f 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -145,6 +145,7 @@ contextBridge.exposeInMainWorld("electron", { /* User */ getUser: (userId: string) => ipcRenderer.invoke("getUser", userId), + getUserFriends: (userId: string) => ipcRenderer.invoke("getUserFriends", userId), /* Auth */ signOut: () => ipcRenderer.invoke("signOut"), diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index bb89f84e..55fc1269 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -16,6 +16,7 @@ import type { UserProfile, FriendRequest, FriendRequestAction, + UserFriends, } from "@types"; import type { DiskSpace } from "check-disk-space"; @@ -127,6 +128,7 @@ declare global { /* User */ getUser: (userId: string) => Promise; + getUserFriends: (userId: string) => Promise; /* Profile */ getMe: () => Promise; From a196b91cb9a744b6bac644611de51ea60a7328b4 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 23 Jul 2024 20:27:38 -0300 Subject: [PATCH 015/108] feat: pass userId to modal --- src/preload/index.ts | 3 +- src/renderer/src/app.tsx | 16 +++++--- .../components/sidebar/sidebar-profile.tsx | 4 +- src/renderer/src/declaration.d.ts | 6 ++- .../src/features/user-details-slice.ts | 7 +++- src/renderer/src/hooks/use-user-details.ts | 6 ++- .../user-friend-modal-list.tsx | 40 ++++++++++++++++++ .../user-friend-modal/user-friend-modal.tsx | 41 +++++++++++-------- src/renderer/src/pages/user/user-content.tsx | 12 +++++- 9 files changed, 104 insertions(+), 31 deletions(-) create mode 100644 src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx diff --git a/src/preload/index.ts b/src/preload/index.ts index e6dafe0f..cd3b9686 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -145,7 +145,8 @@ contextBridge.exposeInMainWorld("electron", { /* User */ getUser: (userId: string) => ipcRenderer.invoke("getUser", userId), - getUserFriends: (userId: string) => ipcRenderer.invoke("getUserFriends", userId), + getUserFriends: (userId: string, take: number, skip: number) => + ipcRenderer.invoke("getUserFriends", userId, take, skip), /* Auth */ signOut: () => ipcRenderer.invoke("signOut"), diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 689a338e..8c6f7604 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -42,11 +42,12 @@ export function App() { const { isFriendsModalVisible, friendRequetsModalTab, + friendModalUserId, fetchFriendRequests, hideFriendsModal, } = useUserDetails(); - const { fetchUserDetails, updateUserDetails, clearUserDetails } = + const { userDetails, fetchUserDetails, updateUserDetails, clearUserDetails } = useUserDetails(); const dispatch = useAppDispatch(); @@ -218,11 +219,14 @@ export function App() { onClose={handleToastClose} /> - + {userDetails && ( + + )}
diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index 66c4d82d..3ff598f7 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -84,7 +84,9 @@ export function SidebarProfile() { - ); - })} - + {isMe && ( +
+ {tabs.map((tab, index) => { + return ( + + ); + })} +
+ )}

{tabs[currentTab]}

{renderTab()}
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 3ace2bd1..9fb1c8b5 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -424,7 +424,12 @@ export function UserContent({
+ ); + } + + if (type === "RECEIVED") { + return ( + <> + + + + ); + } + + return ( + + ); + }; + return (
@@ -73,32 +126,7 @@ export const UserFriendRequest = ({ gap: `${SPACING_UNIT}px`, }} > - {isRequestSent ? ( - - ) : ( - <> - - - - )} + {getRequestActions()}
); 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 62290095..f71d4790 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 @@ -4,7 +4,7 @@ import { SPACING_UNIT } from "@renderer/theme.css"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; -import { UserFriendRequest } from "./user-friend-request"; +import { UserFriendItem } from "./user-friend-item"; export interface UserFriendModalAddFriendProps { closeModal: () => void; @@ -121,16 +121,16 @@ export const UserFriendModalAddFriend = ({

Pendentes

{friendRequests.map((request) => { 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 83d7a6a0..2c639847 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 @@ -1,13 +1,22 @@ +import { SPACING_UNIT } from "@renderer/theme.css"; import { UserFriend } from "@types"; import { useEffect, useState } from "react"; +import { UserFriendItem } from "./user-friend-item"; +import { useNavigate } from "react-router-dom"; export interface UserFriendModalListProps { userId: string; + closeModal: () => void; } const pageSize = 12; -export const UserFriendModalList = ({ userId }: UserFriendModalListProps) => { +export const UserFriendModalList = ({ + userId, + closeModal, +}: UserFriendModalListProps) => { + const navigate = useNavigate(); + const [page, setPage] = useState(0); const [maxPage, setMaxPage] = useState(0); const [friends, setFriends] = useState([]); @@ -34,7 +43,34 @@ export const UserFriendModalList = ({ userId }: UserFriendModalListProps) => { loadNextPage(); }, [userId]); - return friends.map((friend) => { - return

{friend.displayName}

; - }); + const handleClickFriend = (userId: string) => { + closeModal(); + navigate(`/user/${userId}`); + }; + + return ( +
+ {friends.map((friend) => { + return ( + {}} + onClickCancelRequest={() => {}} + onClickRefuseRequest={() => {}} + onClickItem={handleClickFriend} + type={"ACCEPTED"} + key={friend.id} + /> + ); + })} +
+ ); }; 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 b0752de7..39a82ec4 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 @@ -43,7 +43,7 @@ export const UserFriendModal = ({ const renderTab = () => { if (currentTab == UserFriendModalTab.FriendsList) { - return ; + return ; } if (currentTab == UserFriendModalTab.AddFriend) { From 94bd691209c7cd42bbc3d6a5c6533c18d93f321a Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:12:14 -0300 Subject: [PATCH 017/108] fet: show only received friends request on sidebar icon --- .../src/components/sidebar/sidebar-profile.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index 3ff598f7..b16c2c28 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -3,10 +3,11 @@ import { PersonAddIcon, PersonIcon } from "@primer/octicons-react"; import * as styles from "./sidebar-profile.css"; import { assignInlineVars } from "@vanilla-extract/dynamic"; import { useAppSelector, useUserDetails } from "@renderer/hooks"; -import { useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { profileContainerBackground } from "./sidebar-profile.css"; import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal"; +import { FriendRequest } from "@types"; export function SidebarProfile() { const navigate = useNavigate(); @@ -16,6 +17,14 @@ export function SidebarProfile() { const { userDetails, profileBackground, friendRequests, showFriendsModal } = useUserDetails(); + const [receivedRequests, setReceivedRequests] = useState([]); + + useEffect(() => { + setReceivedRequests( + receivedRequests.filter((request) => request.type === "RECEIVED") + ); + }, [friendRequests]); + const { gameRunning } = useAppSelector((state) => state.gameRunning); const handleButtonClick = () => { @@ -79,7 +88,7 @@ export function SidebarProfile() { )} - {userDetails && friendRequests.length > 0 && !gameRunning && ( + {userDetails && receivedRequests.length > 0 && !gameRunning && (
)} 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 ( <> - @@ -175,35 +196,49 @@ export function UserContent({ ); } - if (friendRequest.type === "RECEIVED") { + if (userProfile.relation.status === "ACCEPTED") { return ( <> - ); } + if (userProfile.relation.BId === userProfile.id) { + return ( + + ); + } + return ( - + <> + + + ); }; 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")} - 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 })} +

+
+ + + +
+
+
+ + ); +}; 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")} - @@ -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í.", - "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 ( <> - @@ -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 ( <> @@ -233,7 +216,9 @@ export function UserContent({ @@ -245,14 +230,18 @@ export function UserContent({ @@ -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 && (
@@ -512,7 +500,7 @@ export function UserContent({ gap: `${SPACING_UNIT}px`, }} > - {userProfile.friends.friends.map((friend) => { + {userProfile.friends.map((friend) => { return ( - ); + if (type === "ACCEPTED") { + return ( + + ); + } + + 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>Pengaturan", + "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>sini", + "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 ( <> - - setDisplayName(e.target.value)} - /> - - - - - ); -}; 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 ( +
+ + + setForm({ ...form, displayName: e.target.value })} + /> + + ({ + key: visiblity.value, + value: visiblity.value, + label: visiblity.label, + }))} + /> + + + + ); +}; 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 ( + + ); + })} +
+ {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) => ( - - ))} -
- )} -
- -
+ {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) => ( - - ))} -
+
+

{game.title}

+ + {t("last_time_played", { + period: formatDistance( + game.lastTimePlayed!, + new Date(), + { + addSuffix: true, + } + ), + })} + +
+ + ))} +
+ )}
- {showFriends && ( -
- - +
+ + {t("total_play_time", { amount: formatPlayTime() })} +
- {userProfile.friends.map((friend) => { - return ( - - ); - })} - - {isMe && ( - - )} + {game.iconUrl ? ( + {game.title} + ) : ( + + )} + + ))}
- )} + + {showFriends && ( +
+ + +
+ {userProfile.friends.map((friend) => { + return ( + + ); + })} + + {isMe && ( + + )} +
+
+ )} +
- + )} ); } 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("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")}

+ ); + } + return null; }; + if (type === "BLOCKED") { + return ( +
+
+
+ {profileImageUrl ? ( + {displayName} + ) : ( + + )} +
+
+

{displayName}

+
+
+ +
+ {getRequestActions()} +
+
+ ); + } + return (
- ); - })} - + <> +
+

Meu código de amigo:

+ +
+
+ {tabs.map((tab, index) => { + return ( + + ); + })} +
+ )} {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:

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 && ( {gameRunning.title}(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 && ( {gameRunning.title} )} @@ -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 })}

+
+ + + +
+
+
+ + ); +}; 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 ( <> @@ -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 && ( -
- -
+ {showPendingRequests && ( + )}
); 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", })} > -
+
+ +
+

{t("undo_friendship_modal_text", { displayName })}

+
+ - -
+
-
- +
+ ); }; 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 Ajustes", - "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>aqui", - "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