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", "enable_torbox": "Enable Torbox",
"torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.", "torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.",
"torbox_account_linked": "TorBox account linked", "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": { "notifications": {
"download_complete": "Download complete", "download_complete": "Download complete",

View file

@ -317,7 +317,8 @@
"enable_torbox": "Habilitar Torbox", "enable_torbox": "Habilitar Torbox",
"torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.", "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", "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": { "notifications": {
"download_complete": "Download concluído", "download_complete": "Download concluído",

View file

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

View file

@ -44,10 +44,9 @@ export function DownloadSettingsModal({
(state) => state.userPreferences.value (state) => state.userPreferences.value
); );
const getDiskFreeSpace = (path: string) => { const getDiskFreeSpace = async (path: string) => {
window.electron.getDiskFreeSpace(path).then((result) => { const result = await window.electron.getDiskFreeSpace(path);
setDiskFreeSpace(result.free); setDiskFreeSpace(result.free);
});
}; };
const checkFolderWritePermission = useCallback( const checkFolderWritePermission = useCallback(
@ -100,6 +99,7 @@ export function DownloadSettingsModal({
userPreferences?.downloadsPath, userPreferences?.downloadsPath,
downloaders, downloaders,
userPreferences?.realDebridApiToken, userPreferences?.realDebridApiToken,
userPreferences?.torBoxApiToken,
]); ]);
const handleChooseDownloadsPath = async () => { const handleChooseDownloadsPath = async () => {
@ -155,19 +155,21 @@ export function DownloadSettingsModal({
<span>{t("downloader")}</span> <span>{t("downloader")}</span>
<div className="download-settings-modal__downloaders"> <div className="download-settings-modal__downloaders">
{downloaders.map((downloader) => ( {downloaders.map((downloader) => {
const shouldDisableButton =
(downloader === Downloader.RealDebrid &&
!userPreferences?.realDebridApiToken) ||
(downloader === Downloader.TorBox &&
!userPreferences?.torBoxApiToken);
return (
<Button <Button
key={downloader} key={downloader}
className="download-settings-modal__downloader-option" className="download-settings-modal__downloader-option"
theme={ theme={
selectedDownloader === downloader ? "primary" : "outline" selectedDownloader === downloader ? "primary" : "outline"
} }
disabled={ disabled={shouldDisableButton}
(downloader === Downloader.RealDebrid &&
!userPreferences?.realDebridApiToken) ||
(downloader === Downloader.TorBox &&
!userPreferences?.torBoxApiToken)
}
onClick={() => setSelectedDownloader(downloader)} onClick={() => setSelectedDownloader(downloader)}
> >
{selectedDownloader === downloader && ( {selectedDownloader === downloader && (
@ -175,7 +177,8 @@ export function DownloadSettingsModal({
)} )}
{DOWNLOADER_NAME[downloader]} {DOWNLOADER_NAME[downloader]}
</Button> </Button>
))} );
})}
</div> </div>
</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 { TextField } from "@renderer/components/text-field/text-field";
import { Button } from "@renderer/components/button/button"; import { Button } from "@renderer/components/button/button";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useState } from "react";
import "./modals.scss"; import "./modals.scss";
import { useUserDetails } from "@renderer/hooks"; import { useUserDetails } from "@renderer/hooks";
import { Theme } from "@types"; 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 { interface AddThemeModalProps {
visible: boolean; visible: boolean;
@ -13,45 +17,54 @@ interface AddThemeModalProps {
onThemeAdded: () => void; onThemeAdded: () => void;
} }
export const AddThemeModal = ({ interface FormValues {
name: string;
}
export function AddThemeModal({
visible, visible,
onClose, onClose,
onThemeAdded, onThemeAdded,
}: AddThemeModalProps) => { }: Readonly<AddThemeModalProps>) {
const { t } = useTranslation("settings"); const { t } = useTranslation("settings");
const { userDetails } = useUserDetails(); const { userDetails } = useUserDetails();
const [name, setName] = useState("");
const [error, setError] = useState("");
const handleKeyDown = (event: React.KeyboardEvent) => { const schema = yup.object({
if (event.key === "Enter") { name: yup
handleSubmit(); .string()
} .required(t("required_field"))
}; .min(3, t("name_min_length")),
});
const handleSubmit = async () => { const {
if (!name || name.length < 3) { register,
setError(t("theme_name_error_hint")); handleSubmit,
return; reset,
} formState: { isSubmitting, errors },
} = useForm<FormValues>({
resolver: yupResolver(schema),
});
const onSubmit = useCallback(
async (values: FormValues) => {
const theme: Theme = { const theme: Theme = {
id: crypto.randomUUID(), id: crypto.randomUUID(),
name, name: values.name,
isActive: false, isActive: false,
author: userDetails?.id || undefined, author: userDetails?.id,
authorName: userDetails?.username || undefined, authorName: userDetails?.username,
code: "", code: "",
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
}; };
await window.electron.addCustomTheme(theme); await window.electron.addCustomTheme(theme);
setName("");
setError("");
onThemeAdded(); onThemeAdded();
onClose(); onClose();
}; reset();
},
[onClose, onThemeAdded, userDetails?.id, userDetails?.username, reset]
);
return ( return (
<Modal <Modal
@ -60,21 +73,23 @@ export const AddThemeModal = ({
description={t("create_theme_modal_description")} description={t("create_theme_modal_description")}
onClose={onClose} onClose={onClose}
> >
<div className="add-theme-modal__container"> <form className="add-theme-modal__container">
<TextField <TextField
{...register("name")}
label={t("theme_name")} label={t("theme_name")}
placeholder={t("insert_theme_name")} placeholder={t("insert_theme_name")}
value={name} hint={errors.name?.message}
onChange={(e) => setName(e.target.value)} error={errors.name?.message}
hint={error}
error={!!error}
onKeyDown={handleKeyDown}
/> />
<Button theme="primary" onClick={handleSubmit}> <Button
theme="primary"
onClick={handleSubmit(onSubmit)}
disabled={isSubmitting}
>
{t("create_theme")} {t("create_theme")}
</Button> </Button>
</div> </form>
</Modal> </Modal>
); );
}; }