mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
chore: disabling resuming real debrid downloads when real debrid is not set
This commit is contained in:
parent
4d61b8586d
commit
a34a774fb7
17 changed files with 83 additions and 311 deletions
|
@ -103,7 +103,9 @@
|
||||||
"previous_screenshot": "Previous screenshot",
|
"previous_screenshot": "Previous screenshot",
|
||||||
"next_screenshot": "Next screenshot",
|
"next_screenshot": "Next screenshot",
|
||||||
"screenshot": "Screenshot {{number}}",
|
"screenshot": "Screenshot {{number}}",
|
||||||
"open_screenshot": "Open screenshot {{number}}"
|
"open_screenshot": "Open screenshot {{number}}",
|
||||||
|
"download_settings": "Download settings",
|
||||||
|
"downloader": "Downloader"
|
||||||
},
|
},
|
||||||
"activation": {
|
"activation": {
|
||||||
"title": "Activate Hydra",
|
"title": "Activate Hydra",
|
||||||
|
@ -144,7 +146,7 @@
|
||||||
"telemetry": "Telemetry",
|
"telemetry": "Telemetry",
|
||||||
"telemetry_description": "Enable anonymous usage statistics",
|
"telemetry_description": "Enable anonymous usage statistics",
|
||||||
"real_debrid_api_token_label": "Real-Debrid API token",
|
"real_debrid_api_token_label": "Real-Debrid API token",
|
||||||
"quit_app_instead_hiding": "Quit Hydra instead of minimizing to tray",
|
"quit_app_instead_hiding": "Don't hide Hydra when closing",
|
||||||
"launch_with_system": "Launch Hydra on system start-up",
|
"launch_with_system": "Launch Hydra on system start-up",
|
||||||
"general": "General",
|
"general": "General",
|
||||||
"behavior": "Behavior",
|
"behavior": "Behavior",
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
"playing_now": "Jogando agora",
|
"playing_now": "Jogando agora",
|
||||||
"change": "Mudar",
|
"change": "Mudar",
|
||||||
"repacks_modal_description": "Escolha o repack do jogo que deseja baixar",
|
"repacks_modal_description": "Escolha o repack do jogo que deseja baixar",
|
||||||
"select_folder_hint": "Para trocar a pasta padrão, acesse a <0>Tela de Configurações</0>",
|
"select_folder_hint": "Para trocar o diretório padrão, acesse a <0>Tela de Configurações</0>",
|
||||||
"download_now": "Iniciar download",
|
"download_now": "Iniciar download",
|
||||||
"installation_instructions": "Instruções de Instalação",
|
"installation_instructions": "Instruções de Instalação",
|
||||||
"installation_instructions_description": "Passos adicionais são necessários para instalar esse jogo",
|
"installation_instructions_description": "Passos adicionais são necessários para instalar esse jogo",
|
||||||
|
@ -99,7 +99,9 @@
|
||||||
"previous_screenshot": "Captura de tela anterior",
|
"previous_screenshot": "Captura de tela anterior",
|
||||||
"next_screenshot": "Próxima captura de tela",
|
"next_screenshot": "Próxima captura de tela",
|
||||||
"screenshot": "Captura de tela {{number}}",
|
"screenshot": "Captura de tela {{number}}",
|
||||||
"open_screenshot": "Ver captura de tela {{number}}"
|
"open_screenshot": "Ver captura de tela {{number}}",
|
||||||
|
"download_settings": "Ajustes do download",
|
||||||
|
"downloader": "Downloader"
|
||||||
},
|
},
|
||||||
"activation": {
|
"activation": {
|
||||||
"title": "Ativação",
|
"title": "Ativação",
|
||||||
|
@ -140,8 +142,8 @@
|
||||||
"telemetry": "Telemetria",
|
"telemetry": "Telemetria",
|
||||||
"telemetry_description": "Habilitar estatísticas de uso anônimas",
|
"telemetry_description": "Habilitar estatísticas de uso anônimas",
|
||||||
"real_debrid_api_token_label": "Token de API do Real-Debrid",
|
"real_debrid_api_token_label": "Token de API do Real-Debrid",
|
||||||
"quit_app_instead_hiding": "Fechar o aplicativo em vez de minimizá-lo",
|
"quit_app_instead_hiding": "Não manter o Hydra em segundo plano ao fechar",
|
||||||
"launch_with_system": "Iniciar aplicativo na inicialização do sistema",
|
"launch_with_system": "Iniciar o Hydra junto com o sistema",
|
||||||
"general": "Geral",
|
"general": "Geral",
|
||||||
"behavior": "Comportamento",
|
"behavior": "Comportamento",
|
||||||
"real_debrid_api_token": "Token de API",
|
"real_debrid_api_token": "Token de API",
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
|
|
||||||
|
import { In } from "typeorm";
|
||||||
|
|
||||||
import { gameRepository } from "@main/repository";
|
import { gameRepository } from "@main/repository";
|
||||||
|
|
||||||
import { getDownloadsPath } from "../helpers/get-downloads-path";
|
import { getDownloadsPath } from "../helpers/get-downloads-path";
|
||||||
|
@ -14,7 +16,7 @@ const deleteGameFolder = async (
|
||||||
const game = await gameRepository.findOne({
|
const game = await gameRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
id: gameId,
|
id: gameId,
|
||||||
status: "removed",
|
status: In(["removed", "complete"]),
|
||||||
isDeleted: false,
|
isDeleted: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -41,12 +41,11 @@ export function useDownload() {
|
||||||
return updateLibrary();
|
return updateLibrary();
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelDownload = (gameId: number) =>
|
const cancelDownload = async (gameId: number) => {
|
||||||
window.electron.cancelGameDownload(gameId).then(() => {
|
await window.electron.cancelGameDownload(gameId);
|
||||||
dispatch(clearDownload());
|
dispatch(clearDownload());
|
||||||
updateLibrary();
|
updateLibrary();
|
||||||
deleteGame(gameId);
|
};
|
||||||
});
|
|
||||||
|
|
||||||
const removeGameFromLibrary = (gameId: number) =>
|
const removeGameFromLibrary = (gameId: number) =>
|
||||||
window.electron.removeGameFromLibrary(gameId).then(() => {
|
window.electron.removeGameFromLibrary(gameId).then(() => {
|
||||||
|
@ -67,18 +66,15 @@ export function useDownload() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteGame = (gameId: number) =>
|
const deleteGame = async (gameId: number) => {
|
||||||
window.electron
|
dispatch(setGameDeleting(gameId));
|
||||||
.cancelGameDownload(gameId)
|
|
||||||
.then(() => {
|
try {
|
||||||
dispatch(setGameDeleting(gameId));
|
await window.electron.deleteGameFolder(gameId);
|
||||||
return window.electron.deleteGameFolder(gameId);
|
} finally {
|
||||||
})
|
dispatch(removeGameFromDeleting(gameId));
|
||||||
.catch(() => {})
|
}
|
||||||
.finally(() => {
|
};
|
||||||
updateLibrary();
|
|
||||||
dispatch(removeGameFromDeleting(gameId));
|
|
||||||
});
|
|
||||||
|
|
||||||
const isGameDeleting = (gameId: number) => {
|
const isGameDeleting = (gameId: number) => {
|
||||||
return gamesWithDeletionInProgress.includes(gameId);
|
return gamesWithDeletionInProgress.includes(gameId);
|
||||||
|
|
|
@ -40,7 +40,6 @@ i18n
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
window.electron.updateUserPreferences({ language: i18n.language });
|
window.electron.updateUserPreferences({ language: i18n.language });
|
||||||
i18n.changeLanguage("pt-BR");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
import { SPACING_UNIT } from "../../theme.css";
|
|
||||||
import { style } from "@vanilla-extract/css";
|
|
||||||
|
|
||||||
export const deleteActionsButtonsCtn = style({
|
|
||||||
display: "flex",
|
|
||||||
width: "100%",
|
|
||||||
justifyContent: "end",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: `${SPACING_UNIT}px`,
|
|
||||||
});
|
|
|
@ -1,43 +0,0 @@
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { Button, Modal } from "@renderer/components";
|
|
||||||
|
|
||||||
import * as styles from "./delete-modal.css";
|
|
||||||
|
|
||||||
interface DeleteModalProps {
|
|
||||||
visible: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
deleteGame: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DeleteModal({
|
|
||||||
onClose,
|
|
||||||
visible,
|
|
||||||
deleteGame,
|
|
||||||
}: DeleteModalProps) {
|
|
||||||
const { t } = useTranslation("downloads");
|
|
||||||
|
|
||||||
const handleDeleteGame = () => {
|
|
||||||
deleteGame();
|
|
||||||
onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
visible={visible}
|
|
||||||
title={t("delete_modal_title")}
|
|
||||||
description={t("delete_modal_description")}
|
|
||||||
onClose={onClose}
|
|
||||||
>
|
|
||||||
<div className={styles.deleteActionsButtonsCtn}>
|
|
||||||
<Button onClick={handleDeleteGame} theme="outline">
|
|
||||||
{t("delete")}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button onClick={onClose} theme="primary">
|
|
||||||
{t("cancel")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -7,13 +7,13 @@ import {
|
||||||
formatDownloadProgress,
|
formatDownloadProgress,
|
||||||
steamUrlBuilder,
|
steamUrlBuilder,
|
||||||
} from "@renderer/helpers";
|
} from "@renderer/helpers";
|
||||||
import { useDownload, useLibrary } from "@renderer/hooks";
|
import { useAppSelector, useDownload, useLibrary } from "@renderer/hooks";
|
||||||
import type { Game } from "@types";
|
import type { Game } from "@types";
|
||||||
|
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal";
|
import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal";
|
||||||
import * as styles from "./downloads.css";
|
import * as styles from "./downloads.css";
|
||||||
import { DeleteModal } from "./delete-modal";
|
import { DeleteGameModal } from "./delete-game-modal";
|
||||||
import { Downloader, formatBytes } from "@shared";
|
import { Downloader, formatBytes } from "@shared";
|
||||||
import { DOWNLOADER_NAME } from "@renderer/constants";
|
import { DOWNLOADER_NAME } from "@renderer/constants";
|
||||||
|
|
||||||
|
@ -22,6 +22,10 @@ export function Downloads() {
|
||||||
|
|
||||||
const { t } = useTranslation("downloads");
|
const { t } = useTranslation("downloads");
|
||||||
|
|
||||||
|
const userPreferences = useAppSelector(
|
||||||
|
(state) => state.userPreferences.value
|
||||||
|
);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const gameToBeDeleted = useRef<number | null>(null);
|
const gameToBeDeleted = useRef<number | null>(null);
|
||||||
|
@ -171,7 +175,14 @@ export function Downloads() {
|
||||||
if (game.status === "paused") {
|
if (game.status === "paused") {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button onClick={() => resumeDownload(game.id)} theme="outline">
|
<Button
|
||||||
|
onClick={() => resumeDownload(game.id)}
|
||||||
|
theme="outline"
|
||||||
|
disabled={
|
||||||
|
game.downloader === Downloader.RealDebrid &&
|
||||||
|
!userPreferences?.realDebridApiToken
|
||||||
|
}
|
||||||
|
>
|
||||||
{t("resume")}
|
{t("resume")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => cancelDownload(game.id)} theme="outline">
|
<Button onClick={() => cancelDownload(game.id)} theme="outline">
|
||||||
|
@ -212,9 +223,10 @@ export function Downloads() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteGame = () => {
|
const handleDeleteGame = async () => {
|
||||||
if (gameToBeDeleted.current) {
|
if (gameToBeDeleted.current) {
|
||||||
deleteGame(gameToBeDeleted.current).then(updateLibrary);
|
await deleteGame(gameToBeDeleted.current);
|
||||||
|
await removeGameFromLibrary(gameToBeDeleted.current);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -224,7 +236,8 @@ export function Downloads() {
|
||||||
visible={showBinaryNotFoundModal}
|
visible={showBinaryNotFoundModal}
|
||||||
onClose={() => setShowBinaryNotFoundModal(false)}
|
onClose={() => setShowBinaryNotFoundModal(false)}
|
||||||
/>
|
/>
|
||||||
<DeleteModal
|
|
||||||
|
<DeleteGameModal
|
||||||
visible={showDeleteModal}
|
visible={showDeleteModal}
|
||||||
onClose={() => setShowDeleteModal(false)}
|
onClose={() => setShowDeleteModal(false)}
|
||||||
deleteGame={handleDeleteGame}
|
deleteGame={handleDeleteGame}
|
||||||
|
|
|
@ -3,12 +3,13 @@ import { NoEntryIcon, PlusCircleIcon } from "@primer/octicons-react";
|
||||||
import { BinaryNotFoundModal } from "../../shared-modals/binary-not-found-modal";
|
import { BinaryNotFoundModal } from "../../shared-modals/binary-not-found-modal";
|
||||||
|
|
||||||
import { Button } from "@renderer/components";
|
import { Button } from "@renderer/components";
|
||||||
import { useDownload, useLibrary } from "@renderer/hooks";
|
import { useAppSelector, useDownload, useLibrary } from "@renderer/hooks";
|
||||||
import { useContext, useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import * as styles from "./hero-panel-actions.css";
|
import * as styles from "./hero-panel-actions.css";
|
||||||
import { gameDetailsContext } from "../game-details.context";
|
import { gameDetailsContext } from "../game-details.context";
|
||||||
|
import { Downloader } from "@shared";
|
||||||
|
|
||||||
export function HeroPanelActions() {
|
export function HeroPanelActions() {
|
||||||
const [toggleLibraryGameDisabled, setToggleLibraryGameDisabled] =
|
const [toggleLibraryGameDisabled, setToggleLibraryGameDisabled] =
|
||||||
|
@ -33,12 +34,15 @@ export function HeroPanelActions() {
|
||||||
updateGame,
|
updateGame,
|
||||||
} = useContext(gameDetailsContext);
|
} = useContext(gameDetailsContext);
|
||||||
|
|
||||||
|
const userPreferences = useAppSelector(
|
||||||
|
(state) => state.userPreferences.value
|
||||||
|
);
|
||||||
|
|
||||||
const { updateLibrary } = useLibrary();
|
const { updateLibrary } = useLibrary();
|
||||||
|
|
||||||
const { t } = useTranslation("game_details");
|
const { t } = useTranslation("game_details");
|
||||||
|
|
||||||
const getDownloadsPath = async () => {
|
const getDownloadsPath = async () => {
|
||||||
const userPreferences = await window.electron.getUserPreferences();
|
|
||||||
if (userPreferences?.downloadsPath) return userPreferences.downloadsPath;
|
if (userPreferences?.downloadsPath) return userPreferences.downloadsPath;
|
||||||
return window.electron.getDefaultDownloadsPath();
|
return window.electron.getDefaultDownloadsPath();
|
||||||
};
|
};
|
||||||
|
@ -202,6 +206,10 @@ export function HeroPanelActions() {
|
||||||
onClick={() => resumeDownload(game.id).then(updateGame)}
|
onClick={() => resumeDownload(game.id).then(updateGame)}
|
||||||
theme="outline"
|
theme="outline"
|
||||||
className={styles.heroPanelAction}
|
className={styles.heroPanelAction}
|
||||||
|
disabled={
|
||||||
|
game.downloader === Downloader.RealDebrid &&
|
||||||
|
!userPreferences?.realDebridApiToken
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("resume")}
|
{t("resume")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export * from "./installation-guides";
|
export * from "./installation-guides";
|
||||||
export * from "./repacks-modal";
|
export * from "./repacks-modal";
|
||||||
export * from "./select-folder-modal";
|
export * from "./download-settings-modal";
|
||||||
|
|
|
@ -8,7 +8,7 @@ import * as styles from "./repacks-modal.css";
|
||||||
|
|
||||||
import { SPACING_UNIT } from "../../../theme.css";
|
import { SPACING_UNIT } from "../../../theme.css";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { SelectFolderModal } from "./select-folder-modal";
|
import { DownloadSettingsModal } from "./download-settings-modal";
|
||||||
import { gameDetailsContext } from "../game-details.context";
|
import { gameDetailsContext } from "../game-details.context";
|
||||||
import { Downloader } from "@shared";
|
import { Downloader } from "@shared";
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ export function RepacksModal({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SelectFolderModal
|
<DownloadSettingsModal
|
||||||
visible={showSelectFolderModal}
|
visible={showSelectFolderModal}
|
||||||
onClose={() => setShowSelectFolderModal(false)}
|
onClose={() => setShowSelectFolderModal(false)}
|
||||||
startDownload={startDownload}
|
startDownload={startDownload}
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
import { style } from "@vanilla-extract/css";
|
|
||||||
import { SPACING_UNIT, vars } from "../../../theme.css";
|
|
||||||
|
|
||||||
export const container = style({
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: `${SPACING_UNIT * 2}px`,
|
|
||||||
width: "100%",
|
|
||||||
});
|
|
||||||
|
|
||||||
export const downloadsPathField = style({
|
|
||||||
display: "flex",
|
|
||||||
gap: `${SPACING_UNIT}px`,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const hintText = style({
|
|
||||||
fontSize: "12px",
|
|
||||||
color: vars.color.body,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const downloaders = style({
|
|
||||||
display: "flex",
|
|
||||||
gap: `${SPACING_UNIT}px`,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const downloaderOption = style({
|
|
||||||
flex: "1",
|
|
||||||
position: "relative",
|
|
||||||
});
|
|
||||||
|
|
||||||
export const downloaderIcon = style({
|
|
||||||
position: "absolute",
|
|
||||||
left: `${SPACING_UNIT * 2}px`,
|
|
||||||
});
|
|
|
@ -1,157 +0,0 @@
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { DiskSpace } from "check-disk-space";
|
|
||||||
import * as styles from "./select-folder-modal.css";
|
|
||||||
import { Button, Link, Modal, TextField } from "@renderer/components";
|
|
||||||
import { CheckCircleFillIcon, DownloadIcon } from "@primer/octicons-react";
|
|
||||||
import { Downloader, formatBytes } from "@shared";
|
|
||||||
|
|
||||||
import type { GameRepack } from "@types";
|
|
||||||
import { SPACING_UNIT } from "@renderer/theme.css";
|
|
||||||
import { DOWNLOADER_NAME } from "@renderer/constants";
|
|
||||||
|
|
||||||
export interface SelectFolderModalProps {
|
|
||||||
visible: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
startDownload: (
|
|
||||||
repack: GameRepack,
|
|
||||||
downloader: Downloader,
|
|
||||||
downloadPath: string
|
|
||||||
) => Promise<void>;
|
|
||||||
repack: GameRepack | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const downloaders = [Downloader.Torrent, Downloader.RealDebrid];
|
|
||||||
|
|
||||||
export function SelectFolderModal({
|
|
||||||
visible,
|
|
||||||
onClose,
|
|
||||||
startDownload,
|
|
||||||
repack,
|
|
||||||
}: SelectFolderModalProps) {
|
|
||||||
const { t } = useTranslation("game_details");
|
|
||||||
|
|
||||||
const [diskFreeSpace, setDiskFreeSpace] = useState<DiskSpace | null>(null);
|
|
||||||
const [selectedPath, setSelectedPath] = useState("");
|
|
||||||
const [downloadStarting, setDownloadStarting] = useState(false);
|
|
||||||
const [selectedDownloader, setSelectedDownloader] = useState(
|
|
||||||
Downloader.Torrent
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
getDiskFreeSpace(selectedPath);
|
|
||||||
}
|
|
||||||
}, [visible, selectedPath]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
Promise.all([
|
|
||||||
window.electron.getDefaultDownloadsPath(),
|
|
||||||
window.electron.getUserPreferences(),
|
|
||||||
]).then(([path, userPreferences]) => {
|
|
||||||
setSelectedPath(userPreferences?.downloadsPath || path);
|
|
||||||
|
|
||||||
if (userPreferences?.realDebridApiToken) {
|
|
||||||
setSelectedDownloader(Downloader.RealDebrid);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getDiskFreeSpace = (path: string) => {
|
|
||||||
window.electron.getDiskFreeSpace(path).then((result) => {
|
|
||||||
setDiskFreeSpace(result);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChooseDownloadsPath = async () => {
|
|
||||||
const { filePaths } = await window.electron.showOpenDialog({
|
|
||||||
defaultPath: selectedPath,
|
|
||||||
properties: ["openDirectory"],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (filePaths && filePaths.length > 0) {
|
|
||||||
const path = filePaths[0];
|
|
||||||
setSelectedPath(path);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleStartClick = () => {
|
|
||||||
if (repack) {
|
|
||||||
setDownloadStarting(true);
|
|
||||||
|
|
||||||
startDownload(repack, selectedDownloader, selectedPath).finally(() => {
|
|
||||||
setDownloadStarting(false);
|
|
||||||
onClose();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
visible={visible}
|
|
||||||
title="Download options"
|
|
||||||
description={t("space_left_on_disk", {
|
|
||||||
space: formatBytes(diskFreeSpace?.free ?? 0),
|
|
||||||
})}
|
|
||||||
onClose={onClose}
|
|
||||||
>
|
|
||||||
<div className={styles.container}>
|
|
||||||
<div>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
marginBottom: `${SPACING_UNIT}px`,
|
|
||||||
display: "block",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Method
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div className={styles.downloaders}>
|
|
||||||
{downloaders.map((downloader) => (
|
|
||||||
<Button
|
|
||||||
key={downloader}
|
|
||||||
className={styles.downloaderOption}
|
|
||||||
theme={
|
|
||||||
selectedDownloader === downloader ? "primary" : "outline"
|
|
||||||
}
|
|
||||||
onClick={() => setSelectedDownloader(downloader)}
|
|
||||||
>
|
|
||||||
{selectedDownloader === downloader && (
|
|
||||||
<CheckCircleFillIcon className={styles.downloaderIcon} />
|
|
||||||
)}
|
|
||||||
{DOWNLOADER_NAME[downloader]}
|
|
||||||
</Button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div className={styles.downloadsPathField}>
|
|
||||||
<TextField value={selectedPath} readOnly disabled label="Path" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style={{ alignSelf: "flex-end" }}
|
|
||||||
theme="outline"
|
|
||||||
onClick={handleChooseDownloadsPath}
|
|
||||||
disabled={downloadStarting}
|
|
||||||
>
|
|
||||||
{t("change")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className={styles.hintText}>
|
|
||||||
<Trans i18nKey="select_folder_hint" ns="game_details">
|
|
||||||
<Link to="/settings" />
|
|
||||||
</Trans>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button onClick={handleStartClick} disabled={downloadStarting}>
|
|
||||||
<DownloadIcon />
|
|
||||||
{t("download_now")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -4,16 +4,19 @@ import { useTranslation } from "react-i18next";
|
||||||
import type { UserPreferences } from "@types";
|
import type { UserPreferences } from "@types";
|
||||||
|
|
||||||
import { CheckboxField } from "@renderer/components";
|
import { CheckboxField } from "@renderer/components";
|
||||||
|
import { useAppSelector } from "@renderer/hooks";
|
||||||
|
|
||||||
export interface SettingsBehaviorProps {
|
export interface SettingsBehaviorProps {
|
||||||
userPreferences: UserPreferences | null;
|
|
||||||
updateUserPreferences: (values: Partial<UserPreferences>) => void;
|
updateUserPreferences: (values: Partial<UserPreferences>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SettingsBehavior({
|
export function SettingsBehavior({
|
||||||
updateUserPreferences,
|
updateUserPreferences,
|
||||||
userPreferences,
|
|
||||||
}: SettingsBehaviorProps) {
|
}: SettingsBehaviorProps) {
|
||||||
|
const userPreferences = useAppSelector(
|
||||||
|
(state) => state.userPreferences.value
|
||||||
|
);
|
||||||
|
|
||||||
const [form, setForm] = useState({
|
const [form, setForm] = useState({
|
||||||
preferQuitInsteadOfHiding: false,
|
preferQuitInsteadOfHiding: false,
|
||||||
runAtStartup: false,
|
runAtStartup: false,
|
||||||
|
|
|
@ -5,16 +5,19 @@ import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import * as styles from "./settings-general.css";
|
import * as styles from "./settings-general.css";
|
||||||
import type { UserPreferences } from "@types";
|
import type { UserPreferences } from "@types";
|
||||||
|
import { useAppSelector } from "@renderer/hooks";
|
||||||
|
|
||||||
export interface SettingsGeneralProps {
|
export interface SettingsGeneralProps {
|
||||||
userPreferences: UserPreferences | null;
|
|
||||||
updateUserPreferences: (values: Partial<UserPreferences>) => void;
|
updateUserPreferences: (values: Partial<UserPreferences>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SettingsGeneral({
|
export function SettingsGeneral({
|
||||||
userPreferences,
|
|
||||||
updateUserPreferences,
|
updateUserPreferences,
|
||||||
}: SettingsGeneralProps) {
|
}: SettingsGeneralProps) {
|
||||||
|
const userPreferences = useAppSelector(
|
||||||
|
(state) => state.userPreferences.value
|
||||||
|
);
|
||||||
|
|
||||||
const [form, setForm] = useState({
|
const [form, setForm] = useState({
|
||||||
downloadsPath: "",
|
downloadsPath: "",
|
||||||
downloadNotificationsEnabled: false,
|
downloadNotificationsEnabled: false,
|
||||||
|
|
|
@ -5,19 +5,21 @@ import { Button, CheckboxField, Link, TextField } from "@renderer/components";
|
||||||
import * as styles from "./settings-real-debrid.css";
|
import * as styles from "./settings-real-debrid.css";
|
||||||
import type { UserPreferences } from "@types";
|
import type { UserPreferences } from "@types";
|
||||||
import { SPACING_UNIT } from "@renderer/theme.css";
|
import { SPACING_UNIT } from "@renderer/theme.css";
|
||||||
import { useToast } from "@renderer/hooks";
|
import { useAppSelector, useToast } from "@renderer/hooks";
|
||||||
|
|
||||||
const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken";
|
const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken";
|
||||||
|
|
||||||
export interface SettingsRealDebridProps {
|
export interface SettingsRealDebridProps {
|
||||||
userPreferences: UserPreferences | null;
|
|
||||||
updateUserPreferences: (values: Partial<UserPreferences>) => void;
|
updateUserPreferences: (values: Partial<UserPreferences>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SettingsRealDebrid({
|
export function SettingsRealDebrid({
|
||||||
userPreferences,
|
|
||||||
updateUserPreferences,
|
updateUserPreferences,
|
||||||
}: SettingsRealDebridProps) {
|
}: SettingsRealDebridProps) {
|
||||||
|
const userPreferences = useAppSelector(
|
||||||
|
(state) => state.userPreferences.value
|
||||||
|
);
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [form, setForm] = useState({
|
const [form, setForm] = useState({
|
||||||
useRealDebrid: false,
|
useRealDebrid: false,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button } from "@renderer/components";
|
import { Button } from "@renderer/components";
|
||||||
|
|
||||||
import * as styles from "./settings.css";
|
import * as styles from "./settings.css";
|
||||||
|
@ -7,56 +7,42 @@ import { UserPreferences } from "@types";
|
||||||
import { SettingsRealDebrid } from "./settings-real-debrid";
|
import { SettingsRealDebrid } from "./settings-real-debrid";
|
||||||
import { SettingsGeneral } from "./settings-general";
|
import { SettingsGeneral } from "./settings-general";
|
||||||
import { SettingsBehavior } from "./settings-behavior";
|
import { SettingsBehavior } from "./settings-behavior";
|
||||||
|
import { useAppDispatch } from "@renderer/hooks";
|
||||||
|
import { setUserPreferences } from "@renderer/features";
|
||||||
|
|
||||||
export function Settings() {
|
export function Settings() {
|
||||||
const [userPreferences, setUserPreferences] =
|
|
||||||
useState<UserPreferences | null>(null);
|
|
||||||
|
|
||||||
const { t } = useTranslation("settings");
|
const { t } = useTranslation("settings");
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const categories = [t("general"), t("behavior"), "Real-Debrid"];
|
const categories = [t("general"), t("behavior"), "Real-Debrid"];
|
||||||
|
|
||||||
const [currentCategoryIndex, setCurrentCategoryIndex] = useState(0);
|
const [currentCategoryIndex, setCurrentCategoryIndex] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.electron.getUserPreferences().then((userPreferences) => {
|
|
||||||
setUserPreferences(userPreferences);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleUpdateUserPreferences = async (
|
const handleUpdateUserPreferences = async (
|
||||||
values: Partial<UserPreferences>
|
values: Partial<UserPreferences>
|
||||||
) => {
|
) => {
|
||||||
await window.electron.updateUserPreferences(values);
|
await window.electron.updateUserPreferences(values);
|
||||||
window.electron.getUserPreferences().then((userPreferences) => {
|
window.electron.getUserPreferences().then((userPreferences) => {
|
||||||
setUserPreferences(userPreferences);
|
dispatch(setUserPreferences(userPreferences));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderCategory = () => {
|
const renderCategory = () => {
|
||||||
if (currentCategoryIndex === 0) {
|
if (currentCategoryIndex === 0) {
|
||||||
return (
|
return (
|
||||||
<SettingsGeneral
|
<SettingsGeneral updateUserPreferences={handleUpdateUserPreferences} />
|
||||||
userPreferences={userPreferences}
|
|
||||||
updateUserPreferences={handleUpdateUserPreferences}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentCategoryIndex === 1) {
|
if (currentCategoryIndex === 1) {
|
||||||
return (
|
return (
|
||||||
<SettingsBehavior
|
<SettingsBehavior updateUserPreferences={handleUpdateUserPreferences} />
|
||||||
userPreferences={userPreferences}
|
|
||||||
updateUserPreferences={handleUpdateUserPreferences}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsRealDebrid
|
<SettingsRealDebrid updateUserPreferences={handleUpdateUserPreferences} />
|
||||||
userPreferences={userPreferences}
|
|
||||||
updateUserPreferences={handleUpdateUserPreferences}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue