Merge branch 'feature/custom-themes' of https://github.com/hydralauncher/hydra into feature/custom-themes

This commit is contained in:
Hachi-R 2025-02-16 01:28:08 -03:00
commit 3f6315f043
5 changed files with 96 additions and 73 deletions

View file

@ -327,7 +327,8 @@
"enable_torbox": "Enable Torbox",
"torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.",
"torbox_account_linked": "TorBox account linked",
"real_debrid_account_linked": "Real-Debrid account linked"
"real_debrid_account_linked": "Real-Debrid account linked",
"name_min_length": "Theme name must be at least 3 characters long"
},
"notifications": {
"download_complete": "Download complete",

View file

@ -317,7 +317,8 @@
"enable_torbox": "Habilitar Torbox",
"torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.",
"torbox_account_linked": "Conta do TorBox vinculada",
"real_debrid_account_linked": "Conta Real-Debrid associada"
"real_debrid_account_linked": "Conta Real-Debrid associada",
"name_min_length": "O nome do tema deve ter pelo menos 3 caracteres"
},
"notifications": {
"download_complete": "Download concluído",

View file

@ -230,14 +230,17 @@ export class DownloadManager {
}
static async cancelDownload(downloadKey = this.downloadingGameId) {
await PythonRPC.rpc.post("/action", {
action: "cancel",
game_id: downloadKey,
});
WindowManager.mainWindow?.setProgressBar(-1);
await PythonRPC.rpc
.post("/action", {
action: "cancel",
game_id: downloadKey,
})
.catch((err) => {
logger.error("Failed to cancel game download", err);
});
if (downloadKey === this.downloadingGameId) {
WindowManager.mainWindow?.setProgressBar(-1);
WindowManager.mainWindow?.webContents.send("on-download-progress", null);
this.downloadingGameId = null;
}

View file

@ -44,10 +44,9 @@ export function DownloadSettingsModal({
(state) => state.userPreferences.value
);
const getDiskFreeSpace = (path: string) => {
window.electron.getDiskFreeSpace(path).then((result) => {
setDiskFreeSpace(result.free);
});
const getDiskFreeSpace = async (path: string) => {
const result = await window.electron.getDiskFreeSpace(path);
setDiskFreeSpace(result.free);
};
const checkFolderWritePermission = useCallback(
@ -100,6 +99,7 @@ export function DownloadSettingsModal({
userPreferences?.downloadsPath,
downloaders,
userPreferences?.realDebridApiToken,
userPreferences?.torBoxApiToken,
]);
const handleChooseDownloadsPath = async () => {
@ -155,27 +155,30 @@ export function DownloadSettingsModal({
<span>{t("downloader")}</span>
<div className="download-settings-modal__downloaders">
{downloaders.map((downloader) => (
<Button
key={downloader}
className="download-settings-modal__downloader-option"
theme={
selectedDownloader === downloader ? "primary" : "outline"
}
disabled={
(downloader === Downloader.RealDebrid &&
!userPreferences?.realDebridApiToken) ||
(downloader === Downloader.TorBox &&
!userPreferences?.torBoxApiToken)
}
onClick={() => setSelectedDownloader(downloader)}
>
{selectedDownloader === downloader && (
<CheckCircleFillIcon className="download-settings-modal__downloader-icon" />
)}
{DOWNLOADER_NAME[downloader]}
</Button>
))}
{downloaders.map((downloader) => {
const shouldDisableButton =
(downloader === Downloader.RealDebrid &&
!userPreferences?.realDebridApiToken) ||
(downloader === Downloader.TorBox &&
!userPreferences?.torBoxApiToken);
return (
<Button
key={downloader}
className="download-settings-modal__downloader-option"
theme={
selectedDownloader === downloader ? "primary" : "outline"
}
disabled={shouldDisableButton}
onClick={() => setSelectedDownloader(downloader)}
>
{selectedDownloader === downloader && (
<CheckCircleFillIcon className="download-settings-modal__downloader-icon" />
)}
{DOWNLOADER_NAME[downloader]}
</Button>
);
})}
</div>
</div>

View file

@ -2,10 +2,14 @@ import { Modal } from "@renderer/components/modal/modal";
import { TextField } from "@renderer/components/text-field/text-field";
import { Button } from "@renderer/components/button/button";
import { useTranslation } from "react-i18next";
import { useState } from "react";
import "./modals.scss";
import { useUserDetails } from "@renderer/hooks";
import { Theme } from "@types";
import { useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useCallback } from "react";
interface AddThemeModalProps {
visible: boolean;
@ -13,45 +17,54 @@ interface AddThemeModalProps {
onThemeAdded: () => void;
}
export const AddThemeModal = ({
interface FormValues {
name: string;
}
export function AddThemeModal({
visible,
onClose,
onThemeAdded,
}: AddThemeModalProps) => {
}: Readonly<AddThemeModalProps>) {
const { t } = useTranslation("settings");
const { userDetails } = useUserDetails();
const [name, setName] = useState("");
const [error, setError] = useState("");
const handleKeyDown = (event: React.KeyboardEvent) => {
if (event.key === "Enter") {
handleSubmit();
}
};
const schema = yup.object({
name: yup
.string()
.required(t("required_field"))
.min(3, t("name_min_length")),
});
const handleSubmit = async () => {
if (!name || name.length < 3) {
setError(t("theme_name_error_hint"));
return;
}
const {
register,
handleSubmit,
reset,
formState: { isSubmitting, errors },
} = useForm<FormValues>({
resolver: yupResolver(schema),
});
const theme: Theme = {
id: crypto.randomUUID(),
name,
isActive: false,
author: userDetails?.id || undefined,
authorName: userDetails?.username || undefined,
code: "",
createdAt: new Date(),
updatedAt: new Date(),
};
const onSubmit = useCallback(
async (values: FormValues) => {
const theme: Theme = {
id: crypto.randomUUID(),
name: values.name,
isActive: false,
author: userDetails?.id,
authorName: userDetails?.username,
code: "",
createdAt: new Date(),
updatedAt: new Date(),
};
await window.electron.addCustomTheme(theme);
setName("");
setError("");
onThemeAdded();
onClose();
};
await window.electron.addCustomTheme(theme);
onThemeAdded();
onClose();
reset();
},
[onClose, onThemeAdded, userDetails?.id, userDetails?.username, reset]
);
return (
<Modal
@ -60,21 +73,23 @@ export const AddThemeModal = ({
description={t("create_theme_modal_description")}
onClose={onClose}
>
<div className="add-theme-modal__container">
<form className="add-theme-modal__container">
<TextField
{...register("name")}
label={t("theme_name")}
placeholder={t("insert_theme_name")}
value={name}
onChange={(e) => setName(e.target.value)}
hint={error}
error={!!error}
onKeyDown={handleKeyDown}
hint={errors.name?.message}
error={errors.name?.message}
/>
<Button theme="primary" onClick={handleSubmit}>
<Button
theme="primary"
onClick={handleSubmit(onSubmit)}
disabled={isSubmitting}
>
{t("create_theme")}
</Button>
</div>
</form>
</Modal>
);
};
}