ci: testing pipeline

This commit is contained in:
Hydra 2024-04-29 11:01:34 +01:00
parent 5bec457ba6
commit 3b5f3da2ab
27 changed files with 1349 additions and 1399 deletions

View File

@ -8,21 +8,9 @@ jobs:
build: build:
strategy: strategy:
matrix: matrix:
os: os: [windows-latest, ubuntu-latest]
[
{
name: windows-latest,
build_path: out/Hydra-win32-x64,
artifact: Hydra-win32-x64,
},
{
name: ubuntu-latest,
build_path: out/Hydra-linux-x64,
artifact: Hydra-linux-x64,
},
]
runs-on: ${{ matrix.os.name }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Check out Git repository - name: Check out Git repository
@ -47,10 +35,10 @@ jobs:
- name: Build with cx_Freeze - name: Build with cx_Freeze
run: python torrent-client/setup.py build run: python torrent-client/setup.py build
- name: Publish - name: Build Linux
run: yarn run publish if: matrix.os == 'ubuntu-latest'
run: yarn build:linux
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MAIN_VITE_STEAMGRIDDB_API_KEY: ${{ secrets.STEAMGRIDDB_API_KEY }} MAIN_VITE_STEAMGRIDDB_API_KEY: ${{ secrets.STEAMGRIDDB_API_KEY }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
MAIN_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }} MAIN_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
@ -58,15 +46,55 @@ jobs:
MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }} MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }}
MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }} MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }}
- name: VirusTotal Scan - name: Build Windows
uses: crazy-max/ghaction-virustotal@v4 if: matrix.os == 'windows-latest'
with: run: yarn build:win
vt_api_key: ${{ secrets.VT_API_KEY }} env:
files: | MAIN_VITE_STEAMGRIDDB_API_KEY: ${{ secrets.STEAMGRIDDB_API_KEY }}
./hydra-download-manager/hydra-download-manager.exe SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
MAIN_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
MAIN_VITE_ONLINEFIX_USERNAME: ${{ secrets.ONLINEFIX_USERNAME }}
MAIN_VITE_ONLINEFIX_PASSWORD: ${{ secrets.ONLINEFIX_PASSWORD }}
- name: Create artifact - name: Create artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ${{ matrix.os.artifact }} name: Build-${{ matrix.os }}
path: ${{ matrix.os.build_path }} path: |
dist/*.exe
dist/*.zip
dist/*.dmg
dist/*.AppImage
dist/*.snap
dist/*.deb
dist/*.rpm
dist/*.tar.gz
dist/*.yml
dist/*.blockmap
- name: VirusTotal Scan
uses: crazy-max/ghaction-virustotal@v4
if: matrix.os == 'windows-latest'
with:
vt_api_key: ${{ secrets.VT_API_KEY }}
files: |
./dist/*.exe
- name: Publish
uses: softprops/action-gh-release@v1
with:
draft: true
files: |
dist/*.exe
dist/*.zip
dist/*.dmg
dist/*.AppImage
dist/*.snap
dist/*.deb
dist/*.rpm
dist/*.tar.gz
dist/*.yml
dist/*.blockmap
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -31,6 +31,7 @@ export default defineConfig(({ mode }) => {
alias: { alias: {
"@main": resolve("src/main"), "@main": resolve("src/main"),
"@locales": resolve("src/locales"), "@locales": resolve("src/locales"),
"@resources": resolve("resources"),
}, },
}, },
plugins: [ plugins: [

View File

@ -21,7 +21,7 @@
"build": "npm run typecheck && electron-vite build", "build": "npm run typecheck && electron-vite build",
"postinstall": "electron-builder install-app-deps && node ./postinstall.cjs", "postinstall": "electron-builder install-app-deps && node ./postinstall.cjs",
"build:unpack": "npm run build && electron-builder --dir", "build:unpack": "npm run build && electron-builder --dir",
"build:win": "npm run build && electron-builder --win", "build:win": "electron-builder --win",
"build:mac": "electron-vite build && electron-builder --mac", "build:mac": "electron-vite build && electron-builder --mac",
"build:linux": "electron-vite build && electron-builder --linux" "build:linux": "electron-vite build && electron-builder --linux"
}, },
@ -70,6 +70,7 @@
"@types/jsdom": "^21.1.6", "@types/jsdom": "^21.1.6",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^20.12.7", "@types/node": "^20.12.7",
"@types/parse-torrent": "^5.8.7",
"@types/react": "^18.2.48", "@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"@vanilla-extract/vite-plugin": "^4.0.7", "@vanilla-extract/vite-plugin": "^4.0.7",

View File

@ -1,9 +1,9 @@
import path from "node:path"; import path from "node:path";
import { gameRepository } from "@main/repository"; import { gameRepository } from "@main/repository";
import { getProcesses } from "@main/helpers";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { getProcesses } from "@main/helpers";
const closeGame = async ( const closeGame = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
@ -12,13 +12,17 @@ const closeGame = async (
const processes = await getProcesses(); const processes = await getProcesses();
const game = await gameRepository.findOne({ where: { id: gameId } }); const game = await gameRepository.findOne({ where: { id: gameId } });
const gameProcess = processes.find((runningProcess) => { if (!game) return false;
const basename = path.win32.basename(game.executablePath);
const executablePath = game.executablePath!;
const basename = path.win32.basename(executablePath);
const basenameWithoutExtension = path.win32.basename( const basenameWithoutExtension = path.win32.basename(
game.executablePath, executablePath,
path.extname(game.executablePath) path.extname(executablePath)
); );
const gameProcess = processes.find((runningProcess) => {
if (process.platform === "win32") { if (process.platform === "win32") {
return runningProcess.name === basename; return runningProcess.name === basename;
} }

View File

@ -4,7 +4,7 @@ import {
getNewGOGGames, getNewGOGGames,
getNewRepacksFromCPG, getNewRepacksFromCPG,
getNewRepacksFromUser, getNewRepacksFromUser,
// getNewRepacksFromXatab, getNewRepacksFromXatab,
getNewRepacksFromOnlineFix, getNewRepacksFromOnlineFix,
readPipe, readPipe,
startProcessWatcher, startProcessWatcher,
@ -22,13 +22,6 @@ import { Repack } from "./entity";
import { Notification } from "electron"; import { Notification } from "electron";
import { t } from "i18next"; import { t } from "i18next";
import { In } from "typeorm"; import { In } from "typeorm";
import creatWorker from "./workers/test?nodeWorker";
creatWorker({ workerData: "worker" })
.on("message", (message) => {
console.log(`\nMessage from worker: ${message}`);
})
.postMessage("");
startProcessWatcher(); startProcessWatcher();
@ -80,9 +73,9 @@ const checkForNewRepacks = async () => {
getNewGOGGames( getNewGOGGames(
existingRepacks.filter((repack) => repack.repacker === "GOG") existingRepacks.filter((repack) => repack.repacker === "GOG")
), ),
// getNewRepacksFromXatab( getNewRepacksFromXatab(
// existingRepacks.filter((repack) => repack.repacker === "Xatab") existingRepacks.filter((repack) => repack.repacker === "Xatab")
// ), ),
getNewRepacksFromCPG( getNewRepacksFromCPG(
existingRepacks.filter((repack) => repack.repacker === "CPG") existingRepacks.filter((repack) => repack.repacker === "CPG")
), ),

View File

@ -4,7 +4,6 @@ import { formatUploadDate } from "@main/helpers";
import { Repack } from "@main/entity"; import { Repack } from "@main/entity";
import { requestWebPage, savePage } from "./helpers"; import { requestWebPage, savePage } from "./helpers";
import type { GameRepackInput } from "./helpers";
export const request1337x = async (path: string) => export const request1337x = async (path: string) =>
requestWebPage(`https://1337xx.to${path}`); requestWebPage(`https://1337xx.to${path}`);
@ -68,7 +67,7 @@ export const extractTorrentsFromDocument = async (
user: string, user: string,
document: Document, document: Document,
existingRepacks: Repack[] = [] existingRepacks: Repack[] = []
): Promise<GameRepackInput[]> => { ) => {
const $trs = Array.from(document.querySelectorAll("tbody tr")); const $trs = Array.from(document.querySelectorAll("tbody tr"));
return Promise.all( return Promise.all(
@ -108,7 +107,7 @@ export const getNewRepacksFromUser = async (
user: string, user: string,
existingRepacks: Repack[], existingRepacks: Repack[],
page = 1 page = 1
): Promise<Repack[]> => { ) => {
const response = await request1337x(`/user/${user}/${page}`); const response = await request1337x(`/user/${user}/${page}`);
const { window } = new JSDOM(response); const { window } = new JSDOM(response);

View File

@ -3,7 +3,6 @@ import { JSDOM } from "jsdom";
import { Repack } from "@main/entity"; import { Repack } from "@main/entity";
import { requestWebPage, savePage } from "./helpers"; import { requestWebPage, savePage } from "./helpers";
import type { GameRepackInput } from "./helpers";
import { logger } from "../logger"; import { logger } from "../logger";
export const getNewRepacksFromCPG = async ( export const getNewRepacksFromCPG = async (
@ -14,7 +13,7 @@ export const getNewRepacksFromCPG = async (
const { window } = new JSDOM(data); const { window } = new JSDOM(data);
const repacks: GameRepackInput[] = []; const repacks = [];
try { try {
Array.from(window.document.querySelectorAll(".post")).forEach(($post) => { Array.from(window.document.querySelectorAll(".post")).forEach(($post) => {

View File

@ -1,7 +1,8 @@
import { JSDOM, VirtualConsole } from "jsdom"; import { JSDOM, VirtualConsole } from "jsdom";
import { GameRepackInput, requestWebPage, savePage } from "./helpers"; import { requestWebPage, savePage } from "./helpers";
import { Repack } from "@main/entity"; import { Repack } from "@main/entity";
import { logger } from "../logger";
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity";
const virtualConsole = new VirtualConsole(); const virtualConsole = new VirtualConsole();
@ -36,7 +37,6 @@ const getGOGGame = async (url: string) => {
}; };
export const getNewGOGGames = async (existingRepacks: Repack[] = []) => { export const getNewGOGGames = async (existingRepacks: Repack[] = []) => {
try {
const data = await requestWebPage( const data = await requestWebPage(
"https://freegogpcgames.com/a-z-games-list/" "https://freegogpcgames.com/a-z-games-list/"
); );
@ -46,7 +46,7 @@ export const getNewGOGGames = async (existingRepacks: Repack[] = []) => {
const $uls = Array.from(window.document.querySelectorAll(".az-columns")); const $uls = Array.from(window.document.querySelectorAll(".az-columns"));
for (const $ul of $uls) { for (const $ul of $uls) {
const repacks: GameRepackInput[] = []; const repacks: QueryDeepPartialEntity<Repack>[] = [];
const $lis = Array.from($ul.querySelectorAll("li")); const $lis = Array.from($ul.querySelectorAll("li"));
for (const $li of $lis) { for (const $li of $lis) {
@ -60,19 +60,12 @@ export const getNewGOGGames = async (existingRepacks: Repack[] = []) => {
); );
if (!gameExists) { if (!gameExists) {
try {
const game = await getGOGGame(href); const game = await getGOGGame(href);
repacks.push({ ...game, title }); repacks.push({ ...game, title });
} catch (err) {
logger.error(err.message, { method: "getGOGGame", url: href });
}
} }
} }
if (repacks.length) await savePage(repacks); if (repacks.length) await savePage(repacks);
} }
} catch (err) {
logger.error(err.message, { method: "getNewGOGGames" });
}
}; };

View File

@ -1,6 +1,5 @@
import { Repack } from "@main/entity"; import { Repack } from "@main/entity";
import { savePage } from "./helpers"; import { savePage } from "./helpers";
import type { GameRepackInput } from "./helpers";
import { logger } from "../logger"; import { logger } from "../logger";
import parseTorrent, { import parseTorrent, {
toMagnetURI, toMagnetURI,
@ -85,7 +84,7 @@ export const getNewRepacksFromOnlineFix = async (
}); });
const document = new JSDOM(home.body).window.document; const document = new JSDOM(home.body).window.document;
const repacks: GameRepackInput[] = []; const repacks = [];
const articles = Array.from(document.querySelectorAll(".news")); const articles = Array.from(document.querySelectorAll(".news"));
const totalPages = Number( const totalPages = Number(
document.querySelector("nav > a:nth-child(13)")?.textContent document.querySelector("nav > a:nth-child(13)")?.textContent

View File

@ -1,16 +1,14 @@
import { JSDOM } from "jsdom"; import { JSDOM } from "jsdom";
import parseTorrent, { toMagnetURI } from "parse-torrent";
import { Repack } from "@main/entity"; import { Repack } from "@main/entity";
import { logger } from "../logger"; import { logger } from "../logger";
import { requestWebPage, savePage } from "./helpers"; import { requestWebPage, savePage } from "./helpers";
import type { GameRepackInput } from "./helpers";
const getTorrentBuffer = (url: string) => import createWorker from "@main/workers/torrent-parser.worker?nodeWorker";
fetch(url, { method: "GET" }).then((response) => import { toMagnetURI } from "parse-torrent";
response.arrayBuffer().then((buffer) => Buffer.from(buffer)) import type { Instance } from "parse-torrent";
);
const worker = createWorker({});
const formatXatabDate = (str: string) => { const formatXatabDate = (str: string) => {
const date = new Date(); const date = new Date();
@ -28,7 +26,9 @@ const formatXatabDate = (str: string) => {
const formatXatabDownloadSize = (str: string) => const formatXatabDownloadSize = (str: string) =>
str.replace(",", ".").replace(/Гб/g, "GB").replace(/Мб/g, "MB"); str.replace(",", ".").replace(/Гб/g, "GB").replace(/Мб/g, "MB");
const getXatabRepack = async (url: string) => { const getXatabRepack = (url: string) => {
return new Promise((resolve) => {
(async () => {
const data = await requestWebPage(url); const data = await requestWebPage(url);
const { window } = new JSDOM(data); const { window } = new JSDOM(data);
const { document } = window; const { document } = window;
@ -42,15 +42,20 @@ const getXatabRepack = async (url: string) => {
if (!$downloadButton) throw new Error("Download button not found"); if (!$downloadButton) throw new Error("Download button not found");
const torrentBuffer = await getTorrentBuffer($downloadButton.href); const onMessage = (torrent: Instance) => {
resolve({
return {
fileSize: formatXatabDownloadSize($size.textContent).toUpperCase(), fileSize: formatXatabDownloadSize($size.textContent).toUpperCase(),
magnet: toMagnetURI({ magnet: toMagnetURI(torrent),
infoHash: parseTorrent(torrentBuffer).infoHash,
}),
uploadDate: formatXatabDate($uploadDate.textContent), uploadDate: formatXatabDate($uploadDate.textContent),
});
worker.removeListener("message", onMessage);
}; };
worker.on("message", onMessage);
worker.postMessage($downloadButton.href);
})();
});
}; };
export const getNewRepacksFromXatab = async ( export const getNewRepacksFromXatab = async (
@ -61,7 +66,7 @@ export const getNewRepacksFromXatab = async (
const { window } = new JSDOM(data); const { window } = new JSDOM(data);
const repacks: GameRepackInput[] = []; const repacks = [];
for (const $a of Array.from( for (const $a of Array.from(
window.document.querySelectorAll(".entry__title a") window.document.querySelectorAll(".entry__title a")
@ -84,7 +89,6 @@ export const getNewRepacksFromXatab = async (
const newRepacks = repacks.filter( const newRepacks = repacks.filter(
(repack) => (repack) =>
repack.uploadDate &&
!existingRepacks.some( !existingRepacks.some(
(existingRepack) => existingRepack.title === repack.title (existingRepack) => existingRepack.title === repack.title
) )

View File

@ -2,8 +2,8 @@ import { BrowserWindow, Menu, Tray, app } from "electron";
import { is } from "@electron-toolkit/utils"; import { is } from "@electron-toolkit/utils";
import { t } from "i18next"; import { t } from "i18next";
import path from "node:path"; import path from "node:path";
import icon from "../../../resources/icon.png?asset"; import icon from "@resources/icon.png?asset";
import trayIcon from "../../../resources/tray-icon.png?asset"; import trayIcon from "@resources/tray-icon.png?asset";
export class WindowManager { export class WindowManager {
public static mainWindow: Electron.BrowserWindow | null = null; public static mainWindow: Electron.BrowserWindow | null = null;

View File

@ -0,0 +1,17 @@
import { parentPort } from "worker_threads";
import parseTorrent from "parse-torrent";
const port = parentPort;
if (!port) throw new Error("IllegalState");
const getTorrentBuffer = (url: string) =>
fetch(url, { method: "GET" }).then((response) =>
response.arrayBuffer().then((buffer) => Buffer.from(buffer))
);
port.on("message", async (url: string) => {
const buffer = await getTorrentBuffer(url);
const torrent = await parseTorrent(buffer);
port.postMessage(torrent);
});

View File

@ -71,7 +71,8 @@ contextBridge.exposeInMainWorld("electron", {
openGame: (gameId: number, executablePath: string) => openGame: (gameId: number, executablePath: string) =>
ipcRenderer.invoke("openGame", gameId, executablePath), ipcRenderer.invoke("openGame", gameId, executablePath),
closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId), closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId),
removeGame: (gameId: number) => ipcRenderer.invoke("removeGame", gameId), removeGameFromLibrary: (gameId: number) =>
ipcRenderer.invoke("removeGameFromLibrary", gameId),
deleteGameFolder: (gameId: number) => deleteGameFolder: (gameId: number) =>
ipcRenderer.invoke("deleteGameFolder", gameId), ipcRenderer.invoke("deleteGameFolder", gameId),
getGameByObjectID: (objectID: string) => getGameByObjectID: (objectID: string) =>

View File

@ -80,7 +80,8 @@ contextBridge.exposeInMainWorld("electron", {
openGame: (gameId: number, executablePath: string) => openGame: (gameId: number, executablePath: string) =>
ipcRenderer.invoke("openGame", gameId, executablePath), ipcRenderer.invoke("openGame", gameId, executablePath),
closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId), closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId),
removeGame: (gameId: number) => ipcRenderer.invoke("removeGame", gameId), removeGameFromLibrary: (gameId: number) =>
ipcRenderer.invoke("removeGameFromLibrary", gameId),
deleteGameFolder: (gameId: number) => deleteGameFolder: (gameId: number) =>
ipcRenderer.invoke("deleteGameFolder", gameId), ipcRenderer.invoke("deleteGameFolder", gameId),
getGameByObjectID: (objectID: string) => getGameByObjectID: (objectID: string) =>

View File

@ -9,9 +9,8 @@
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://steamcdn-a.akamaihd.net https://cdn.cloudflare.steamstatic.com https://cdn2.steamgriddb.com https://cdn.akamai.steamstatic.com;" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://steamcdn-a.akamaihd.net https://cdn.cloudflare.steamstatic.com https://cdn2.steamgriddb.com https://cdn.akamai.steamstatic.com;"
/> />
</head> </head>
<body> <body style="background-color: #1c1c1">
<div id="root"></div> <div id="root"></div>
<h1>hello</h1>
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>
</body> </body>
</html> </html>

View File

@ -22,7 +22,7 @@ export function BottomPanel() {
}, []); }, []);
const status = useMemo(() => { const status = useMemo(() => {
if (isDownloading) { if (isDownloading && game) {
if (game.status === "downloading_metadata") if (game.status === "downloading_metadata")
return t("downloading_metadata", { title: game.title }); return t("downloading_metadata", { title: game.title });

View File

@ -62,13 +62,15 @@ export function Modal({
const onMouseDown = (e: MouseEvent) => { const onMouseDown = (e: MouseEvent) => {
if (!isTopMostModal()) return; if (!isTopMostModal()) return;
const clickedOutsideContent = !modalContentRef.current.contains( if (modalContentRef.current) {
const clickedOutsideContent = modalContentRef.current.contains(
e.target as Node e.target as Node
); );
if (clickedOutsideContent) { if (clickedOutsideContent) {
handleCloseClick(); handleCloseClick();
} }
}
}; };
window.addEventListener("mousedown", onMouseDown); window.addEventListener("mousedown", onMouseDown);

View File

@ -62,7 +62,7 @@ declare global {
openGameInstaller: (gameId: number) => Promise<boolean>; openGameInstaller: (gameId: number) => Promise<boolean>;
openGame: (gameId: number, executablePath: string) => Promise<void>; openGame: (gameId: number, executablePath: string) => Promise<void>;
closeGame: (gameId: number) => Promise<boolean>; closeGame: (gameId: number) => Promise<boolean>;
removeGame: (gameId: number) => Promise<void>; removeGameFromLibrary: (gameId: number) => Promise<void>;
deleteGameFolder: (gameId: number) => Promise<unknown>; deleteGameFolder: (gameId: number) => Promise<unknown>;
getGameByObjectID: (objectID: string) => Promise<Game | null>; getGameByObjectID: (objectID: string) => Promise<Game | null>;
onPlaytime: (cb: (gameId: number) => void) => () => Electron.IpcRenderer; onPlaytime: (cb: (gameId: number) => void) => () => Electron.IpcRenderer;

View File

@ -14,7 +14,10 @@ export const userPreferencesSlice = createSlice({
name: "userPreferences", name: "userPreferences",
initialState, initialState,
reducers: { reducers: {
setUserPreferences: (state, action: PayloadAction<UserPreferences>) => { setUserPreferences: (
state,
action: PayloadAction<UserPreferences | null>
) => {
state.value = action.payload; state.value = action.payload;
}, },
}, },

View File

@ -58,17 +58,17 @@ export function useDownload() {
deleteGame(gameId); deleteGame(gameId);
}); });
const removeGame = (gameId: number) => const removeGameFromLibrary = (gameId: number) =>
window.electron.removeGame(gameId).then(() => { window.electron.removeGameFromLibrary(gameId).then(() => {
updateLibrary(); updateLibrary();
}); });
const isVerifying = ["downloading_metadata", "checking_files"].includes( const isVerifying = ["downloading_metadata", "checking_files"].includes(
lastPacket?.game.status lastPacket?.game.status ?? ""
); );
const getETA = () => { const getETA = () => {
if (isVerifying || !isFinite(lastPacket?.timeRemaining)) { if (isVerifying || !isFinite(lastPacket?.timeRemaining ?? 0)) {
return ""; return "";
} }
@ -124,7 +124,7 @@ export function useDownload() {
pauseDownload, pauseDownload,
resumeDownload, resumeDownload,
cancelDownload, cancelDownload,
removeGame, removeGameFromLibrary,
deleteGame, deleteGame,
isGameDeleting, isGameDeleting,
clearDownload: () => dispatch(clearDownload()), clearDownload: () => dispatch(clearDownload()),

View File

@ -33,6 +33,7 @@ export function Downloads() {
numSeeds, numSeeds,
pauseDownload, pauseDownload,
resumeDownload, resumeDownload,
removeGameFromLibrary,
cancelDownload, cancelDownload,
deleteGame, deleteGame,
isGameDeleting, isGameDeleting,
@ -52,11 +53,6 @@ export function Downloads() {
updateLibrary(); updateLibrary();
}); });
const removeGame = (gameId: number) =>
window.electron.removeGame(gameId).then(() => {
updateLibrary();
});
const getFinalDownloadSize = (game: Game) => { const getFinalDownloadSize = (game: Game) => {
const isGameDownloading = isDownloading && gameDownloading?.id === game?.id; const isGameDownloading = isDownloading && gameDownloading?.id === game?.id;
@ -194,7 +190,7 @@ export function Downloads() {
</Button> </Button>
<Button <Button
onClick={() => removeGame(game.id)} onClick={() => removeGameFromLibrary(game.id)}
theme="outline" theme="outline"
disabled={deleting} disabled={deleting}
> >

View File

@ -32,7 +32,7 @@ export function HeroPanelActions({
resumeDownload, resumeDownload,
pauseDownload, pauseDownload,
cancelDownload, cancelDownload,
removeGame, removeGameFromLibrary,
isGameDeleting, isGameDeleting,
} = useDownload(); } = useDownload();
@ -63,7 +63,7 @@ export function HeroPanelActions({
try { try {
if (game) { if (game) {
await removeGame(game.id); await removeGameFromLibrary(game.id);
} else { } else {
const gameExecutablePath = await selectGameExecutable(); const gameExecutablePath = await selectGameExecutable();
@ -187,7 +187,7 @@ export function HeroPanelActions({
{t("open_download_options")} {t("open_download_options")}
</Button> </Button>
<Button <Button
onClick={() => removeGame(game.id).then(getGame)} onClick={() => removeGameFromLibrary(game.id).then(getGame)}
theme="outline" theme="outline"
disabled={deleting} disabled={deleting}
> >

View File

@ -25,7 +25,7 @@ export function SelectFolderModal({
}: SelectFolderModalProps) { }: SelectFolderModalProps) {
const { t } = useTranslation("game_details"); const { t } = useTranslation("game_details");
const [diskFreeSpace, setDiskFreeSpace] = useState<DiskSpace>(null); const [diskFreeSpace, setDiskFreeSpace] = useState<DiskSpace | null>(null);
const [selectedPath, setSelectedPath] = useState(""); const [selectedPath, setSelectedPath] = useState("");
const [downloadStarting, setDownloadStarting] = useState(false); const [downloadStarting, setDownloadStarting] = useState(false);

View File

@ -24,7 +24,7 @@ export function SearchResults() {
const [searchResults, setSearchResults] = useState<CatalogueEntry[]>([]); const [searchResults, setSearchResults] = useState<CatalogueEntry[]>([]);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const debouncedFunc = useRef<DebouncedFunc<() => void | null>>(null); const debouncedFunc = useRef<DebouncedFunc<() => void> | null>(null);
const navigate = useNavigate(); const navigate = useNavigate();
@ -39,7 +39,7 @@ export function SearchResults() {
debouncedFunc.current = debounce(() => { debouncedFunc.current = debounce(() => {
window.electron window.electron
.searchGames(searchParams.get("query")) .searchGames(searchParams.get("query") ?? "")
.then((results) => { .then((results) => {
setSearchResults(results); setSearchResults(results);
}) })

View File

@ -23,10 +23,10 @@ export function Settings() {
setForm({ setForm({
downloadsPath: userPreferences?.downloadsPath || path, downloadsPath: userPreferences?.downloadsPath || path,
downloadNotificationsEnabled: downloadNotificationsEnabled:
userPreferences?.downloadNotificationsEnabled, userPreferences?.downloadNotificationsEnabled ?? false,
repackUpdatesNotificationsEnabled: repackUpdatesNotificationsEnabled:
userPreferences?.repackUpdatesNotificationsEnabled, userPreferences?.repackUpdatesNotificationsEnabled ?? false,
telemetryEnabled: userPreferences?.telemetryEnabled, telemetryEnabled: userPreferences?.telemetryEnabled ?? false,
}); });
}); });
}, []); }, []);

View File

@ -13,7 +13,8 @@
"@main/*": ["src/main/*"], "@main/*": ["src/main/*"],
"@renderer/*": ["src/renderer/*"], "@renderer/*": ["src/renderer/*"],
"@types": ["src/types/index.ts"], "@types": ["src/types/index.ts"],
"@locales": ["src/locales/index.ts"] "@locales": ["src/locales/index.ts"],
"@resources": ["src/resources/index.ts"]
} }
} }
} }

2421
yarn.lock

File diff suppressed because it is too large Load Diff