Skip to content

Commit 59bc23b

Browse files
authored
Merge pull request #1367 from hydralauncher/fix/issues
fix: issues
2 parents 297ca5a + 16b50fc commit 59bc23b

30 files changed

+199
-106
lines changed

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hydralauncher",
3-
"version": "3.1.4",
3+
"version": "3.1.5",
44
"description": "Hydra",
55
"main": "./out/main/index.js",
66
"author": "Los Broxas",
@@ -47,13 +47,13 @@
4747
"auto-launch": "^5.0.6",
4848
"axios": "^1.7.9",
4949
"better-sqlite3": "^11.7.0",
50-
"check-disk-space": "^3.4.0",
5150
"classnames": "^2.5.1",
5251
"color": "^4.2.3",
5352
"color.js": "^1.2.0",
5453
"create-desktop-shortcuts": "^1.11.0",
5554
"date-fns": "^3.6.0",
5655
"dexie": "^4.0.10",
56+
"diskusage": "^1.2.0",
5757
"electron-log": "^5.2.4",
5858
"electron-updater": "^6.3.9",
5959
"file-type": "^19.6.0",

src/locales/bg/translation.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
},
5959
"game_details": {
6060
"launch_options": "Опции за стартиране",
61-
"launch_options_description": "Напредналите потребители могат да въведат модификации на своите опции за стартиране",
61+
"launch_options_description": "Напредналите потребители могат да въведат модификации на своите опции за стартиране (экспериментальный)",
6262
"launch_options_placeholder": "Няма зададен параметър",
6363
"open_download_options": "Варианти за изтегляне",
6464
"download_options_zero": "Няма варианти за изтегляне",

src/locales/en/translation.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@
168168
"wine_prefix": "Wine Prefix",
169169
"wine_prefix_description": "The Wine prefix used to run this game",
170170
"launch_options": "Launch Options",
171-
"launch_options_description": "Advanced users may choose to enter modifications to their launch options",
171+
"launch_options_description": "Advanced users may choose to enter modifications to their launch options (experimental feature)",
172172
"launch_options_placeholder": "No parameter specified",
173173
"no_download_option_info": "No information available",
174174
"backup_deletion_failed": "Failed to delete backup",
@@ -178,7 +178,8 @@
178178
"select_folder": "Select folder",
179179
"backup_from": "Backup from {{date}}",
180180
"custom_backup_location_set": "Custom backup location set",
181-
"no_directory_selected": "No directory selected"
181+
"no_directory_selected": "No directory selected",
182+
"no_write_permission": "Cannot download into this directory. Click here to learn more."
182183
},
183184
"activation": {
184185
"title": "Activate Hydra",

src/locales/pt-BR/translation.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@
156156
"wine_prefix": "Prefixo Wine",
157157
"wine_prefix_description": "O prefixo Wine que foi utilizado para instalar o jogo",
158158
"launch_options": "Opções de Inicialização",
159-
"launch_options_description": "Usuários avançados podem adicionar opções de inicialização no jogo",
159+
"launch_options_description": "Usuários avançados podem adicionar opções de inicialização no jogo (experimental)",
160160
"launch_options_placeholder": "Nenhum parâmetro informado",
161161
"no_download_option_info": "Sem informações disponíveis",
162162
"backup_deletion_failed": "Falha ao apagar backup",
@@ -167,7 +167,8 @@
167167
"select_folder": "Selecione a pasta",
168168
"manage_files_description": "Gerencie quais arquivos serão feitos backup",
169169
"clear": "Limpar",
170-
"no_directory_selected": "Nenhum diretório selecionado"
170+
"no_directory_selected": "Nenhum diretório selecionado",
171+
"no_write_permission": "O download não pode ser feito neste diretório. Clique aqui para saber mais."
171172
},
172173
"activation": {
173174
"title": "Ativação",

src/main/events/auth/get-session-hash.ts

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const getSessionHash = async (_event: Electron.IpcMainInvokeEvent) => {
99
if (!auth) return null;
1010
const payload = jwt.decode(auth.accessToken) as jwt.JwtPayload;
1111

12+
if (!payload) return null;
13+
1214
return payload.sessionId;
1315
};
1416

src/main/events/cloud-save/get-game-artifacts.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { HydraApi } from "@main/services";
22
import { registerEvent } from "../register-event";
33
import type { GameArtifact, GameShop } from "@types";
4-
import { SubscriptionRequiredError } from "@shared";
4+
import { SubscriptionRequiredError, UserNotLoggedInError } from "@shared";
55

66
const getGameArtifacts = async (
77
_event: Electron.IpcMainInvokeEvent,
@@ -22,6 +22,10 @@ const getGameArtifacts = async (
2222
return [];
2323
}
2424

25+
if (err instanceof UserNotLoggedInError) {
26+
return [];
27+
}
28+
2529
throw err;
2630
});
2731
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import fs from "node:fs";
2+
3+
import { registerEvent } from "../register-event";
4+
5+
const checkFolderWritePermission = async (
6+
_event: Electron.IpcMainInvokeEvent,
7+
path: string
8+
) =>
9+
new Promise((resolve) => {
10+
fs.access(path, fs.constants.W_OK, (err) => {
11+
resolve(!err);
12+
});
13+
});
14+
15+
registerEvent("checkFolderWritePermission", checkFolderWritePermission);
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import checkDiskSpace from "check-disk-space";
1+
import disk from "diskusage";
22

33
import { registerEvent } from "../register-event";
44

55
const getDiskFreeSpace = async (
66
_event: Electron.IpcMainInvokeEvent,
77
path: string
8-
) => checkDiskSpace(path);
8+
) => disk.check(path);
99

1010
registerEvent("getDiskFreeSpace", getDiskFreeSpace);

src/main/events/helpers/parse-launch-options.ts

-9
This file was deleted.

src/main/events/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import "./catalogue/get-trending-games";
1111
import "./catalogue/get-publishers";
1212
import "./catalogue/get-developers";
1313
import "./hardware/get-disk-free-space";
14+
import "./hardware/check-folder-write-permission";
1415
import "./library/add-game-to-library";
1516
import "./library/create-game-shortcut";
1617
import "./library/close-game";
@@ -30,6 +31,8 @@ import "./library/select-game-wine-prefix";
3031
import "./misc/open-checkout";
3132
import "./misc/open-external";
3233
import "./misc/show-open-dialog";
34+
import "./misc/get-features";
35+
import "./misc/show-item-in-folder";
3336
import "./torrenting/cancel-game-download";
3437
import "./torrenting/pause-game-download";
3538
import "./torrenting/resume-game-download";
@@ -71,7 +74,6 @@ import "./cloud-save/delete-game-artifact";
7174
import "./cloud-save/select-game-backup-path";
7275
import "./notifications/publish-new-repacks-notification";
7376
import { isPortableVersion } from "@main/helpers";
74-
import "./misc/show-item-in-folder";
7577

7678
ipcMain.handle("ping", () => "pong");
7779
ipcMain.handle("getVersion", () => appVersion);

src/main/events/library/open-game.ts

+2-10
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,23 @@ import { gameRepository } from "@main/repository";
22

33
import { registerEvent } from "../register-event";
44
import { shell } from "electron";
5-
import { spawn } from "child_process";
65
import { parseExecutablePath } from "../helpers/parse-executable-path";
7-
import { parseLaunchOptions } from "../helpers/parse-launch-options";
86

97
const openGame = async (
108
_event: Electron.IpcMainInvokeEvent,
119
gameId: number,
1210
executablePath: string,
1311
launchOptions: string | null
1412
) => {
13+
// TODO: revisit this for launchOptions
1514
const parsedPath = parseExecutablePath(executablePath);
16-
const parsedParams = parseLaunchOptions(launchOptions);
1715

1816
await gameRepository.update(
1917
{ id: gameId },
2018
{ executablePath: parsedPath, launchOptions }
2119
);
2220

23-
if (process.platform === "linux" || process.platform === "darwin") {
24-
shell.openPath(parsedPath);
25-
}
26-
27-
if (process.platform === "win32") {
28-
spawn(parsedPath, parsedParams, { shell: false, detached: true });
29-
}
21+
shell.openPath(parsedPath);
3022
};
3123

3224
registerEvent("openGame", openGame);

src/main/events/misc/get-features.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { registerEvent } from "../register-event";
2+
import { HydraApi } from "@main/services";
3+
4+
const getFeatures = async (_event: Electron.IpcMainInvokeEvent) => {
5+
return HydraApi.get<string[]>("/features", null, { needsAuth: false });
6+
};
7+
8+
registerEvent("getFeatures", getFeatures);

src/main/events/misc/open-checkout.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
import { shell } from "electron";
22
import { registerEvent } from "../register-event";
3-
import {
4-
userAuthRepository,
5-
userPreferencesRepository,
6-
} from "@main/repository";
3+
import { userAuthRepository } from "@main/repository";
74
import { HydraApi } from "@main/services";
85

96
const openCheckout = async (_event: Electron.IpcMainInvokeEvent) => {
10-
const [userAuth, userPreferences] = await Promise.all([
11-
userAuthRepository.findOne({ where: { id: 1 } }),
12-
userPreferencesRepository.findOne({ where: { id: 1 } }),
13-
]);
7+
const userAuth = await userAuthRepository.findOne({ where: { id: 1 } });
148

159
if (!userAuth) {
1610
return;
@@ -22,7 +16,6 @@ const openCheckout = async (_event: Electron.IpcMainInvokeEvent) => {
2216

2317
const params = new URLSearchParams({
2418
token: paymentToken,
25-
lng: userPreferences?.language || "en",
2619
});
2720

2821
shell.openExternal(

src/main/services/window-manager.ts

+9-10
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ export class WindowManager {
6464

6565
this.mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
6666
(details, callback) => {
67-
if (details.webContentsId !== this.mainWindow?.webContents.id) {
67+
if (
68+
details.webContentsId !== this.mainWindow?.webContents.id ||
69+
details.url.includes("chatwoot")
70+
) {
6871
return callback(details);
6972
}
7073

@@ -81,15 +84,11 @@ export class WindowManager {
8184

8285
this.mainWindow.webContents.session.webRequest.onHeadersReceived(
8386
(details, callback) => {
84-
if (details.webContentsId !== this.mainWindow?.webContents.id) {
85-
return callback(details);
86-
}
87-
88-
if (details.url.includes("featurebase")) {
89-
return callback(details);
90-
}
91-
92-
if (details.url.includes("chatwoot")) {
87+
if (
88+
details.webContentsId !== this.mainWindow?.webContents.id ||
89+
details.url.includes("featurebase") ||
90+
details.url.includes("chatwoot")
91+
) {
9392
return callback(details);
9493
}
9594

src/preload/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ contextBridge.exposeInMainWorld("electron", {
150150
/* Hardware */
151151
getDiskFreeSpace: (path: string) =>
152152
ipcRenderer.invoke("getDiskFreeSpace", path),
153+
checkFolderWritePermission: (path: string) =>
154+
ipcRenderer.invoke("checkFolderWritePermission", path),
153155

154156
/* Cloud save */
155157
uploadSaveGame: (
@@ -226,6 +228,7 @@ contextBridge.exposeInMainWorld("electron", {
226228
ipcRenderer.invoke("showOpenDialog", options),
227229
showItemInFolder: (path: string) =>
228230
ipcRenderer.invoke("showItemInFolder", path),
231+
getFeatures: () => ipcRenderer.invoke("getFeatures"),
229232
platform: process.platform,
230233

231234
/* Auto update */

src/renderer/src/components/modal/modal.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ export function Modal({
4646
}, [onClose]);
4747

4848
const isTopMostModal = () => {
49+
if (
50+
document.querySelector(
51+
".featurebase-widget-overlay.featurebase-display-block"
52+
)
53+
)
54+
return false;
4955
const openModals = document.querySelectorAll("[role=dialog]");
5056

5157
return (

src/renderer/src/components/text-field/text-field.tsx

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, { useId, useMemo, useState } from "react";
22
import type { RecipeVariants } from "@vanilla-extract/recipes";
3-
import type { FieldError, FieldErrorsImpl, Merge } from "react-hook-form";
43
import { EyeClosedIcon, EyeIcon } from "@primer/octicons-react";
54
import { useTranslation } from "react-i18next";
65

@@ -23,7 +22,7 @@ export interface TextFieldProps
2322
HTMLDivElement
2423
>;
2524
rightContent?: React.ReactNode | null;
26-
error?: FieldError | Merge<FieldError, FieldErrorsImpl<any>> | undefined;
25+
error?: string | React.ReactNode;
2726
}
2827

2928
export const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
@@ -55,10 +54,7 @@ export const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
5554
}, [props.type, isPasswordVisible]);
5655

5756
const hintContent = useMemo(() => {
58-
if (error && error.message)
59-
return (
60-
<small className={styles.errorLabel}>{error.message as string}</small>
61-
);
57+
if (error) return <small className={styles.errorLabel}>{error}</small>;
6258

6359
if (hint) return <small>{hint}</small>;
6460
return null;

src/renderer/src/context/game-details/game-details.context.tsx

+3-7
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,7 @@ export function GameDetailsContextProvider({
117117
abortControllerRef.current = abortController;
118118

119119
window.electron
120-
.getGameShopDetails(
121-
objectId!,
122-
shop as GameShop,
123-
getSteamLanguage(i18n.language)
124-
)
120+
.getGameShopDetails(objectId, shop, getSteamLanguage(i18n.language))
125121
.then((result) => {
126122
if (abortController.signal.aborted) return;
127123

@@ -140,14 +136,14 @@ export function GameDetailsContextProvider({
140136
setIsLoading(false);
141137
});
142138

143-
window.electron.getGameStats(objectId, shop as GameShop).then((result) => {
139+
window.electron.getGameStats(objectId, shop).then((result) => {
144140
if (abortController.signal.aborted) return;
145141
setStats(result);
146142
});
147143

148144
if (userDetails) {
149145
window.electron
150-
.getUnlockedAchievements(objectId, shop as GameShop)
146+
.getUnlockedAchievements(objectId, shop)
151147
.then((achievements) => {
152148
if (abortController.signal.aborted) return;
153149
setAchievements(achievements);

src/renderer/src/declaration.d.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import type {
3131
CatalogueSearchPayload,
3232
} from "@types";
3333
import type { AxiosProgressEvent } from "axios";
34-
import type { DiskSpace } from "check-disk-space";
34+
import type disk from "diskusage";
3535

3636
declare global {
3737
declare module "*.svg" {
@@ -140,7 +140,8 @@ declare global {
140140
) => Promise<{ fingerprint: string }>;
141141

142142
/* Hardware */
143-
getDiskFreeSpace: (path: string) => Promise<DiskSpace>;
143+
getDiskFreeSpace: (path: string) => Promise<disk.DiskUsage>;
144+
checkFolderWritePermission: (path: string) => Promise<boolean>;
144145

145146
/* Cloud save */
146147
uploadSaveGame: (
@@ -195,6 +196,7 @@ declare global {
195196
options: Electron.OpenDialogOptions
196197
) => Promise<Electron.OpenDialogReturnValue>;
197198
showItemInFolder: (path: string) => Promise<void>;
199+
getFeatures: () => Promise<string[]>;
198200
platform: NodeJS.Platform;
199201

200202
/* Auto update */

src/renderer/src/hooks/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from "./redux";
66
export * from "./use-user-details";
77
export * from "./use-format";
88
export * from "./use-repacks";
9+
export * from "./use-feature";

0 commit comments

Comments
 (0)